diff --git a/src/blockchain/token.js b/src/blockchain/token.js index 90a575f..2b12336 100644 --- a/src/blockchain/token.js +++ b/src/blockchain/token.js @@ -26,6 +26,7 @@ export async function getToken(chainId, addr) { chainId = s.chainId } let found = metadataMap[chainId][addr] + // console.log('token from metadataMap', found) if (found) return found if (!(addr in s.tokens)) @@ -37,6 +38,7 @@ export async function getToken(chainId, addr) { const _inFlightLookups = {} export async function addExtraToken(chainId, addr) { + // console.log('addExtraToken', chainId, addr) if (addr===undefined) { // todo remove check console.warn('addExtraToken(addr) is deprecated. use addExtraToken(chainId,addr)') addr = chainId @@ -51,7 +53,7 @@ export async function addExtraToken(chainId, addr) { const prom = new Promise((resolve) => { const s = useStore() console.log('querying token', addr) - socket.emit('lookupToken', chainId, addr, (info) => { + socket.emit('lookupToken', chainId, addr, async (info) => { console.log('server token info', addr, info) if (info !== null) { s.addToken(chainId, info) @@ -63,7 +65,7 @@ export async function addExtraToken(chainId, addr) { resolve(null) } else { - const token = newContract(addr, 'IERC20Metadata', provider) + const token = await newContract(addr, 'IERC20Metadata', provider) Promise.all( [token.name(), token.symbol(), token.decimals()] ).then((name,symbol,decimals)=>{ info = { a: addr, diff --git a/src/blockchain/wallet.js b/src/blockchain/wallet.js index 18ae466..c6f7dbf 100644 --- a/src/blockchain/wallet.js +++ b/src/blockchain/wallet.js @@ -303,7 +303,7 @@ export async function pendOrder(order, fee=null) { id: uuid(), chainId: s.chainId, placementTime: Date.now()/1000, - fee: fee, + fee: fee, // dexorder place and gas fee total vault: s.vaults.length ? s.vaults[0] : null, state: PendingOrderState.Submitted, order diff --git a/src/charts/chart.js b/src/charts/chart.js index 5eb7485..2f6bd33 100644 --- a/src/charts/chart.js +++ b/src/charts/chart.js @@ -1,6 +1,6 @@ import {useChartOrderStore} from "@/orderbuild.js"; import {invokeCallbacks, prototype} from "@/common.js"; -import {DataFeed, initFeeDropdown, lookupSymbol} from "@/charts/datafeed.js"; +import {DataFeed, feelessTickerKey, getAllSymbols, lookupSymbol} from "@/charts/datafeed.js"; import {intervalToSeconds, SingletonCoroutine} from "@/misc.js"; import {useStore} from "@/store/store.js"; import {tvCustomThemes} from "../../theme.js"; @@ -22,11 +22,11 @@ export function removeSymbolChangedCallback(cb) { symbolChangedCbs = symbolChangedCbs.filter((i)=>i!==cb) } -function changeSymbol(symbol) { - console.error('change symbol', symbol) +function symbolChanged(symbol) { if (symbol===null) co.selectedSymbol = null else { + updateFeeDropdown() const info = lookupSymbol(symbol.ticker) symbolChangedCbs.forEach((cb) => cb(info)) co.selectedSymbol = info @@ -44,6 +44,16 @@ export async function setSymbol(symbol) { } +export async function setSymbolTicker(ticker) { + const found = getAllSymbols()[ticker] + if (!found) { + console.error('No symbol for ticker', ticker) + return + } + await setSymbol(found) +} + + function changeInterval(interval, _timeframe) { co.intervalSecs = intervalToSeconds(interval) DataFeed.intervalChanged(co.intervalSecs) @@ -70,6 +80,50 @@ const subscribeEvents = [ */ +let feeDropdown = null + +export function initFeeDropdown(w) { + widget = w + widget.createDropdown( + { + title: 'Fees', + tooltip: 'Choose Fee Tier', + items: [/*{title: 'Automatic Fee Selection', onSelect: () => {log('autofees')}}*/], + icon: ``, + } + ).then(dropdown => { + feeDropdown = dropdown; + updateFeeDropdown() + }) +} + +export function updateFeeDropdown() { + if (feeDropdown === null) return + const symbolItem = useChartOrderStore().selectedSymbol + let items + if (symbolItem === null) + items = [{title: '0.00%'}] + else { + const feeGroup = symbolItem.feeGroup + items = feeGroup.map((p) => { + const [_addr, fee] = p + return { + title: (fee / 10000).toFixed(2) + '%', + onSelect: fee === symbolItem.fee ? ()=>{} : () => selectPool(fee), + } + }) + } + feeDropdown.applyOptions({items}) +} + +function selectPool(fee) { + const co = useChartOrderStore(); + const s = co.selectedSymbol; + const ticker = feelessTickerKey(s.ticker) + '|' + fee + if (ticker !== s.ticker) + setSymbolTicker(ticker).catch((e)=>console.error('Could not change TV symbol to', ticker)) +} + export function initWidget(el) { widget = window.tvWidget = new TradingView.widget({ library_path: "/charting_library/", @@ -107,7 +161,7 @@ function initChart() { const themeName = useStore().theme; widget.changeTheme(themeName).catch((e)=>console.warn(`Could not change theme to ${themeName}`, e)) chart.crossHairMoved().subscribe(null, (point)=>setTimeout(()=>handleCrosshairMovement(point),0) ) - chart.onSymbolChanged().subscribe(null, changeSymbol) + chart.onSymbolChanged().subscribe(null, symbolChanged) chart.onIntervalChanged().subscribe(null, changeInterval) chart.onDataLoaded().subscribe(null, dataLoaded) const tzapi = chart.getTimezoneApi(); @@ -118,10 +172,10 @@ function initChart() { const symbolExt = chart.symbolExt(); // console.log('symbolExt', symbolExt); if(symbolExt) { - changeSymbol(symbolExt) + symbolChanged(symbolExt) } else { - changeSymbol(null) + symbolChanged(null) } changeInterval(widget.symbolInterval().interval) co.chartReady = true diff --git a/src/charts/datafeed.js b/src/charts/datafeed.js index 44daf69..677d773 100644 --- a/src/charts/datafeed.js +++ b/src/charts/datafeed.js @@ -32,7 +32,6 @@ const quoteSymbols = [ 'WETH', ] -let feeDropdown = null let widget = null // Python code to generate VARIABLE_TICK_SIZE: @@ -42,46 +41,6 @@ let widget = null // ' '.join(f'{Decimal(10)**(k-D):f} {Decimal(10)**k:f}' for k in range(-K,K+1))+f' {Decimal(10)**(K+1-D)}' const VARIABLE_TICK_SIZE = '0.00000000000000001 0.000000000001 0.0000000000000001 0.00000000001 0.000000000000001 0.0000000001 0.00000000000001 0.000000001 0.0000000000001 0.00000001 0.000000000001 0.0000001 0.00000000001 0.000001 0.0000000001 0.00001 0.000000001 0.0001 0.00000001 0.001 0.0000001 0.01 0.000001 0.1 0.00001 1 0.0001 10 0.001 100 0.01 1000 0.1 10000 1 100000 10 1000000 100 10000000 1000 100000000 10000 1000000000 100000 10000000000 1000000 100000000000 10000000 1000000000000 100000000' -export function initFeeDropdown(w) { - widget = w - widget.createDropdown( - { - title: 'Fees', - tooltip: 'Choose Fee Tier', - items: [/*{title: 'Automatic Fee Selection', onSelect: () => {log('autofees')}}*/], - icon: ``, - } - ).then(dropdown => {feeDropdown = dropdown; updateFeeDropdown()}) -} - - -function updateFeeDropdown() { - if (feeDropdown===null) return - const symbolItem = useChartOrderStore().selectedSymbol - let items - if (symbolItem===null) - items = [{title: '0.00%'}] - else { - items = symbolItem.pools.map((p)=> { - return { - title: (p[1]/10000).toFixed(2)+'%', - onSelect: ()=>selectPool(p), - } - }) - } - feeDropdown.applyOptions({items}) -} - - -function selectPool(p) { - const co = useChartOrderStore(); - if ( co.selectedPool === null || co.selectedPool[0] !== p[0] || co.selectedPool[1] !== p[1]) { - co.selectedPool = p - } -} - - -const lastBarsCache = new Map(); // DatafeedConfiguration implementation const configurationData = { @@ -110,8 +69,8 @@ const configurationData = { }; -const tokenMap = {} -const poolMap = {} +const tokenMap = {} // todo needs chainId +const poolMap = {} // todo needs chainId let _symbols = null // keyed by the concatenated hex addrs of the token pair e.g. '0xf3Ed85D882b5d9A67fC10dBf8f9AA991212983aA' + '0x6cdC5106DC100115E6C310539Fe44a61b3EEa6C4' const indexer = new FlexSearch.Document({ @@ -121,53 +80,90 @@ const indexer = new FlexSearch.Document({ }) const indexes = {} -const symbolsSeen = {} // keyed by (base,quote) so we only list one pool per pair even if there are many fee tiers +const feeGroups = {} // keyed by ticker without the final fee field. values are a list of pools [[addr,fee],...] -// todo add chainIds to all these keys -function addSymbol(p, base, quote, inverted) { +// The symbol key is the chain and base/quote: basically a "pair." It is only used by dexorder. The TradingView +// symbol is keyed by the `ticker` which is defined as 'chain_id|pool_addr' for absolute uniqueness. + +export function tickerForOrder(chainId, order) { + const [exchange, fee] = order.route + return tickerKey(chainId, exchange, order.tokenIn, order.tokenOut, fee, true) +} + +export function tickerKey(chainId, exchange, tokenAddrA, tokenAddrB, fee, chooseInversion=false ) { + if (chooseInversion && invertedDefault(tokenAddrA, tokenAddrB) ) + [tokenAddrA, tokenAddrB] = [tokenAddrB, tokenAddrA] + // NOTE: the ticker key specifies a base and quote ordering, so there are two tickers per pool + return `${chainId}|${exchange}|${tokenAddrA}|${tokenAddrB}|${fee}` +} + +export function feelessTickerKey(ticker) { + return ticker.split('|').slice(0, -1).join('|'); +} + +function addSymbol(chainId, p, base, quote, inverted) { const symbol = base.s + quote.s const exchange = ['UNIv2', 'UNIv3'][p.e] const full_name = exchange + ':' + symbol // + '%' + formatFee(fee) - const key = `${base.a}/${quote.a}` - log('addSymbol', p, base, quote, inverted, key) - if (symbolsSeen[key]) { - // add this pool's address to the existing symbol as an additional fee tier - const symbolInfo = _symbols[key]; - symbolInfo.pools.push([p.a, p.f]) - symbolInfo.pools.sort((a,b)=>a[1]-b[1]) - log('integrated symbol', symbolInfo) - indexes[key].as.push(p.a) - return - } - symbolsSeen[key] = true - const longExchange = ['Uniswap v2', 'Uniswap v3',][p.e] - const description = `${base.n} / ${quote.n}` + const ticker = tickerKey(chainId, p.e, base.a, quote.a, p.f) + // add the search index only if this is the natural, noninverted base/quote pair + log('addSymbol', p, base, quote, inverted, ticker) + const description = `${base.n} / ${quote.n} ${(p.f/10000).toFixed(2)}%` const type = 'swap' - const pools = [[p.a, p.f]] const decimals = inverted ? -p.d : p.d - _symbols[key] = { - ticker: key, full_name, symbol, description, - exchange, type, inverted, base, quote, pools, decimals, x:p.x + const symbolInfo = { + key: ticker, ticker, + chainId, address: p.a, exchangeId: p.e, + full_name, symbol, description, exchange, type, inverted, base, quote, decimals, x:p.x, fee:p.f, + }; + _symbols[ticker] = symbolInfo + const feelessKey = feelessTickerKey(ticker) + if (feelessKey in feeGroups) { + feeGroups[feelessKey].push([symbolInfo.address, symbolInfo.fee]) + feeGroups[feelessKey].sort((a,b)=>a[1]-b[1]) } - if (defaultSymbol===null) - defaultSymbol = _symbols[key] - log('new symbol', key, _symbols[key]) - indexes[key] = { - // key - id: key, + else + feeGroups[feelessKey] = [[symbolInfo.address, symbolInfo.fee]] + symbolInfo.feeGroup = feeGroups[feelessKey] + if (defaultSymbol===null && !invertedDefault(symbolInfo.base.a, symbolInfo.quote.a)) + defaultSymbol = _symbols[ticker] + log('new symbol', ticker, _symbols[ticker]) +} - // addresses - as: [p.a], // multiple pool addrs for each fee tier - b: p.b, - q: p.q, - // symbols - fn: full_name, - bs: base.s, - qs: quote.s, - e: exchange + ' ' + longExchange, - d: description, +function buildSymbolIndex() { + for (const symbol of Object.values(_symbols)) { + if (invertedDefault(symbol.base.a, symbol.quote.a)) + continue // don't search "upside down" pairs + const feelessKey = feelessTickerKey(symbol.ticker) + const feeGroup = feeGroups[feelessKey] + const [_addr, medianFee] = feeGroup[Math.floor((feeGroup.length-1)/2)] + if (symbol.fee !== medianFee) + continue // show the pool with the median fee by default + const ticker = symbol.ticker + const longExchange = ['Uniswap v2', 'Uniswap v3',][symbol.exchangeId] + if (ticker in indexes) { + indexes[ticker].as.push(symbol.address) // add the pool address index + } else { + indexes[ticker] = { + // key + id: ticker, + + // addresses + a: symbol.address, + b: symbol.base.a, + q: symbol.quote.a, + + // symbols + fn: symbol.full_name, + bs: symbol.base.s, + qs: symbol.quote.s, + e: symbol.exchange + ' ' + longExchange, + d: symbol.description, + } + } } + } @@ -178,7 +174,29 @@ function addSymbol(p, base, quote, inverted) { // return str // } -function getAllSymbols() { +function invertedDefault(tokenAddrA, tokenAddrB) { + // lower priority is more important (earlier in the list) + const a = tokenMap[tokenAddrA]; + const b = tokenMap[tokenAddrB]; + if (!a) { + log(`No token ${tokenAddrA} found`) + return + } + if (!b) { + log(`No token ${tokenAddrB} found`) + return + } + let basePriority = quoteSymbols.indexOf(a.s) + let quotePriority = quoteSymbols.indexOf(b.s) + if (basePriority === -1) + basePriority = Number.MAX_SAFE_INTEGER + if (quotePriority === -1) + quotePriority = Number.MAX_SAFE_INTEGER + return basePriority < quotePriority +} + + +export function getAllSymbols() { if (_symbols===null) { const chainId = useStore().chainId; const md = metadata[chainId] @@ -201,19 +219,10 @@ function getAllSymbols() { log(`No token ${p.q} found`) return } - // todo check quotes symbol list for inversion hint - let basePriority = quoteSymbols.indexOf(base.s) - let quotePriority = quoteSymbols.indexOf(quote.s) - if (basePriority === -1) - basePriority = Number.MAX_SAFE_INTEGER - if (quotePriority === -1) - quotePriority = Number.MAX_SAFE_INTEGER - const showInverted = basePriority < quotePriority - if (showInverted) - addSymbol(p, quote, base, true); - else - addSymbol(p, base, quote, false); + addSymbol(chainId, p, quote, base, true); + addSymbol(chainId, p, base, quote, false); }) + buildSymbolIndex() log('indexes', indexes) Object.values(indexes).forEach(indexer.add.bind(indexer)) } @@ -221,18 +230,24 @@ function getAllSymbols() { return _symbols } -export function lookupSymbol(key) { // lookup by ticker which is "0xbaseAddress/0xquoteAddress" +function invertTicker(ticker) { + const [chainId, exchange, base, quote, fee] = ticker.split('|') + return tickerKey(chainId, exchange, quote, base, fee) +} + +export function lookupSymbol(ticker) { // lookup by ticker which is "0xbaseAddress/0xquoteAddress" + // todo tim lookup default base/quote pool const symbols = getAllSymbols(); - if (!(key in symbols)) { + if (!(ticker in symbols)) { // check the inverted symbol - const [base,quote] = key.split('/') - key = quote+'/'+base - if (!(key in symbols)) { - console.error('no symbol found for key', key, symbols) + const orig = ticker + ticker = invertTicker(ticker); + if (!(ticker in symbols)) { + console.error('no symbol found for ticker', orig, symbols) return null } } - return symbols[key] + return symbols[ticker] } function checkBar(bar, msg) { @@ -322,9 +337,13 @@ export const DataFeed = { const found = indexer.search(userInput, 10) log('found', found) const result = [] + const seen = {} for (const f of found) - for (const key of f.result) - result.push(_symbols[key]) + for (const ticker of f.result) + if (!(ticker in seen)) { + result.push(_symbols[ticker]) + seen[ticker] = true + } onResultReadyCallback(result); }, @@ -348,7 +367,6 @@ export const DataFeed = { log('[resolveSymbol]: Method call', symbolName); const symbols = getAllSymbols(); const symbolItem = symbolName === 'default' ? defaultSymbol : symbols[symbolName] - log('symbol resolved?', symbolItem) if (!symbolItem) { log('[resolveSymbol]: Cannot resolve symbol', symbolName); onResolveErrorCallback('cannot resolve symbol'); @@ -356,19 +374,19 @@ export const DataFeed = { } const co = useChartOrderStore(); co.selectedSymbol = symbolItem - const pool = symbolItem.pools[0]; // choose the first-listed pool. server will adjust metadata accordingly. + const feelessKey = feelessTickerKey(symbolItem.ticker) + const symbolsByFee = feeGroups[feelessKey] + symbolsByFee.sort((a,b)=>a.fee-b.fee) + const pool = symbolsByFee[Math.floor((symbolsByFee.length - 1)/2)] // median rounded down // noinspection JSValidateTypes - co.selectedPool = pool - updateFeeDropdown() - const priceScale = '100' + co.selectedPool = pool // todo remove // LibrarySymbolInfo // https://www.tradingview.com/charting-library-docs/latest/api/interfaces/Charting_Library.LibrarySymbolInfo const symbolInfo = { ticker: symbolItem.ticker, name: symbolItem.symbol, pro_name: symbolItem.full_name, - // we add the fee to the description after the specific pool has been resolved - description: symbolItem.description + ` ${(pool[1] / 10000).toFixed(2)}%`, + description: symbolItem.description, type: symbolItem.type, session: '24x7', timezone: 'Etc/UTC', @@ -392,9 +410,9 @@ export const DataFeed = { log('[getBars]: Method call', symbolInfo, resolution, from, to); try { // todo need to consider the selected fee tier - const [poolAddr, poolFee] = useChartOrderStore().selectedPool; await getAllSymbols() const symbol = lookupSymbol(symbolInfo.ticker); + const poolAddr = symbol.address const res = convertTvResolution(resolution) const key = `${useStore().chainId}|${poolAddr}|${res.name}` let bars = await loadOHLC(symbol, poolAddr, from, to, resolution); // This is the one that does all the work @@ -436,30 +454,12 @@ export const DataFeed = { } log('[subscribeBars]', symbolInfo, resolution, subscriberUID); - const symbol = getAllSymbols()[symbolInfo.full_name] - const co = useChartOrderStore() - - - // todo redo symbolInfo: the full_name passed to TV should just be the pool addr. when searching a symbol, go ahead and select the first/most liquid automatically - let poolAddr = null - const selectedFee = co.selectedPool[1]; - for (const [addr, fee] of symbol.pools) { - if (fee === selectedFee) { - poolAddr = addr - break - } - } - if (poolAddr===null) { - console.error(`Could not find pool for fee ${selectedFee}: ${symbol.pools}`) - return - } - - + const symbol = getAllSymbols()[symbolInfo.ticker] + const poolAddr = symbol.address const chainId = useStore().chainId; - const res = convertTvResolution(resolution) const period = res.name - console.log('subscription symbol', symbol, getAllSymbols()) + log('subscription symbol', symbol, chainId, poolAddr, res) const sub = new RealtimeSubscription(chainId, poolAddr, res, symbol, subscriberUID, onRealtimeCallback, onResetCacheNeededCallback) log('sub', sub.key) subOHLC(chainId, poolAddr, period) diff --git a/src/charts/ohlc.js b/src/charts/ohlc.js index 01311b7..8b91a6f 100644 --- a/src/charts/ohlc.js +++ b/src/charts/ohlc.js @@ -116,7 +116,7 @@ export async function loadOHLC (symbol, contract, from, to, tvRes) { let chainId let bars = []; let inverted = false; - let baseURL + let baseUrl let latest = null // latest time, price function fill(end, period) { @@ -131,41 +131,41 @@ export async function loadOHLC (symbol, contract, from, to, tvRes) { } if (symbol.x?.data) { - baseURL = symbol.x.data.uri + baseUrl = symbol.x.data.uri contract = symbol.x.data.symbol chainId = symbol.x.data.chain inverted ^= symbol.x.data.inverted } else { - baseURL = `/ohlc/` + baseUrl = `/ohlc/` chainId = useStore().chainId } - baseURL += `${chainId}/${contract}/` + baseUrl += `${chainId}/${contract}/` const res = tvResMap[tvRes] const fetches = [] let start = from - if (!(baseURL in seriesStarts)) { + if (!(baseUrl in seriesStarts)) { try { - const response = await getUrl(baseURL+'quote.csv') + // console.log('getting quote', baseUrl+'quote.csv') + const response = await getUrl(baseUrl+'quote.csv') if (response.length) { - seriesStarts[baseURL] = parseInt(response.split(',')[0]) - // console.log(`Series ${baseURL} starts at ${new Date(start*1000)}`) + seriesStarts[baseUrl] = parseInt(response.split(',')[0]) + // console.log(`Series ${baseUrl} starts at ${new Date(start*1000)}`) } else { - console.error(`Bad response while fetching ${baseURL+'quote.csv'}`) + console.error(`Bad response while fetching ${baseUrl+'quote.csv'}`) } } catch (e) { console.error(e) } } - if (baseURL in seriesStarts) - start = Math.max(start, seriesStarts[baseURL]) + if (baseUrl in seriesStarts) + start = Math.max(start, seriesStarts[baseUrl]) for(let now = start; now < to; now = res.nextStart(now)) { - const url = baseURL + res.filename(contract, now); - // console.log('fetching', url) + const url = baseUrl + res.filename(contract, now); const prom = getUrl(url) fetches.push(prom); } diff --git a/src/common.js b/src/common.js index 0978d3d..b5c0205 100644 --- a/src/common.js +++ b/src/common.js @@ -124,7 +124,7 @@ export class AbiURLCache extends AsyncURLCache { const files = { // If a contract is in a file different than its name, put the exception here // 'IVaultImpl' : 'IVault', // for example - 'IERC20Metadata' : 'interfaces/IERC20Metadata', + // 'IERC20Metadata' : 'interfaces/IERC20Metadata', } export function abiPath(name) { diff --git a/src/components/Order.vue b/src/components/Order.vue index 52c86d9..8ac7654 100644 --- a/src/components/Order.vue +++ b/src/components/Order.vue @@ -30,9 +30,11 @@ import {pendOrder} from "@/blockchain/wallet.js"; import router from "@/router/index.js"; import PairChoice from "@/components/PairChoice.vue"; import NeedsSigner from "@/components/NeedsSigner.vue"; +import {useChartOrderStore} from "@/orderbuild.js"; const s = useStore() const os = useOrderStore() +const co = useChartOrderStore() const props = defineProps(['title','subtitle','valid','tranches']) function placeOrder() { @@ -40,12 +42,12 @@ function placeOrder() { const tb = os.tokenB; const tokenIn = os.buy ? tb.address : ta.address const tokenOut = os.buy ? ta.address : tb.address - const route = os.route + const symbol = co.selectedSymbol const amt = FixedNumber.fromString(os.totalAmount.toString(), {decimals: os.amountToken.decimals}).value const ts = props.tranches() - const order = newOrder(tokenIn, tokenOut, route.exchange, route.fee, amt, os.amountIsInput, ts) // todo: minAmount, outputToOwner, conditionalOrder + console.log('new order', symbol) + const order = newOrder(tokenIn, tokenOut, symbol.e, symbol.fee, amt, os.amountIsInput, ts) // todo: minAmount, outputToOwner, conditionalOrder pendOrder(order) - route('Status') } diff --git a/src/components/Status.vue b/src/components/Status.vue index 52802b7..1a666d2 100644 --- a/src/components/Status.vue +++ b/src/components/Status.vue @@ -167,7 +167,7 @@ import {DISTANT_FUTURE, isOpen, OrderState} from "@/blockchain/orderlib.js"; import Pulse from "@/components/Pulse.vue"; import {OrderShapes} from "@/charts/ordershapes.js"; import {useChartOrderStore} from "@/orderbuild.js"; -import {lookupSymbol} from "@/charts/datafeed.js"; +import {lookupSymbol, tickerForOrder} from "@/charts/datafeed.js"; import {setSymbol} from "@/charts/chart.js"; import {uniswapV3AveragePrice} from "@/blockchain/uniswap.js"; @@ -187,17 +187,14 @@ watch(selected, async ()=>{ for (const order of orders.value) statusIndex[order.id] = order const selectedIndex = {} + const chainId = s.chainId for (const id of selected.value) { selectedIndex[id] = true const status = statusIndex[id]; if (!(id in orderShapes)) { - let base = status.order.tokenIn - let quote = status.order.tokenOut - if (base > quote) - [base, quote] = [quote, base] - const symbolKey = `${base}/${quote}` - const symbol = lookupSymbol(symbolKey) - if (co.selectedSymbol.ticker !== symbolKey) { + const ticker = tickerForOrder(chainId, status.order) + const symbol = lookupSymbol(ticker) + if (co.selectedSymbol.ticker !== ticker) { co.selectedSymbol = symbol await setSymbol(symbol) } diff --git a/src/components/TokenRow.vue b/src/components/TokenRow.vue index a475760..9f461a6 100644 --- a/src/components/TokenRow.vue +++ b/src/components/TokenRow.vue @@ -3,16 +3,16 @@ - {{ token.n || '' }} + {{ token?.n || '' }} {{ fixed }} - {{ token.s }} + {{ token?.s }} - + @@ -26,16 +26,17 @@ import {useStore} from "@/store/store"; import {getToken} from "@/blockchain/token.js"; import {FixedNumber} from "ethers"; -import {computed, ref} from "vue"; +import {computed} from "vue"; const s = useStore() const props = defineProps(['chainId', 'addr', 'amount', 'onWithdraw']) const token = await getToken(props.chainId, props.addr) -const fixed = computed(() => FixedNumber.fromValue(props.amount, token.d, { +console.log('TokenRow token is', token) +const fixed = computed(() => token ? FixedNumber.fromValue(props.amount, token.d, { width: 256, decimals: token.d -})) -const imageSrc = computed(() => token.image) +}) : null) +const imageSrc = computed(() => token ? token.image : null)