symbol rework; fee % switching; symbol search improvements

This commit is contained in:
tim
2024-10-11 00:26:29 -04:00
parent 6d19adb130
commit 5b23864c2e
11 changed files with 233 additions and 178 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -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: `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28"><g fill="none" stroke="currentColor"><circle cx="10" cy="10" r="2.5"/><circle cx="18" cy="18" r="2.5"/><path stroke-linecap="square" d="M17.5 7.5l-7 13"/></g></svg>`,
}
).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

View File

@@ -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: `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28"><g fill="none" stroke="currentColor"><circle cx="10" cy="10" r="2.5"/><circle cx="18" cy="18" r="2.5"/><path stroke-linecap="square" d="M17.5 7.5l-7 13"/></g></svg>`,
}
).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,54 +80,91 @@ 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] = {
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])
}
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: key,
id: ticker,
// addresses
as: [p.a], // multiple pool addrs for each fee tier
b: p.b,
q: p.q,
a: symbol.address,
b: symbol.base.a,
q: symbol.quote.a,
// symbols
fn: full_name,
bs: base.s,
qs: quote.s,
e: exchange + ' ' + longExchange,
d: description,
fn: symbol.full_name,
bs: symbol.base.s,
qs: symbol.quote.s,
e: symbol.exchange + ' ' + longExchange,
d: symbol.description,
}
}
}
}
// function formatFee(fee) {
@@ -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)

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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')
}
</script>

View File

@@ -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)
}

View File

@@ -3,16 +3,16 @@
<td>
<v-avatar v-if="imageSrc" :image="imageSrc" size="x-small"/>
</td>
<td class="d-none d-sm-table-cell">{{ token.n || '' }}</td>
<td class="d-none d-sm-table-cell">{{ token?.n || '' }}</td>
<td class="text-right">{{ fixed }}</td>
<td class="text-left">{{ token.s }}</td>
<td class="text-left">{{ token?.s }}</td>
<!-- todo price and value columns -->
<td>
<v-menu>
<template v-slot:activator="{ props }">
<v-btn variant="plain" v-bind="props" icon="mdi-dots-vertical"/>
</template>
<v-list>
<v-list v-if="token">
<v-list-subheader :title="token.s"/>
<v-list-item title="Withdraw" key="withdraw" value="withdraw" prepend-icon="mdi-arrow-down-bold"
@click="()=>onWithdraw(token.a)"/>
@@ -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)
</script>
<style scoped lang="scss">

View File

@@ -60,6 +60,7 @@ import RowBar from "@/components/chart/RowBar.vue";
import ColorBand from "@/components/chart/ColorBand.vue";
import Color from "color";
import {Exchange, newOrder} from "@/blockchain/orderlib.js";
import {lookupSymbol} from "@/charts/datafeed.js";
const props = defineProps(['order'])
const s = useStore()
@@ -112,7 +113,6 @@ function buildOrder() {
// Tranche[] tranches;
// }
const symbol = co.selectedSymbol
const fee = co.selectedPool[1]
const amountDec = order.amountIsTokenA ? symbol.base.d : symbol.quote.d
const amount = BigInt(Math.trunc(order.amount * 10 ** amountDec))
const amountIsInput = !!(order.amountIsTokenA ^ order.buy)
@@ -125,7 +125,7 @@ function buildOrder() {
tranches = [...tranches, ...ts]
}
return newOrder(tokenIn.value.a, tokenOut.value.a, Exchange.UniswapV3, fee, amount, amountIsInput, tranches)
return newOrder(tokenIn.value.a, tokenOut.value.a, symbol.exchangeId, symbol.fee, amount, amountIsInput, tranches)
}

View File

@@ -49,15 +49,14 @@ export const useChartOrderStore = defineStore('chart_orders', () => {
const orders = ref([defaultOrder]) // order models in UI format
const selectedOrder = ref(null)
const selectedSymbol = ref(null)
const selectedPool = ref(null)
const intervalSecs = ref(0)
const baseToken = computed(()=>selectedSymbol.value === null ? null : selectedSymbol.value.base)
const quoteToken = computed(()=>selectedSymbol.value === null ? null : selectedSymbol.value.quote)
const price = computed(() => {
if (!selectedPool.value || !selectedSymbol.value)
if (!selectedSymbol.value)
return null
const s = useStore()
let result = s.poolPrices[[s.chainId, selectedPool.value[0]]]
let result = s.poolPrices[[s.chainId, selectedSymbol.address]]
if (selectedSymbol.value.inverted)
result = 1 / result
return result
@@ -94,7 +93,7 @@ export const useChartOrderStore = defineStore('chart_orders', () => {
}
return {
chartReady, selectedSymbol, selectedPool, intervalSecs, baseToken, quoteToken, price,
chartReady, selectedSymbol, intervalSecs, baseToken, quoteToken, price,
orders, drawing, newOrder, removeOrder, resetOrders, meanRange,
}
})