symbol rework; fee % switching; symbol search improvements
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user