diff --git a/src/blockchain/route.js b/src/blockchain/route.js index a42a175..06e612d 100644 --- a/src/blockchain/route.js +++ b/src/blockchain/route.js @@ -1,9 +1,12 @@ import {Exchange} from "@/blockchain/orderlib.js"; +import {useOrderStore, useStore} from "@/store/store.js"; +import {queryHelperContract} from "@/blockchain/contract.js"; +import {SingletonCoroutine} from "@/misc.js"; export async function findRoute(helper, chainId, tokenA, tokenB) { - console.log('getting raw routes', helper, tokenA.address, tokenB.address) - const rawRoutes = await helper.getRoutes(tokenA.address, tokenB.address) + console.log('getting raw routes', helper, tokenA.a, tokenB.a) + const rawRoutes = await helper.getRoutes(tokenA.a, tokenB.a) // todo expose all available pools console.log('raw routes', rawRoutes) let result = null // we actually only find a single pool for now @@ -15,11 +18,41 @@ export async function findRoute(helper, chainId, tokenA, tokenB) { case 0: // UniswapV2 throw Error('Uniswap V2 not yet supported') case 1: // UniswapV3 - const [token0, token1] = tokenA.address < tokenB.address ? [tokenA, tokenB] : [tokenB, tokenA] - result = {chainId, exchange: Exchange.UniswapV3, pool, fee, token0, token1} + const inverted = tokenA.a < tokenB.a; + const [token0, token1] = inverted ? [tokenA, tokenB] : [tokenB, tokenA] + result = {chainId, exchange: Exchange.UniswapV3, pool, fee, token0, token1, inverted} break } } } return result ? [result] : [] } + +async function componentFindRoute() { + const s = useStore() + const os = useOrderStore() + const tokenA = os.tokenA + const tokenB = os.tokenB + os.routes = [] + if (!tokenA || !tokenB) + return + console.log('finding route', s.chainId.value, tokenA, tokenB) + os.routesPending = true + try { + console.log('getting query helper') + const helper = await queryHelperContract(s.helper, s.provider) + if (!helper) { + console.log('no helper') + } else { + const result = await findRoute(helper, s.chainId.value, tokenA, tokenB) + console.log('found route', result) + os.routes = result + } + } catch (e) { + console.log('ignoring routes exception', e) + } finally { + os.routesPending = false + } +} + +export const routeFinder = new SingletonCoroutine(componentFindRoute, 10) diff --git a/src/blockchain/wallet.js b/src/blockchain/wallet.js index 8f4672d..c4812d4 100644 --- a/src/blockchain/wallet.js +++ b/src/blockchain/wallet.js @@ -131,7 +131,10 @@ async function _discoverVaults(owner) { console.error(`bad vault version ${version}`) } catch (e) { - console.log(`no vault ${num} at ${addr}`, e) + if( e.value==='0x' && e.code==='BAD_DATA' ) + console.log(`no vault ${num} at ${addr}`) + else + console.error(`routeFinder failed`, e) } if( s.account == owner ) { // double-check the account since it could have changed during our await s.vaults = result diff --git a/src/charts/chart.js b/src/charts/chart.js index f09c128..6dd724b 100644 --- a/src/charts/chart.js +++ b/src/charts/chart.js @@ -1,10 +1,28 @@ import {useChartOrderStore} from "@/orderbuild.js"; import {invokeCallbacks, prototype} from "@/common.js"; -import datafeed from "@/charts/datafeed.js"; +import datafeed, {lookupSymbol} from "@/charts/datafeed.js"; export let widget = null export let chart = null export let crosshairPoint = null +let symbolChangedCbs = [] // callbacks for TV's chart.onSymbolChanged() +let lastSymbolChangedArgs = null + +export function addSymbolChangedCallback(cb) { + symbolChangedCbs.push(cb) +} + +export function removeSymbolChangedCallback(cb) { + symbolChangedCbs = symbolChangedCbs.filter((i)=>i!==cb) +} + +function changeSymbol(symbol) { + console.log('tv changeSymbol', symbol) + const info = lookupSymbol(symbol.full_name) + lastSymbolChangedArgs = info + symbolChangedCbs.forEach((cb)=>cb(info)) +} + const subscribeEvents = [ 'toggle_sidebar', 'indicators_dialog', 'toggle_header', 'edit_object_dialog', 'chart_load_requested', @@ -51,7 +69,10 @@ function initChart() { console.log('init chart') chart = widget.activeChart() chart.crossHairMoved().subscribe(null, (point)=>setTimeout(()=>handleCrosshairMovement(point),0) ) + chart.onSymbolChanged().subscribe(null, changeSymbol); + changeSymbol(chart.symbolExt()) useChartOrderStore().chartReady = true + console.log('chart ready') } diff --git a/src/charts/datafeed.js b/src/charts/datafeed.js index 33c0375..67ddf77 100644 --- a/src/charts/datafeed.js +++ b/src/charts/datafeed.js @@ -57,7 +57,7 @@ function addSymbol(p, base, quote, inverted) { const longExchange = ['Uniswap v2', 'Uniswap v3',][p.e] const description = `${base.n} / ${quote.n}` const type = 'swap' - _symbols[full_name] = {symbol, full_name, description, exchange, type, inverted,} + _symbols[full_name] = {symbol, full_name, description, exchange, type, inverted, base, quote} indexes[full_name] = { // key id: full_name, @@ -84,7 +84,6 @@ function addSymbol(p, base, quote, inverted) { // } async function getAllSymbols() { - if (_symbols===null) { _symbols = {} for (const t of metadata.t) @@ -112,6 +111,10 @@ async function getAllSymbols() { return _symbols } +export function lookupSymbol(fullName) { + return _symbols[fullName] +} + export default { onReady: (callback) => { console.log('[onReady]: Method call'); diff --git a/src/components/ChartPairSelector.vue b/src/components/ChartPairSelector.vue new file mode 100644 index 0000000..e61a43d --- /dev/null +++ b/src/components/ChartPairSelector.vue @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/src/components/NeedsChart.vue b/src/components/NeedsChart.vue new file mode 100644 index 0000000..3c53b14 --- /dev/null +++ b/src/components/NeedsChart.vue @@ -0,0 +1,11 @@ + + + + + diff --git a/src/components/PairChoice.vue b/src/components/PairChoice.vue index 2bef669..5c2efc7 100644 --- a/src/components/PairChoice.vue +++ b/src/components/PairChoice.vue @@ -33,7 +33,7 @@ import TokenChoice from "@/components/TokenChoice.vue" import {useOrderStore, useStore} from "@/store/store"; import RoutePrice from "@/components/RoutePrice.vue"; -import {findRoute} from "@/blockchain/route.js"; +import {findRoute, routeFinder} from "@/blockchain/route.js"; import {SingletonCoroutine, routeInverted} from "@/misc.js"; import {computed, ref} from "vue"; import {queryHelperContract} from "@/blockchain/contract.js"; @@ -46,7 +46,7 @@ const tokenA = computed({ return os.tokenA }, set(value) { - if( !os.tokenA || os.tokenA.address !== value.address ) { + if( !os.tokenA || os.tokenA.a !== value.a ) { os.tokenA = value routeFinder.invoke() } @@ -57,7 +57,7 @@ const tokenB = computed({ return os.tokenB }, set(value) { - if( !os.tokenB || os.tokenB.address !== value.address ) { + if( !os.tokenB || os.tokenB.a !== value.address ) { os.tokenB = value routeFinder.invoke() } @@ -75,36 +75,6 @@ const routes = computed({ } }) -async function componentFindRoute() { - const tokenA = os.tokenA - const tokenB = os.tokenB - os.routes = [] - if (!tokenA || !tokenB) - return - console.log('finding route', s.chainId.value, tokenA, tokenB) - os.routesPending = true - try { - console.log('getting query helper') - const helper = await queryHelperContract(s.helper, s.provider) - if (!helper) { - console.log('no helper') - } - else { - const result = await findRoute(helper, s.chainId.value, tokenA, tokenB) - console.log('found route', result) - os.routes = result - } - } - catch (e) { - console.log('ignoring routes exception', e) - } - finally { - os.routesPending = false - } -} - - -const routeFinder = new SingletonCoroutine(componentFindRoute,10) routeFinder.invoke() diff --git a/src/components/chart/ChartOrder.vue b/src/components/chart/ChartOrder.vue index 4541a07..6b939ec 100644 --- a/src/components/chart/ChartOrder.vue +++ b/src/components/chart/ChartOrder.vue @@ -14,8 +14,25 @@ import {useChartOrderStore} from "@/orderbuild.js"; import Toolbar from "@/components/chart/Toolbar.vue"; import BuilderFactory from "@/components/chart/BuilderFactory.vue"; +import {addSymbolChangedCallback, removeSymbolChangedCallback} from "@/charts/chart.js"; +import {onBeforeUnmount} from "vue"; +import {useOrderStore} from "@/store/store.js"; + +import {routeFinder} from "@/blockchain/route.js"; const co = useChartOrderStore() +const os = useOrderStore() + +function changeSymbol(symbol) { + console.log('changeSymbol', symbol) + os.tokenA = symbol.base + os.tokenB = symbol.quote + routeFinder.invoke() +} + + +const oldSymbolChanged = addSymbolChangedCallback(changeSymbol) +onBeforeUnmount(() => removeSymbolChangedCallback(changeSymbol) ) diff --git a/src/misc.js b/src/misc.js index d1304f2..cb1a53b 100644 --- a/src/misc.js +++ b/src/misc.js @@ -91,8 +91,8 @@ export function timestamp(date=null) { export function pairKey(tokenA, tokenB) { - const token0 = tokenA.address < tokenB.address ? tokenA.address : tokenB.address - const token1 = tokenA.address > tokenB.address ? tokenA.address : tokenB.address + const token0 = tokenA.a < tokenB.a ? tokenA.a : tokenB.a + const token1 = tokenA.a > tokenB.a ? tokenA.a : tokenB.a return [token0, token1]; } diff --git a/src/orderbuild.js b/src/orderbuild.js index c999e4e..c09bf40 100644 --- a/src/orderbuild.js +++ b/src/orderbuild.js @@ -93,7 +93,7 @@ export function applyLine(tranche, intercept, slope, isMinimum = null) { const os = useOrderStore() const route = os.route const inverted = routeInverted(route) - const scale = 10 ** (os.tokenA.decimals - os.tokenB.decimals) + const scale = 10 ** (os.tokenA.d - os.tokenB.d) let m = inverted ? -scale / slope : slope / scale let b = inverted ? scale / intercept : intercept / scale const cur = b + timestamp() * m diff --git a/src/routeFinder.js b/src/routeFinder.js new file mode 100644 index 0000000..e69de29 diff --git a/src/version.js b/src/version.js index ed94910..fd3b544 100644 --- a/src/version.js +++ b/src/version.js @@ -1,5 +1,17 @@ -const versionPromise = fetch('/version.json').then(async (response)=>await response.json()) -const metadataPromise = fetch('/metadata.json').then(async (response)=>await response.json()) +function _json(name) { + return async function(response) { + try { + return await response.json() + } + catch (e) { + console.error(`could not read ${name}`) + return null + } + } +} + +const versionPromise = fetch('/version.json').then(_json('version.json')) +const metadataPromise = fetch('/metadata.json').then(_json('metadata.json')) export const version = await versionPromise console.log('version', version)