319 lines
12 KiB
Vue
319 lines
12 KiB
Vue
<template>
|
|
<div>
|
|
<v-data-table :headers="datatableHeaders" :items="orders" item-value="index"
|
|
item-selectable="selectable" :show-select="false" :show-expand="true">
|
|
<template v-slot:item.start="{ value }">{{ dateString(value) }}</template>
|
|
<template v-slot:item.input="{ item }">
|
|
<suspense v-if="item.order.amountIsInput">
|
|
<span>
|
|
<token-amount :addr="item.order.tokenIn" :amount="item.filled" :raw="true"/>
|
|
/
|
|
<token-amount :addr="item.order.tokenIn" :amount="item.order.amount"/>
|
|
</span>
|
|
</suspense>
|
|
<suspense v-if="!item.order.amountIsInput">
|
|
<token-symbol :addr="item.order.tokenIn"/>
|
|
</suspense>
|
|
</template>
|
|
<template v-slot:item.output="{ item }">
|
|
<suspense v-if="!item.order.amountIsInput">
|
|
<span>
|
|
<token-amount :addr="item.order.tokenOut" :amount="item.filled" :raw="true"/>
|
|
/
|
|
<token-amount :addr="item.order.tokenOut" :amount="item.order.amount"/>
|
|
</span>
|
|
</suspense>
|
|
<suspense v-if="item.order.amountIsInput">
|
|
<token-symbol :addr="item.order.tokenOut"/>
|
|
</suspense>
|
|
</template>
|
|
<!--
|
|
<template v-slot:item.remaining="{ item }">
|
|
<suspense>
|
|
<token-amount :addr="item.amountToken" :amount="item.remaining"/>
|
|
</suspense>
|
|
</template>
|
|
<template v-slot:item.filled="{ item }">
|
|
<suspense>
|
|
<token-amount :addr="item.amountToken" :amount="item.filled"/>
|
|
</suspense>
|
|
</template>
|
|
-->
|
|
<template v-slot:item.avg="{ item }">
|
|
<pair-price :base-addr="item.order.tokenIn" :quote-addr="item.order.tokenOut" :value="item.avg" :show-btn="true"/>
|
|
</template>
|
|
<template v-slot:item.status="{ item }">
|
|
<v-chip v-if="item.state===PendingOrderState.Submitted || item.state===PendingOrderState.Signing"
|
|
prepend-icon="mdi-signature">Wallet Signing</v-chip>
|
|
<v-chip v-if="item.state===PendingOrderState.Rejected" prepend-icon="mdi-cancel">Rejected</v-chip>
|
|
<v-chip v-if="item.state===PendingOrderState.Sent" prepend-icon="mdi-send">Sent</v-chip>
|
|
<v-chip v-if="item.state===OrderState.Open" class="d-none d-lg-inline-flex" prepend-icon="mdi-dots-horizontal"
|
|
color="green">Open
|
|
</v-chip>
|
|
<btn v-if="isOpen(item.state)" class="d-none d-sm-inline-flex" icon="mdi-cancel" color="red"
|
|
@click="cancelOrder(vaultAddr,item.index)">Cancel
|
|
</btn>
|
|
<btn v-if="isOpen(item.state)" class="d-sm-none" variant="plain" icon="mdi-cancel" color="red"
|
|
@click="cancelOrder(vaultAddr,item.index)"/>
|
|
<v-chip v-if="item.state===OrderState.Canceled" prepend-icon="mdi-cancel" color="red">Canceled</v-chip>
|
|
<v-chip v-if="item.state===OrderState.Filled" prepend-icon="mdi-check-circle-outline" color="green">Completed
|
|
</v-chip>
|
|
<v-chip v-if="item.state===OrderState.Expired" prepend-icon="mdi-progress-check" color="grey-darken-1">Expired
|
|
</v-chip>
|
|
<v-chip v-if="item.state===OrderState.Underfunded" prepend-icon="mdi-alert" color="warning">Underfunded</v-chip>
|
|
</template>
|
|
<!-- <template v-slot:item.action="{item}">-->
|
|
<!-- <btn v-if="item.state===OrderState.Open" icon="mdi-cancel" color="red"-->
|
|
<!-- @click="cancelOrder(vaultAddr,item.index)">Cancel-->
|
|
<!-- </btn>-->
|
|
<!-- </template>-->
|
|
<template v-slot:expanded-row="{item}">
|
|
<tr>
|
|
<td> </td>
|
|
<td colspan="100">
|
|
<v-table>
|
|
<!--
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
<th>Filled</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
-->
|
|
<tbody>
|
|
<tr v-for="(t, i) in item.order.tranches">
|
|
<td class="text-right">Tranche {{ i + 1 }}</td>
|
|
<td class="text-center w-33">
|
|
<suspense v-if="item.state > OrderState.Signing">
|
|
<span>
|
|
<token-amount :addr="item.amountToken" :amount="item.trancheFilled[i]" :raw="true"/>
|
|
/
|
|
</span>
|
|
</suspense>
|
|
<suspense>
|
|
<span>
|
|
<token-amount :addr="item.amountToken" :amount="item.order.amount*BigInt(t.fraction)/65535n"/>
|
|
</span>
|
|
</suspense>
|
|
</td>
|
|
<td class="text-left">
|
|
<span class="mx-3">{{ describeTrancheTime(item, true, t) }}</span>
|
|
<span class="mx-3">{{ describeTrancheTime(item, false, t) }}</span>
|
|
<span class="mx-3">{{ describeTrancheLine(item, true, t.minIntercept, t.minSlope) }}</span>
|
|
<span class="mx-3">{{ describeTrancheLine(item, false, t.maxIntercept, t.maxSlope) }}</span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</v-table>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
</v-data-table>
|
|
<btn icon="mdi-delete-alert" color="red" class="mx-3 mb-3" @click="cancelAll(vaultAddr)">Cancel All Orders</btn>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {FixedNumber} from "ethers";
|
|
import {useStore} from "@/store/store";
|
|
import {computed, defineAsyncComponent, ref} from "vue";
|
|
import Btn from "@/components/Btn.vue"
|
|
import {cancelAll, cancelOrder, PendingOrderState, useWalletStore} from "@/blockchain/wallet.js";
|
|
import {dateString, pairPriceAddr} from "@/misc.js";
|
|
import {isOpen, OrderState} from "@/blockchain/orderlib.js";
|
|
import NewOrder from "@/components/NewOrder.vue";
|
|
import PairPrice from "@/components/PairPrice.vue";
|
|
|
|
const TokenAmount = defineAsyncComponent(()=>import('./TokenAmount.vue'))
|
|
const TokenSymbol = defineAsyncComponent(()=>import('./TokenSymbol.vue'))
|
|
|
|
const s = useStore()
|
|
const ws = useWalletStore()
|
|
const props = defineProps(['vault'])
|
|
const vaultAddr = computed(()=>props.vault?props.vault:s.vault)
|
|
const inverted = ref({})
|
|
|
|
|
|
// <thead>
|
|
// <tr>
|
|
// <th className="num d-none d-md-table-cell">Date</th>
|
|
// <th className="token d-none d-sm-table-cell">Input</th>
|
|
// <th className="token d-none d-sm-table-cell">Output</th>
|
|
// <th className="amount">Remaining</th>
|
|
// <th className="amount">Filled</th>
|
|
// <th className="amount d-none d-md-table-cell">Avg Price</th>
|
|
// <th className="status">Status</th>
|
|
// <th className="cancel d-none d-lg-table-cell"> </th>
|
|
// </tr>
|
|
// </thead>
|
|
const datatableHeaders = [
|
|
{title: 'Date', align: 'start', key: 'start'},
|
|
{title: 'Input', align: 'end', key: 'input'},
|
|
{title: 'Output', align: 'end', key: 'output'},
|
|
// {title: 'Remaining', align: 'end', key: 'remaining'},
|
|
// {title: 'Filled', align: 'end', key: 'filled'},
|
|
{title: 'Avg Price', align: 'end', key: 'avg'},
|
|
{title: 'Status', align: 'end', key: 'status'},
|
|
// {title: '', align: 'end', key: 'action'},
|
|
];
|
|
|
|
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 = []
|
|
|
|
// in-flight orders
|
|
for (const pend of ws.pendingOrders) {
|
|
console.log('pended order', pend)
|
|
result.push({
|
|
start: pend.placementTime,
|
|
order: pend.order,
|
|
filled: 0,
|
|
state: pend.state,
|
|
amountToken: pend.order.amountIsInput ? pend.order.tokenIn : pend.order.tokenOut
|
|
})
|
|
}
|
|
|
|
// historical orders
|
|
if( vaultAddr.value in s.orders ) {
|
|
for (const [index, status] of Object.entries(s.orders[vaultAddr.value]).reverse()) {
|
|
const st = {...status}
|
|
const o = {...st.order}
|
|
st.order = o
|
|
console.log('order status', st)
|
|
result.push(st)
|
|
st.index = parseInt(index)
|
|
o.tranches = o.tranches.map((tranche)=>{
|
|
const t = {...tranche}
|
|
// t.startTime = t.startTimeIsRelative ? intervalString(t.startTime) : dateString(t.startTime)
|
|
// t.endTime = t.endTimeIsRelative ? intervalString(t.endTime) : dateString(t.endTime)
|
|
return t
|
|
})
|
|
const fmtX18 = {decimals: 18, width: 256, signed: false};
|
|
st.filled = o.amountIsInput ? st.filledIn : st.filledOut
|
|
if(BigInt(st.filled) === 0n)
|
|
st.avg = null
|
|
else {
|
|
st.avg = FixedNumber.fromValue(status.filledOut, 0, fmtX18)
|
|
.div(FixedNumber.fromValue(status.filledIn, 0, fmtX18)).toUnsafeFloat();
|
|
if( o.tokenIn > o.tokenOut )
|
|
st.avg = 1/st.avg
|
|
}
|
|
st.trancheFilled = o.amountIsInput ? st.trancheFilledIn : st.trancheFilledOut
|
|
st.amountToken = o.amountIsInput ? o.tokenIn : o.tokenOut
|
|
st.input = o.amountIsInput ? o.amount : 0
|
|
st.output = !o.amountIsInput ? o.amount : 0
|
|
st.remaining = o.amount - st.filled
|
|
st.selectable = st.state===OrderState.Open
|
|
st.status = st.state
|
|
}
|
|
}
|
|
console.log('orders', result)
|
|
return result
|
|
})
|
|
|
|
function describeTrancheTime(st, isStart, t) {
|
|
let result = ''
|
|
if( t.minIsBarrier || t.maxIsBarrier )
|
|
return 'barrier'
|
|
if( isStart && t.startTime > 0 ) {
|
|
const started = t.startTimeIsRelative ? st.start + t.startTime : t.startTime
|
|
result += started*1000 < Date.now() ? 'Activated ' : 'Activates '
|
|
result += dateString(started) + ' '
|
|
}
|
|
if( !isStart && t.endTime < 4294967295 ) {
|
|
const ended = t.endTimeIsRelative ? st.start + t.endTime : t.endTime
|
|
result += ended*1000 < Date.now() ? 'Expired ' : 'Expires '
|
|
result += dateString(ended)
|
|
}
|
|
return result
|
|
}
|
|
|
|
function describeTrancheLine(st, isMin, b, m) {
|
|
// todo make this a PairPrice
|
|
if( b===0 && m===0 ) return ''
|
|
const chainId = useStore().chainId
|
|
console.log('tranche line', isMin, b, m)
|
|
const inverted = st.order.tokenIn > st.order.tokenOut
|
|
const t0 = inverted ? st.order.tokenIn : st.order.tokenOut
|
|
const t1 = !inverted ? st.order.tokenIn : st.order.tokenOut
|
|
if( m !== 0 ) {
|
|
const limit = b + m * s.clock
|
|
const price = pairPriceAddr(chainId, t0, t1, limit);
|
|
return 'diagonal ' + (price === null ? null : price.toPrecision(5))
|
|
}
|
|
const price = pairPriceAddr(chainId, t0, t1, b)
|
|
return (isMin === st.order.amountIsInput ? 'dont-chase ' : 'limit ') + (price === null ? null : price.toPrecision(5))
|
|
}
|
|
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
@use "src/styles/vars" as *;
|
|
|
|
// columns
|
|
.num {
|
|
min-width: 1em;
|
|
}
|
|
.token {
|
|
min-width: 3em;
|
|
max-width: 5em;
|
|
}
|
|
.pair {
|
|
min-width: 5em;
|
|
}
|
|
.amount {
|
|
min-width: 3em;
|
|
max-width: 6em;
|
|
overflow-x: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.status {
|
|
max-width: 8em;
|
|
}
|
|
.cancel {
|
|
}
|
|
|
|
</style>
|