Files
web/src/components/chart/ChartOrder.vue
2025-03-19 20:27:10 -04:00

192 lines
7.0 KiB
Vue

<template>
<row-bar :color="color">
<color-band :color="color"/>
<div style="width: 100%" class="justify-start align-content-start">
<order-amount :order="props.order" :color="color"/>
<template v-for="b in builders">
<builder-factory :order="order" :builder="b"/>
</template>
<div class="my-3">
<div v-if="order.builders.length===0"> <!--todo remove gralpha limitation of one builder-->
<v-tooltip text="Up to 1000 equal parts spread across time" location="top">
<template v-slot:activator="{ props }">
<span v-bind="props">
<v-btn :color="color" variant="text" prepend-icon="mdi-clock-outline" @click="build(order,'DCABuilder')">DCA</v-btn>
</span>
</template>
</v-tooltip>
<v-tooltip text="Trade a price level" location="top">
<template v-slot:activator="{ props }">
<span v-bind="props">
<v-btn :color="color" variant="text" prepend-icon="mdi-ray-vertex" @click="build(order,'LimitBuilder')">Limit</v-btn>
</span>
</template>
</v-tooltip>
<v-tooltip text="Trade trends and channels" location="top">
<template v-slot:activator="{ props }">
<span v-bind="props">
<v-btn :color="color" variant="text" prepend-icon="mdi-vector-line" @click="build(order,'DiagonalBuilder')">Diagonal</v-btn>
</span>
</template>
</v-tooltip>
<v-tooltip text="Up to 10 weighted parts spaced out in time" location="top">
<template v-slot:activator="{ props }">
<span v-bind="props">
<v-btn :color="color" variant="text" prepend-icon="mdi-reorder-vertical" @click="build(order,'DateBuilder')">Dates</v-btn>
</span>
</template>
</v-tooltip>
<v-tooltip text="Coming Soon! Stoplosses and Takeprofits" location="top">
<template v-slot:activator="{ props }">
<span v-bind="props">
<v-btn :color="color" variant="text" prepend-icon="mdi-plus-minus" disabled>Stoploss</v-btn>
</span>
</template>
</v-tooltip>
<!-- mdi-ray-start-end mdi-vector-polyline -->
</div>
</div>
</div>
</row-bar>
</template>
<script setup>
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, watchEffect} from "vue";
import {toPrecision} from "@/misc.js";
import {useTheme} from "vuetify";
import RowBar from "@/components/chart/RowBar.vue";
import ColorBand from "@/components/chart/ColorBand.vue";
import Color from "color";
import {newOrder} from "@/blockchain/orderlib.js";
import OrderAmount from "@/components/chart/OrderAmount.vue";
const props = defineProps(['order'])
const s = useStore()
const co = useChartOrderStore()
const os = useOrderStore()
const marketBuilder = newBuilder('MarketBuilder')
const builders = computed(()=>props.order.builders.length > 0 ? props.order.builders : [marketBuilder])
const tokenIn = computed(()=>props.order.buy ? co.quoteToken : co.baseToken)
const tokenOut = computed(()=>props.order.buy ? co.baseToken : co.quoteToken)
console.log('order', props.order)
function build(order, component, options={}) {
order.builders.push(newBuilder(component, options))
}
// check order validity
watchEffect(()=>{
let valid = props.order.amount > 0
if (valid) {
for (const builder of builders.value) {
if (!builder.valid) {
valid = false
break
}
}
}
props.order.valid = valid
})
function buildOrder() {
const order = props.order
console.log('buildOrder', order)
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;
// address tokenOut;
// Route route;
// uint256 amount;
// uint256 minFillAmount; // if a tranche has less than this amount available to fill, it is considered completed
// bool amountIsInput;
// bool outputDirectlyToOwner;
// 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 amountIsInput = !!(order.amountIsTokenA ^ order.buy)
let tranches = []
for (const builder of builders.value) {
console.log('builder', builder)
const built = builderFuncs[builder.id]()
const ts = built.tranches
const ws = built.warnings
console.log('tranches', ts)
tranches = [...tranches, ...ts]
warnings = [...warnings, ...ws]
}
return {
warnings,
order: newOrder(tokenIn.value.a, tokenOut.value.a, symbol.exchangeId, symbol.fee,
amount, amountIsInput, symbol.inverted, tranches),
}
}
let lastId = props.order.id
orderFuncs[lastId] = buildOrder
onUpdated(()=>{
// console.log('onUpdated', lastId, ...arguments)
delete orderFuncs[lastId]
orderFuncs[props.order.id] = buildOrder
lastId = props.order.id
})
onUnmounted(() => delete orderFuncs[lastId])
const theme = useTheme().current
const color = computed(()=>new Color(props.order.buy?theme.value.colors.success:theme.value.colors.error).darken(0.2).string())
// const lightColor = computed(() => lightenColor(color.value))
// const faintColor = computed(() => lightenColor2(color.value))
// const colorStyle = computed(() => { return {'color': color.value} })
// const bgColorStyle = computed(() => { return {'background-color': color.value} })
// const lightColorStyle = computed(() => { return {'background-color': lightColor.value} })
// const faintColorStyle = computed(() => { return {'background-color': faintColor.value} })
</script>