orderspec refactor for server and web

This commit is contained in:
Tim Olson
2023-12-07 18:37:11 -04:00
parent 545583586c
commit 83619ea248
12 changed files with 306 additions and 110 deletions

View File

@@ -1,5 +1,5 @@
export function applyFills( orderStatus, filled ) { export function applyFills( orderStatus, filled ) {
// console.log('apply fills', orderStatus, filled) console.log('apply fills', orderStatus, filled)
orderStatus[4] = filled[0][0] orderStatus[4] = filled[0][0]
orderStatus[5] = filled[0][1] orderStatus[5] = filled[0][1]
for( const i in filled[1] ) { for( const i in filled[1] ) {
@@ -7,7 +7,7 @@ export function applyFills( orderStatus, filled ) {
orderStatus[6][i] = filledIn orderStatus[6][i] = filledIn
orderStatus[7][i] = filledOut orderStatus[7][i] = filledOut
} }
// console.log('applied fills', orderStatus) console.log('applied fills', orderStatus)
} }

View File

@@ -5,7 +5,7 @@ import {useStore} from "@/store/store.js";
export function vaultAddress( owner, num=0) { export function vaultAddress( owner, num=0) {
const s = useStore() const s = useStore()
// console.log('vaultAddress', owner, s.factory, s.vaultInitCodeHash) console.log('vaultAddress', owner, s.factory, s.vaultInitCodeHash)
if( !owner ) if( !owner )
return null return null
const salt = ethers.solidityPackedKeccak256(['address','uint8'],[owner,num]) const salt = ethers.solidityPackedKeccak256(['address','uint8'],[owner,num])

View File

@@ -86,16 +86,91 @@ export function newTranche({
} }
} }
// enum Exchange {
// UniswapV2,
// UniswapV3
// }
export const Exchange = { export const Exchange = {
UniswapV2: 0, UniswapV2: 0,
UniswapV3: 1, UniswapV3: 1,
} }
export function sqrtX96(value) { export const OrderState = {
return BigInt(Math.round(Math.sqrt(value * 2 ** (96*2)))) Signing: -1,
Unknown: 0,
Open: 1,
Canceled: 2,
Filled: 3,
Expired: 4,
Underfunded: 5,
} }
export function parseOrderStatus(status) {
let [
order,
state,
start,
ocoGroup,
filledIn,
filledOut,
trancheFilledIn,
trancheFilledOut,
] = status
order = parseOrder(order)
filledIn = BigInt(filledIn)
filledOut = BigInt(filledOut)
trancheFilledIn = trancheFilledIn.map((f)=>BigInt(f))
trancheFilledOut = trancheFilledOut.map((f)=>BigInt(f))
return {
order, state, start, ocoGroup, filledIn, filledOut, trancheFilledIn, trancheFilledOut,
}
}
export function parseOrder(order) {
let [
tokenIn,
tokenOut,
route,
amount,
minFillAmount,
amountIsInput,
outputDirectlyToOwner,
chainOrder,
tranches,
] = order
route = parseRoute(route)
amount = BigInt(amount)
minFillAmount = BigInt(minFillAmount)
tranches = tranches.map(parseTranche)
return {
tokenIn, tokenOut, route, amount, minFillAmount, amountIsInput, outputDirectlyToOwner, chainOrder, tranches
}
}
export function parseRoute(route) {
let [exchange, fee] = route
return {exchange, fee} // todo enum?
}
export function parseTranche(tranche) {
let [
fraction,
startTimeIsRelative,
endTimeIsRelative,
minIsBarrier,
maxIsBarrier,
marketOrder,
_reserved5,
_reserved6,
_reserved7,
_reserved8,
_reserved16,
startTime,
endTime,
minIntercept,
minSlope,
maxIntercept,
maxSlope,
] = tranche
return {
fraction, startTimeIsRelative, endTimeIsRelative, minIsBarrier, maxIsBarrier, marketOrder,
startTime, endTime, minIntercept, minSlope, maxIntercept, maxSlope,
}
}

View File

@@ -6,6 +6,10 @@ import {ethers} from "ethers";
// synchronous version may return null but will trigger a lookup // synchronous version may return null but will trigger a lookup
export function token(addr) { export function token(addr) {
if( !addr ) {
// console.log('ignoring call to token', addr)
return null
}
const s = useStore() const s = useStore()
if( !(addr in s.tokens) ) { if( !(addr in s.tokens) ) {
getToken(addr) getToken(addr)
@@ -23,35 +27,48 @@ export async function getToken(addr) {
return s.tokens[addr] return s.tokens[addr]
} }
const _inFlightLookups = {}
export async function addExtraToken(addr) { export async function addExtraToken(addr) {
const prom = new Promise((resolve) => { if( !addr ) {
const s = useStore() console.log('ignoring call to add extra token', addr)
const chainId = s.chainId return
console.log('querying token', addr) }
socket.emit('lookupToken', chainId, addr, (info) => { if( !_inFlightLookups[addr] ) {
console.log('server token info', info) _inFlightLookups[addr] = true
if (info !== null) { const prom = new Promise((resolve) => {
s.addToken(chainId, info) const s = useStore()
resolve(info) const chainId = s.chainId
} console.log('querying token', addr)
else { socket.emit('lookupToken', chainId, addr, (info) => {
if( s.provider===null ) { console.log('server token info', addr, info)
console.log('warning: token lookup cancelled due to null provider', addr) if (info !== null) {
resolve(null)
return
}
const token = new ethers.Contract(addr, erc20Abi, s.provider)
Promise.all( [token.symbol(), token.decimals()] ).then((symbol,decimals)=>{
info = {
address: addr,
symbol: symbol,
decimals: decimals,
}
s.addToken(chainId, info) s.addToken(chainId, info)
resolve(info) resolve(info)
}) }
} else {
if( s.provider===null ) {
console.log('warning: token lookup cancelled due to null provider', addr)
resolve(null)
}
else {
const token = new ethers.Contract(addr, erc20Abi, s.provider)
Promise.all( [token.symbol(), token.decimals()] ).then((symbol,decimals)=>{
info = {
address: addr,
symbol: symbol,
decimals: decimals,
}
s.addToken(chainId, info)
resolve(info)
})
}
}
})
}) })
}) const result = await prom
return await prom delete _inFlightLookups[addr]
return result
}
} }

View File

@@ -96,6 +96,7 @@ let pendingOrders = []
function discoverVaults(owner) { function discoverVaults(owner) {
const s = useStore() const s = useStore()
console.log('discoverVaults', owner)
if( owner === null ) if( owner === null )
s.vaults = [] s.vaults = []
else else
@@ -112,6 +113,7 @@ function discoverVaults(owner) {
} }
async function _discoverVaults(owner) { async function _discoverVaults(owner) {
console.log('_discoverVaults',owner)
const result = [] const result = []
// todo multi-vault scan // todo multi-vault scan
const num = 0 const num = 0
@@ -174,6 +176,7 @@ export async function pendOrder(order) {
export async function cancelOrder(vault, orderIndex) { export async function cancelOrder(vault, orderIndex) {
console.log('cancel order', vault, orderIndex)
pendTransaction(async (signer)=> { pendTransaction(async (signer)=> {
const contract = contractOrNull(vault, vaultAbi, signer) const contract = contractOrNull(vault, vaultAbi, signer)
if( contract === null ) { if( contract === null ) {

View File

@@ -2,11 +2,9 @@
<v-table> <v-table>
<thead> <thead>
<tr> <tr>
<th class="num d-none d-md-table-cell">#</th> <!-- todo placement date instead --> <th class="num d-none d-md-table-cell">Date</th> <!-- todo placement date instead -->
<th class="token d-none d-sm-table-cell">Buy</th> <th class="token d-none d-sm-table-cell">Input</th>
<th class="token d-none d-sm-table-cell">Sell</th> <th class="token d-none d-sm-table-cell">Output</th>
<th class="token d-sm-none">Pair</th>
<th class="amount d-none d-md-table-cell">Amount</th>
<th class="amount">Remaining</th> <th class="amount">Remaining</th>
<th class="amount">Filled</th> <th class="amount">Filled</th>
<th class="amount d-none d-md-table-cell">Avg&nbsp;Price</th> <th class="amount d-none d-md-table-cell">Avg&nbsp;Price</th>
@@ -15,33 +13,32 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="[index, inTokenAddr, outTokenAddr, amount, amountTokenAddr, filled, avgPrice, state] in orders"> <tr v-for="st of orders">
<td class="d-none d-md-table-cell" style="width: 1em">{{parseInt(index)+1}}</td> <td class="">{{st.start}}</td>
<td class="token d-none d-sm-table-cell">{{tokenSymbol(inTokenAddr)}}</td> <td class="amount"><suspense><token-amount :addr="st.order.tokenIn" :amount="st.order.amountIsInput ? st.order.amount : null"/></suspense></td>
<td class="token d-none d-sm-table-cell">{{tokenSymbol(outTokenAddr)}}</td> <td class="amount"><suspense><token-amount :addr="st.order.tokenOut" :amount="!st.order.amountIsInput ? st.order.amount : null"/></suspense></td>
<td class="pair d-sm-none">Buy {{tokenSymbol(inTokenAddr)}}<br/>Sell {{tokenSymbol(outTokenAddr)}}</td> <td class="amount"><suspense><token-amount :addr="st.amountToken" :amount="st.order.amount-st.filled"/></suspense></td>
<td class="amount d-none d-md-table-cell">{{tokenAmount(amountTokenAddr, amount)}}</td> <td class="amount"><suspense><token-amount :addr="st.amountToken" :amount="st.filled"/></suspense></td>
<td class="amount">{{tokenAmount(amountTokenAddr, amount-filled)}}</td> <td class="amount">
<td class="amount">{{tokenAmount(amountTokenAddr, filled)}}</td> {{pairPrice(st.order.tokenIn, st.order.tokenOut, vaultAddr, st.index, st.avg)}}
<td class="amount d-none d-md-table-cell"> <btn v-if="pairPrice(st.order.tokenIn, st.order.tokenOut, vaultAddr, st.index, st.avg)!==''" size="small"
{{pairPrice(inTokenAddr, outTokenAddr, vaultAddr, index, avgPrice)}}
<btn v-if="pairPrice(inTokenAddr, outTokenAddr, vaultAddr, index, avgPrice)!==''" size="small"
variant="plain" variant="plain"
@click="inverted[[vaultAddr,index]] = !inverted[[vaultAddr,index]]"> @click="inverted[[vaultAddr,st.index]] = !inverted[[vaultAddr,st.index]]">
{{pair(inTokenAddr, outTokenAddr, vaultAddr,index)}} {{pair(st.order.tokenIn, st.order.tokenOut, vaultAddr, st.index)}}
</btn> </btn>
</td> </td>
<td class="status"> <td class="status">
<v-chip v-if="state===-1" prepend-icon="mdi-signature" color="yellow">Signing</v-chip> <v-chip v-if="st.state===OrderState.Signing" prepend-icon="mdi-signature" color="yellow">Signing</v-chip>
<v-chip v-if="state===0" class="d-none d-lg-inline-flex" prepend-icon="mdi-dots-horizontal" color="green">Open</v-chip> <v-chip v-if="st.state===OrderState.Open" class="d-none d-lg-inline-flex" prepend-icon="mdi-dots-horizontal" color="green">Open</v-chip>
<btn v-if="state===0" class="d-none d-sm-inline-flex d-lg-none" icon="mdi-cancel" color="red" @click="cancelOrder(vaultAddr,index)">Cancel</btn> <btn v-if="st.state===OrderState.Open" class="d-none d-sm-inline-flex d-lg-none" icon="mdi-cancel" color="red" @click="cancelOrder(vaultAddr,st.index)">Cancel</btn>
<btn v-if="state===0" class="d-sm-none" variant="plain" icon="mdi-cancel" color="red" @click="cancelOrder(vaultAddr,index)"/> <btn v-if="st.state===OrderState.Open" class="d-sm-none" variant="plain" icon="mdi-cancel" color="red" @click="cancelOrder(vaultAddr,st.index)"/>
<v-chip v-if="state===1" prepend-icon="mdi-cancel" color="red">Canceled</v-chip> <v-chip v-if="st.state===OrderState.Canceled" prepend-icon="mdi-cancel" color="red">Canceled</v-chip>
<v-chip v-if="state===2" prepend-icon="mdi-check-circle-outline" color="green">Completed</v-chip> <v-chip v-if="st.state===OrderState.Filled" prepend-icon="mdi-check-circle-outline" color="green">Completed</v-chip>
<v-chip v-if="state===3" prepend-icon="mdi-progress-check" color="grey-darken-1">Expired</v-chip> <v-chip v-if="st.state===OrderState.Expired" prepend-icon="mdi-progress-check" color="grey-darken-1">Expired</v-chip>
<v-chip v-if="st.state===OrderState.Underfunded" prepend-icon="mdi-alert" color="warning">Underfunded</v-chip>
</td> </td>
<td class="cancel d-none d-lg-table-cell"> <td class="cancel d-none d-lg-table-cell">
<btn v-if="state===0" icon="mdi-cancel" color="red" @click="cancelOrder(vaultAddr,index)">Cancel</btn> <btn v-if="st.state===OrderState.Open" icon="mdi-cancel" color="red" @click="cancelOrder(vaultAddr,st.index)">Cancel</btn>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -51,10 +48,13 @@
<script setup> <script setup>
import {FixedNumber} from "ethers"; import {FixedNumber} from "ethers";
import {useStore} from "@/store/store"; import {useStore} from "@/store/store";
import {computed, reactive} from "vue"; import {computed, defineAsyncComponent, reactive} from "vue";
import {token} from "@/blockchain/token.js"; import {token} from "@/blockchain/token.js";
import Btn from "@/components/Btn.vue" import Btn from "@/components/Btn.vue"
import {cancelOrder} from "@/blockchain/wallet.js"; import {cancelOrder} from "@/blockchain/wallet.js";
import {intervalString, dateString} from "@/misc.js";
import {OrderState} from "@/blockchain/orderlib.js";
const TokenAmount = defineAsyncComponent(()=>import('./TokenAmount.vue'))
const s = useStore() const s = useStore()
const props = defineProps(['vault']) const props = defineProps(['vault'])
@@ -66,15 +66,20 @@ function tokenSymbol(addr) {
return t ? t.symbol : addr return t ? t.symbol : addr
} }
/*
function tokenAmount(tokenAddr, amount) { function tokenAmount(tokenAddr, amount) {
if( !tokenAddr || !amount )
return ''
console.log('token amount', tokenAddr, amount)
if( typeof tokenAddr !== 'string' )
throw Error('broke')
const t = token(tokenAddr) const t = token(tokenAddr)
if( !t ) if( !t )
return '' return ''
// console.log('tokenAmount amount', typeof amount, amount)
// console.log('tokenAmount decimals', typeof t.decimals, t.decimals)
const amt = FixedNumber.fromValue(amount, t.decimals, {width:256, decimals:t.decimals, signed:false}) const amt = FixedNumber.fromValue(amount, t.decimals, {width:256, decimals:t.decimals, signed:false})
return `${amt} ${t.symbol}` return `${amt} ${t.symbol}`
} }
*/
// todo create a Price component that keeps inversion flags in the store and defaults to stablecoins as the quote // todo create a Price component that keeps inversion flags in the store and defaults to stablecoins as the quote
function pairPrice(inTokenAddr, outTokenAddr, vaultAddr, index, price) { function pairPrice(inTokenAddr, outTokenAddr, vaultAddr, index, price) {
@@ -110,43 +115,94 @@ function pair(inTokenAddr, outTokenAddr, vaultAddr, index) {
} }
const orders = computed(()=>{ const orders = computed(()=>{
// example twap
// status = [
// [ order
// "0x52412507302F6bAB17f56370b6a8F304CbB30Ce1", in token
// "0x63e187162a4c33A4D14465eA3859fFe423647710", out token
// [1, 500], route
// "100000000", amount
// "1000000", min amount
// false, amount is input
// false, output to owner
// 18446744073709552000, chain order
// [ tranches
// [21845, fraction
// true, start time relative
// true, end time relative
// false, min is barrier
// false, max is barrier
// false, market order
// false, reserved
// false, reserved
// false, reserved
// 0, reserved
// 0, reserved
// 0, start time
// 20, end time
// 730643660, min intercept
// 0, min slope
// 0, max intercept
// 0 max slope
// ],
// [...],
// [...],
// ]
// ],
// 4, state
// 1701809053, started at
// null, oco group
// "0", filled in
// "0", filled out
// ["0", "0", "0"], tranche filled in
// ["0", "0", "0"] tranche filled out
// ]
const result = [] const result = []
// for( const [status] of pendingOrders.reverse() ) {
// console.log('adding pended order')
// const inTokenAddr = status[0]
// const outTokenAddr = status[1]
// const amountIsInput = !!(status[4])
// const amountTokenAddr = amountIsInput ? inTokenAddr : outTokenAddr
// const amount = 0
// const filled = 0
// const avg = ''
// const state = -1 // pending
// result.push(['...', inTokenAddr, outTokenAddr, amount, amountTokenAddr, filled, avg, state])
// }
if( vaultAddr.value in s.orders ) { if( vaultAddr.value in s.orders ) {
for (const [index, status] of Object.entries(s.orders[vaultAddr.value]).reverse()) { for (const [index, status] of Object.entries(s.orders[vaultAddr.value]).reverse()) {
// console.log('order status', status) const st = {...status}
// [index, symbolA, symbolB, amount, amountSymbol, filled] console.log('order status', st)
const inTokenAddr = status[0][0] const o = {...st.order}
const outTokenAddr = status[0][1] st.order = o
const amountIsInput = !!(status[0][4]) result.push(st)
const amountTokenAddr = amountIsInput ? inTokenAddr : outTokenAddr st.index = parseInt(index)
// console.log('getamount', status[0][3]) o.tranches = o.tranches.map((tranche)=>{
const amount = BigInt(status[0][3]) const t = {...tranche}
// console.log('amount', amount) t.startTime = t.startTimeIsRelative ? intervalString(t.startTime) : dateString(t.startTime)
// console.log('getfilled', amountIsInput ? status[4] : status[5]) t.endTime = t.endTimeIsRelative ? intervalString(t.endTime) : dateString(t.endTime)
const filled = BigInt(amountIsInput ? status[4] : status[5]) return t
// console.log('filled', filled) })
const amountIn = BigInt(status[4]) st.start = dateString(st.start)
const amountOut = BigInt(status[5])
const fmtX18 = {decimals: 18, width: 256, signed: false}; const fmtX18 = {decimals: 18, width: 256, signed: false};
const avg = !amountIn || !amountOut ? null : st.filled = st.amountIsInput ? st.filledIn : st.filledOut
FixedNumber.fromValue(status[5], 0, fmtX18).div(FixedNumber.fromValue(status[4], 0, fmtX18)) st.avg = BigInt(st.filled) === 0n ? null :
const state = status[1] FixedNumber.fromValue(status.filledOut, 0, fmtX18)
result.push([index, inTokenAddr, outTokenAddr, amount, amountTokenAddr, filled, avg, state]) .div(FixedNumber.fromValue(status.filledIn, 0, fmtX18))
.toUnsafeFloat().toPrecision(5) // todo precision
st.trancheFilled = o.amountIsInput ? st.trancheFilledIn : st.trancheFilledOut
st.amountToken = o.amountIsInput ? o.tokenIn : o.tokenOut
// // [index, symbolA, symbolB, amount, amountSymbol, filled]
// const inTokenAddr = status[0][0]
// const outTokenAddr = status[0][1]
// const amountIsInput = !!(status[0][4])
// const amountTokenAddr = amountIsInput ? inTokenAddr : outTokenAddr
// // console.log('getamount', status[0][3])
// const amount = BigInt(status[0][3])
// // console.log('amount', amount)
// // console.log('getfilled', amountIsInput ? status[4] : status[5])
// const filled = BigInt(amountIsInput ? status[4] : status[5])
// // console.log('filled', filled)
// const amountIn = BigInt(status[4])
// const amountOut = BigInt(status[5])
// const avg = !amountIn || !amountOut ? null :
// FixedNumber.fromValue(status[5], 0, fmtX18).div(FixedNumber.fromValue(status[4], 0, fmtX18))
// const state = status[1]
// result.push([index, inTokenAddr, outTokenAddr, amount, amountTokenAddr, filled, avg, state])
} }
} }
// console.log('result', result) console.log('orders', result)
return result return result
}) })

View File

@@ -0,0 +1,27 @@
<template>
<span>{{fmtAmount}} {{ token.symbol || '' }}</span>
</template>
<script setup>
import {useStore} from "@/store/store";
import {getToken} from "@/blockchain/token.js";
import {FixedNumber} from "ethers";
import {computed, ref} from "vue";
const s = useStore()
const props = defineProps(['addr', 'amount'])
const token = await getToken(props.addr)
const fmtAmount = computed(() => {
if( props.amount === null || props.amount === undefined )
return ''
return FixedNumber.fromValue(props.amount, token.decimals, {
width: 256,
decimals: token.decimals
}).toUnsafeFloat().toPrecision(5) // todo precision
})
</script>
<style scoped lang="scss">
@use "src/styles/vars" as *;
</style>

View File

@@ -36,7 +36,6 @@ const fixed = computed(() => FixedNumber.fromValue(props.amount, token.decimals,
decimals: token.decimals decimals: token.decimals
})) }))
const imageSrc = computed(() => token.image) const imageSrc = computed(() => token.image)
const withdrawing = ref(false)
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -83,10 +83,8 @@ import {computed, defineAsyncComponent, ref} from "vue";
import {vaultAddress} from "@/blockchain/contract.js"; import {vaultAddress} from "@/blockchain/contract.js";
import {ensureVault} from "@/blockchain/wallet.js"; import {ensureVault} from "@/blockchain/wallet.js";
import CopyButton from "@/components/CopyButton.vue"; import CopyButton from "@/components/CopyButton.vue";
import NeedsProvider from "@/components/NeedsProvider.vue";
import Withdraw from "@/components/Withdraw.vue"; import Withdraw from "@/components/Withdraw.vue";
import PhoneCard from "@/components/PhoneCard.vue"; import PhoneCard from "@/components/PhoneCard.vue";
import Btn from "@/components/Btn.vue";
const TokenRow = defineAsyncComponent(()=>import('./TokenRow.vue')) const TokenRow = defineAsyncComponent(()=>import('./TokenRow.vue'))
const s = useStore() const s = useStore()

View File

@@ -59,3 +59,21 @@ export function routeInverted(route) {
return route && (route.token0 === s.tokenA) === s.inverted 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)
}

View File

@@ -3,6 +3,7 @@ import {useStore} from "@/store/store.js";
import {flushOrders, onChainChanged} from "@/blockchain/wallet.js"; import {flushOrders, onChainChanged} from "@/blockchain/wallet.js";
import {ethers} from "ethers"; import {ethers} from "ethers";
import {applyFills} from "@/blockchain/common.js"; import {applyFills} from "@/blockchain/common.js";
import {parseOrderStatus} from "@/blockchain/orderlib.js";
export const socket = io(import.meta.env.VITE_WS_URL || undefined, {transports: ["websocket"]}) export const socket = io(import.meta.env.VITE_WS_URL || undefined, {transports: ["websocket"]})
@@ -62,13 +63,16 @@ function handleOrderStatus(chainId, vault, orderIndex, status) {
const s = useStore() const s = useStore()
if( s.chainId.value !== chainId ) if( s.chainId.value !== chainId )
return return
console.log('o', chainId, vault, orderIndex, status) // message 'o' is a single order status
const parsed = parseOrderStatus(status);
console.log('o', chainId, vault, orderIndex, status, parsed)
if( !(vault in s.orders) ) if( !(vault in s.orders) )
s.orders[vault] = {} s.orders[vault] = {}
s.orders[vault][orderIndex] = status s.orders[vault][orderIndex] = parsed
} }
socket.on('os', (chainId, vault, orders) => { socket.on('os', (chainId, vault, orders) => {
// message 'os' has multiple order statuses
console.log('os', orders) console.log('os', orders)
for( const [orderIndex, status] of orders ) for( const [orderIndex, status] of orders )
handleOrderStatus(chainId, vault, orderIndex, status) handleOrderStatus(chainId, vault, orderIndex, status)

View File

@@ -97,14 +97,13 @@ export const useStore = defineStore('app', ()=> {
this.errors = result this.errors = result
} }
function addToken(chainId, info) { function addToken(chainId, info) {
this.$patch(() => { let extras = this.extraTokens[chainId]
let extras = extraTokens[chainId] if (extras === undefined) {
if (extras === undefined) { extras = {}
extras = {} this.extraTokens[chainId] = extras
extraTokens[chainId] = extras }
} extras[info.address] = info
extras[info.address] = info this.extraTokens = extras
})
} }
return { return {