199 lines
6.6 KiB
Vue
199 lines
6.6 KiB
Vue
|
|
<template>
|
|
<row-bar :color="color">
|
|
<color-band :color="color"/>
|
|
<div :style="faintColorStyle" style="width: 100%" class="justify-start align-content-start">
|
|
<v-text-field type="number" inputmode="numeric" pattern="[0-9]*\.?[0-9]*" v-model="order.amount" variant="outlined"
|
|
density="compact"
|
|
:hint="available" :persistent-hint="true"
|
|
min="0"
|
|
class="amount py-2" :color="color"
|
|
:label="order.amountIsTokenA ? 'Amount':('Value in '+co.selectedSymbol.quote.s)"
|
|
>
|
|
<template #prepend>
|
|
<v-btn variant="outlined" :color="color" class="ml-3"
|
|
:text="(order.buy ? 'Buy ' : 'Sell ') + co.selectedSymbol.base.s"
|
|
@click="order.buy=!order.buy"/>
|
|
</template>
|
|
<template #prepend-inner>
|
|
<v-btn variant="text" text="max" class="px-0" size="small"
|
|
:disabled="!maxAmount" @click="setMax"/>
|
|
</template>
|
|
<template #append-inner>
|
|
<v-btn :text="order.amountIsTokenA?co.baseToken.s:co.quoteToken.s+' worth'"
|
|
:color="color" variant="text" @click="toggleAmountToken"/>
|
|
</template>
|
|
</v-text-field>
|
|
<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-->
|
|
<span :style="colorStyle" class="ma-3">Add condition:</span>
|
|
<!-- <v-btn variant="flat" prepend-icon="mdi-clock-outline" @click="build('DCABuilder')">DCA</v-btn>-->
|
|
<v-btn :color="color" variant="text" prepend-icon="mdi-ray-vertex" @click="build(order,'LimitBuilder')">Limit</v-btn>
|
|
<!-- <v-btn variant="flat" prepend-icon="mdi-vector-line">Line</v-btn>-->
|
|
<!--
|
|
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, ref, watchEffect} from "vue";
|
|
import {lightenColor, lightenColor2} 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 {Exchange, newOrder} from "@/blockchain/orderlib.js";
|
|
|
|
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
|
|
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 null
|
|
|
|
// 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 chainOrder; // use NO_CHAIN for no chaining. chainOrder index must be < than this order's index for safety (written first) and chainOrder state must be Template
|
|
// Tranche[] tranches;
|
|
// }
|
|
const symbol = co.selectedSymbol
|
|
const fee = co.selectedPool[1]
|
|
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]()
|
|
console.log('tranches', ts)
|
|
tranches = [...tranches, ...ts]
|
|
}
|
|
|
|
return newOrder(tokenIn.value.a, tokenOut.value.a, Exchange.UniswapV3, fee, amount, amountIsInput, 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} })
|
|
|
|
const inToken = computed( ()=>props.order.buy ? co.quoteToken : co.baseToken )
|
|
const maxAmount = computed(()=>{
|
|
const token = inToken.value;
|
|
if (!token)
|
|
return null
|
|
const balance = s.balances[token.a]
|
|
if( !balance )
|
|
return 0
|
|
const divisor = os.amountIsTotal ? 1 : os.tranches
|
|
return balance / 10**token.d / divisor
|
|
})
|
|
const available = computed(()=>{
|
|
const max = maxAmount.value
|
|
return max === null ? '' : `Available: ${maxAmount.value} ${tokenIn.value.s}`
|
|
})
|
|
|
|
const lastMaxValue = ref(null)
|
|
|
|
function setMax() {
|
|
let amount = maxAmount.value
|
|
if (amount) {
|
|
const order = props.order
|
|
const price = co.price
|
|
if (order.amountIsTokenA===order.buy) {
|
|
if (order.buy)
|
|
amount /= price
|
|
else
|
|
amount *= price
|
|
}
|
|
amount = Number(amount.toPrecision(7))
|
|
lastMaxValue.value = amount
|
|
order.amount = amount
|
|
}
|
|
}
|
|
|
|
function toggleAmountToken() {
|
|
const order = props.order
|
|
order.amountIsTokenA=!order.amountIsTokenA
|
|
if (order.amount === lastMaxValue.value)
|
|
setMax()
|
|
}
|
|
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.amount {
|
|
max-width: 30em;
|
|
div.v-field {
|
|
padding-left: 0;
|
|
}
|
|
}
|
|
</style>
|