import {FixedNumber} from "ethers"; import {usePrefStore, useStore} from "@/store/store.js"; import {token} from "@/blockchain/token.js"; import Color from "color"; const QUOTE_SYMBOLS = [ // in order of preference // todo put this in metadata.json using addrs 'USDT', 'USDC', 'USDC.e', 'BTC', 'WBTC', 'ETH', 'WETH', 'ARB', ] export class SingletonCoroutine { constructor(f, delay = 10) { this.f = f this.delay = delay this.timeout = null this.args = null } pending() { return this.timeout !== null } invoke(/*arguments*/) { this.args = arguments // console.log('invoke', arguments) if (this.timeout === null) // noinspection JSCheckFunctionSignatures this.timeout = setTimeout(this.onTimeout, this.delay, this) } async onTimeout(self) { try { await self.f(...self.args) } catch (e) { if (self.retry) { console.log('retrying', this.f, 'due to error', e) self.timeout = null self.invoke(self.args) } else console.error(e) } finally { self.timeout = null } } } export const vAutoSelect = { beforeMount: (el) => { const input = el.querySelector('input') input.onfocus = () => setTimeout(() => input.select(), 0) } } export const uint32max = 4294967295n export const uint64max = 18446744073709551615n export function tokenNumber(token, balance) { return FixedNumber.fromValue(balance, token.decimals, {decimals: token.decimals, width: 256}) } export function tokenFloat(token, balance) { return tokenNumber(token, balance).toUnsafeFloat() } export function routeInverted(route) { const s = useStore() return route && (route.token0 === s.tokenA) === s.inverted } export function intervalString(seconds) { if (seconds < 1) return 'now' else if (seconds < 60) return `${seconds} seconds` else if (seconds < 3600) return `${(seconds / 60).toFixed(1)} minutes` else if (seconds < 86400) return `${(seconds / 3600).toFixed(1)} hours` else return `${(seconds / 86400).toFixed(1)} days` } const _dateFormat = new Intl.DateTimeFormat(undefined, {dateStyle: 'medium', timeStyle: 'short'}) export function dateString(seconds) { const date = new Date(seconds * 1000) return _dateFormat.format(date) } export function timestamp(date = null) { if (date === null) date = new Date() return Math.round(date.getTime() / 1000) } export function pairKey(chainId, tokenA, tokenB) { const token0 = tokenA.a < tokenB.a ? tokenA.a : tokenB.a const token1 = tokenA.a > tokenB.a ? tokenA.a : tokenB.a return [chainId, token0, token1]; } export function pairPriceAddr(chainId, baseTokenAddr, quoteTokenAddr, price) { const baseToken = token(chainId, baseTokenAddr) const quoteToken = token(chainId, quoteTokenAddr) if (!baseToken || !quoteToken) return null return pairPrice(chainId, baseToken, quoteToken, price) } export function flipInversionPreference(chainId, base, quote) { const k = pairKey(chainId, base, quote) const prefs = usePrefStore() prefs.inverted[k] = !prefs.inverted[k] } export function inversionPreference(chainId, base, quote) { // returns the user preference for whether to invert this pair or not const invertedKey = pairKey(chainId, base, quote); const prefs = usePrefStore() if (!(invertedKey in prefs.inverted)) { // todo prefer stablecoins as the quote asset let inverted = false; for (const q of QUOTE_SYMBOLS) { if (quote.s === q) break // definitely not inverted if (base.s === q) { inverted = true break // definitely inverted } } prefs.inverted[invertedKey] = inverted } return prefs.inverted[invertedKey] } export function pairPrice(chainId, baseToken, quoteToken, price) { // console.warn('pairPrice', chainId, baseToken, quoteToken, price, decimals) if (price === null || price === undefined) return null const decimals = quoteToken.d - baseToken.d if (decimals >= 0) price /= 10 ** decimals else price *= 10 ** -decimals if (inversionPreference(chainId, baseToken, quoteToken)) price = 1 / price return price } export const sleep = ms => new Promise(r => setTimeout(r, ms)) export function builderDefaults(props, emit, defaults) { let changed = false for (const k in defaults) if (props.builder[k] === undefined) { props.builder[k] = defaults[k] instanceof Function ? defaults[k]() : defaults[k] changed = true } if (changed) emit('update:builder', props.builder) } export function uuid() { return crypto.randomUUID(); } export function lightenColor(color, lightness = 85, alpha = null) { let c = new Color(color).hsl().lightness(lightness) if (alpha !== null) c = c.rgb().alpha(alpha) return c.string() } export function lightenColor2(color, alpha = null) { return lightenColor(color, 98, alpha) } const colorRanges = { buy: ['#00CC33', '#3300CC'], sell: ['#CC0033', '#CCCC33'], } export function lineColor(buy, index) { const range = buy ? colorRanges.buy : colorRanges.sell const a = new Color(range[0]).rgb() const b = new Color(range[1]).rgb() // const z2 = Math.PI * Math.PI / 6 - 1 // ζ(2) modulo 1 const z2 = 1 - 1/Math.E // ζ(2) modulo 1 const kk = index * z2; const k = kk - Math.trunc(kk) const mix = (x, y) => (1 - k) * x + k * y const c = new Color([mix(a.red(), b.red()), mix(a.green(), b.green()), mix(a.blue(), b.blue())]) // console.log('default color', c.string()) return c.string() }