order sanity checks

This commit is contained in:
tim
2025-03-16 21:15:00 -04:00
parent b9975cda10
commit 5876efe29f
11 changed files with 130 additions and 33 deletions

View File

@@ -30,6 +30,7 @@ export function refreshOHLCSubs() {
}
keys.push(`${pool}|${period}`)
}
console.log('refreshing OHLC subs', keys)
socket.emit('subOHLCs', chainId, keys)
}

View File

@@ -46,8 +46,6 @@ export function allocationText(buy, weight, amount, baseSymbol, amountSymbol = n
const hasWeight = weight !== null && weight !== undefined && weight !== 1
if (hasWeight)
weight = Number(weight)
if (buy===undefined)
console.error('allocation text buy', buy)
let text = buy === undefined ? '' : buy ? 'Buy ' : 'Sell '
if (hasWeight)
text += `${(weight * 100).toFixed(1)}%`

View File

@@ -70,7 +70,7 @@ import BuilderFactory from "@/components/chart/BuilderFactory.vue";
import {builderFuncs, newBuilder, orderFuncs, useChartOrderStore} from "@/orderbuild.js";
import {useOrderStore, useStore} from "@/store/store.js";
import {computed, onUnmounted, onUpdated, ref, watchEffect} from "vue";
import {lightenColor, lightenColor2} from "@/misc.js";
import {lightenColor, lightenColor2, toPrecision} from "@/misc.js";
import {useTheme} from "vuetify";
import RowBar from "@/components/chart/RowBar.vue";
import ColorBand from "@/components/chart/ColorBand.vue";
@@ -114,7 +114,38 @@ function buildOrder() {
const order = props.order
console.log('buildOrder', order)
if (!order.amount) return null
if (!order.amount)
return {order: null, warnings: ['Amount must be greater than 0']}
const symbol = co.selectedSymbol
const amountDec = order.amountIsTokenA ? symbol.base.d : symbol.quote.d
const amount = BigInt(Math.trunc(order.amount * 10 ** amountDec))
let warnings = []
const inAddr = order.buy ? symbol.quote.a : symbol.base.a
const inDec = order.buy ? symbol.quote.d : symbol.base.d
const available = s.getBalance(inAddr) / 10 ** inDec
let needed
if (order.amountIsTokenA && order.buy)
// need quote currency to buy an amount of base
needed = order.amount * co.price
else if (order.amountIsTokenA && !order.buy)
// sell an exact amount of base
needed = order.amount
else if (!order.amountIsTokenA && order.buy)
// need an exact amount of quote
needed = order.amount
else if (!order.amountIsTokenA && !order.buy)
// sell a quote amount worth of base
needed = order.amount / co.price
else
throw new Error('Invalid order')
const deficit = needed - available
if (deficit > 0) {
const inSymbol = order.buy ? symbol.quote.s : symbol.base.s
warnings.push(`Insufficient funds. Add ${toPrecision(deficit, 5)} ${inSymbol} to your vault to complete this order.`)
}
// struct SwapOrder {
// address tokenIn;
@@ -127,20 +158,24 @@ function buildOrder() {
// uint64 conditionalOrder; // use NO_CONDITIONAL_ORDER for no chaining. conditionalOrder index must be < than this order's index for safety (written first) and conditionalOrder state must be Template
// Tranche[] tranches;
// }
const symbol = co.selectedSymbol
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)
let tranches = []
for (const builder of builders.value) {
console.log('builder', builder)
const ts = builderFuncs[builder.id]()
const built = builderFuncs[builder.id]()
const ts = built.tranches
const ws = built.warnings
console.log('tranches', ts)
tranches = [...tranches, ...ts]
warnings = [...warnings, ...ws]
}
return newOrder(tokenIn.value.a, tokenOut.value.a, symbol.exchangeId, symbol.fee, amount, amountIsInput, symbol.inverted, tranches)
return {
warnings,
order: newOrder(tokenIn.value.a, tokenOut.value.a, symbol.exchangeId, symbol.fee,
amount, amountIsInput, symbol.inverted, tranches),
}
}

View File

@@ -7,7 +7,7 @@
Place Dexorder
</v-btn>
<v-btn variant="text" prepend-icon="mdi-delete" v-if="co.orders.length>0"
:disabled="!orderChanged" @click="cancelOrder">Reset</v-btn>
:disabled="!orderChanged" @click="resetOrder">Reset</v-btn>
</template>
<div class="overflow-y-auto">
<needs-chart>
@@ -19,7 +19,21 @@
<v-card-actions>
<v-spacer/>
<v-btn @click="()=>showResetDialog=false">Keep Existing</v-btn>
<v-btn @click="()=>{co.resetOrders(); showResetDialog=false}" color="red" text="Reset Order"/>
<v-btn @click="doResetOrder" color="red" text="Reset Order"/>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="showWarnings" max-width="300">
<v-card prepend-icon="mdi-warning" title="Order Warnings" text="Your order has the following warnings:">
<v-card-text>
<v-list>
<v-list-item v-for="w of orderWarnings">{{w}}</v-list-item>
</v-list>
</v-card-text>
<v-card-actions>
<v-spacer/>
<v-btn @click="()=>showWarnings=false">Back</v-btn>
<v-btn @click="doPlaceOrder">Place Order</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
@@ -78,10 +92,19 @@ const valid = computed(()=>{
const orderChanged = computed(()=>!(co.orders.length===1 && co.orders[0].builders.length===0 && !co.orders[0].amount))
function cancelOrder() {
const showWarnings = ref(false)
const orderWarnings = ref([])
function resetOrder() {
showResetDialog.value = true
}
function doResetOrder() {
co.resetOrders();
orderWarnings.value = []
showResetDialog.value = false
}
watchEffect(()=>{
const removable = []
for (const order of ws.pendingOrders) {
@@ -108,17 +131,31 @@ watchEffect(()=>{
}
})
let built = []
async function placeOrder() {
const chartOrders = co.orders;
const built = []
const allWarns = []
built = []
for (const chartOrder of chartOrders) {
console.log('chartOrder', chartOrder)
const buildOrder = orderFuncs[chartOrder.id]
const order = buildOrder()
const {order, warnings} = buildOrder()
built.push(order)
allWarns.push(...warnings)
}
console.log('place orders', built)
if (allWarns.length > 0) {
orderWarnings.value = allWarns
showWarnings.value = true
return
}
await doPlaceOrder()
}
async function doPlaceOrder() {
console.log('place orders')
if (ws.transaction!==null) {
console.error('Transaction already in progress')
}

View File

@@ -85,8 +85,6 @@ const amountSymbol = computed(()=>props.order.amountIsTokenA ? co.selectedSymbol
const allocationTexts = computed(()=>weights.value.map((w)=>allocationText(props.order.buy, w, w * props.order.amount, co.selectedSymbol.base.s, amountSymbol.value)))
const endTimes = computed(()=>{
if (props.builder.rungs === 1)
return DISTANT_FUTURE
const ts = times.value
const window = Math.max(MIN_EXECUTION_TIME, Math.floor((ts[ts.length-1]-ts[0])/props.builder.rungs))
return ts.map((t)=>t+window)
@@ -151,28 +149,28 @@ const timeEndpoints = computed({
get() { return _timeEndpoints.value},
set(v) {
const [a, b] = v
update(a,b)
update(a,b, true, true)
}
})
function updateA(a) {
update(a, _timeEndpoints.value[1], true)
update(a, _timeEndpoints.value[1], true, false)
}
function updateB(b) {
update(_timeEndpoints.value[0], b, false)
update(_timeEndpoints.value[0], b, false, true)
}
function update(a, b, updatingA) {
if (updatingA) {
function update(a, b, updateA, updateB) {
if (updateA) {
const minB = a + minWidth.value
if (b < minB)
b = minB
}
else {
if (updateB) {
const maxA = b - minWidth.value
if (a > maxA)
a = maxA
@@ -194,21 +192,27 @@ function buildTranches() {
const order = props.order
const builder = props.builder
const tranches = []
const warnings = []
console.log('buildTranches', builder, order, tranches)
const ts = times.value
const ets = endTimes.value
const ws = weights.value
console.log('buildTranches times ends weights', ts, ets, ws)
for(let i=0; i<ts.length; i++) {
const endTime = Math.max(ets[i],ts[i]+60);
console.log('time check', endTime, s.clock)
if (endTime <= s.clock)
warnings.push(`Tranche already expired at ${new Date(endTime*1000)}`)
const t = newTranche({
fraction: ws[i] * MAX_FRACTION,
startTime: ts[i],
endTime: Math.max(ets[i],ts[i]+60), // always give at least 60 seconds of window to execute
endTime, // always give at least 60 seconds of window to execute
slippage: builder.slippage,
})
tranches.push(t)
}
return tranches
return {tranches, warnings}
}

View File

@@ -101,7 +101,9 @@ import {computed, ref} from "vue";
import {allocationText, DLine} from "@/charts/shape.js";
import {vectorEquals, vectorInterpolate} from "@/vector.js";
import AbsoluteTimeEntry from "@/components/AbsoluteTimeEntry.vue";
import {useStore} from "@/store/store.js";
const s = useStore()
const co = useChartOrderStore()
const props = defineProps(['order', 'builder'])
const emit = defineEmits(['update:builder'])
@@ -132,6 +134,7 @@ function buildTranches() {
const order = props.order
const builder = props.builder
const tranches = []
const warnings = []
console.log('buildTranches', builder, order, _endpoints.value)
const la = _endpoints.value[0] // use the flatline format which is a vector of length 4, useful for vectorInterpolate()
@@ -149,6 +152,9 @@ function buildTranches() {
t.startTime = reversed ? line[2] : line[0]
if (reversed ? !el : !er)
t.endTime = reversed ? line[0] : line[2]
if (t.endTime <= s.clock)
warnings.push(`Tranche already expired at ${new Date(t.endTime*1000)}`)
// console.log('tranche start/end',
// t.startTime === DISTANT_PAST ? 'PAST' : t.startTime,
// t.endTime === DISTANT_FUTURE ? 'FUTURE' : t.endTime)
@@ -157,7 +163,7 @@ function buildTranches() {
}
// if( flipped.value )
// tranches.reverse()
return tranches
return {tranches, warnings}
}

View File

@@ -81,6 +81,7 @@ function buildTranches() {
const order = props.order
const builder = props.builder
const tranches = []
const warnings = []
console.log('buildTranches', builder, order, tranches)
const ps = prices.value
@@ -90,6 +91,9 @@ function buildTranches() {
const w = ws[i]
const t = newTranche({
// todo start/end
// todo check expired
// if (endTime <= s.clock)
// warnings.push(`Tranche already expired at ${new Date(endTime*1000)}`)
fraction: w * MAX_FRACTION,
})
const symbol = co.selectedSymbol
@@ -99,7 +103,7 @@ function buildTranches() {
}
if (!flipped.value)
tranches.reverse()
return tranches
return {tranches, warnings}
}

View File

@@ -37,7 +37,14 @@ const slippage = computed({
})
function buildTranches() {
return [newTranche({slippage: slippage.value/100})]
let warnings = []
if (slippage.value < 0.01)
warnings.push('Slippage will be set to the minimum of 0.01%')
const slip = Math.min(slippage.value, 0.01)
return {
tranches: [newTranche({slippage: slip / 100})],
warnings,
}
}
onMounted(() => builderFuncs[props.builder.id] = buildTranches)

View File

@@ -102,7 +102,6 @@ watchEffect(()=>{
})
function setEndpoints(a, b) {
// console.log('rb setting endpoints', devectorize(a), devectorize(b))
endpoints.value = [devectorize(a), devectorize(b)]
}
@@ -125,8 +124,7 @@ const rungs = computed({
r = Number(r)
const prevR = Number(props.builder.rungs)
props.builder.rungs = r
// console.log('set rungs', prevR, r, a, b)
if ( r > 0 && vectorIsNull(b) ) {
if ( prevR === 1 && r > 1 ) {
// convert single shape to a range
if (props.mode===0) {
const width = vectorize(props.stdWidth)
@@ -143,7 +141,7 @@ const rungs = computed({
else
throw Error(`Unknown rung mode ${props.mode}`)
}
else if ( r === 1 && !vectorIsNull(b) ) {
else if ( prevR > 1 && r === 1 ) {
// convert from a range to a single shape
if (props.mode===0)
a = vectorDiv(vectorAdd(a,b), 2)

View File

@@ -56,7 +56,8 @@ export const useChartOrderStore = defineStore('chart_orders', () => {
if (!selectedSymbol.value)
return null
const s = useStore()
let result = s.poolPrices[[s.chainId, selectedSymbol.address]]
const key = [s.chainId, selectedSymbol.value.address];
let result = s.poolPrices[key]
if (selectedSymbol.value.inverted)
result = 1 / result
return result

View File

@@ -133,6 +133,11 @@ export const useStore = defineStore('app', ()=> {
this.extraTokens = extras
}
function getBalance(tokenAddr) {
const found = this.balances[tokenAddr]
return found === undefined ? 0 : found
}
return {
connected,
nav, chainId, chainInfo, chain, provider, providerRef, vaultInitCodeHash, account, vaults, vaultVersions,
@@ -141,6 +146,7 @@ export const useStore = defineStore('app', ()=> {
mockenv, mockCoins,
removeTransactionSender, error, closeError, addToken, clock, timeZone, balances,
approved, regionApproved, walletApproved,
getBalance
}
})