chainInfo from server, routes, wallet rework

This commit is contained in:
Tim Olson
2023-10-04 03:40:47 -04:00
parent e7a9600c54
commit 7239987867
26 changed files with 603 additions and 297 deletions

View File

@@ -1,80 +1,124 @@
<template>
<v-card title="DCA / TWAP" subtitle="Split order across time" class="order-card" elevation="4">
<v-card-text>
<pair-entry v-model="pair"/>
<v-text-field label='Amount' type="number" step="1" variant="outlined" aria-valuemin="0" min="0"
:model-value="amount" :rules="[validateRequired,validateAmount]" class="amount">
<template v-slot:append-inner>
<v-btn @click="amountIsBase=!amountIsBase" variant="outlined" class="mr-2">
{{ amountIsBase ? pair.tokenA.symbol : pair.tokenB.symbol }}
</v-btn>
<v-btn :text="amountIsTotal ? 'total' : 'per tranche'" variant="outlined"
@click="amountIsTotal=!amountIsTotal" class="total"/>
</template>
</v-text-field>
<v-text-field label="Tranches" type="number" variant="outlined" aria-valuemin="1" min="1"
:model-value="tranches" :rules="[validateRequired]">
<template v-slot:prepend-inner>
<v-btn class="split-into mr-2" variant="outlined" @click="amountIsTotal=!amountIsTotal">
{{ amountIsTotal ? 'Split into' : 'Times' }}
</v-btn>
</template>
<template v-slot:append-inner>tranches</template>
</v-text-field>
<v-text-field type="number" variant="outlined" :min="1" v-model="interval" class="interval"
:label="intervalIsTotal ? 'Completion time' : 'Time between tranches'">
<!-- <template v-slot:append>APART</template>-->
<template v-slot:prepend-inner>
<v-btn variant="outlined" :text="intervalIsTotal ? 'Within' : 'Spaced apart'" class="within mr-2"
@click="intervalIsTotal=!intervalIsTotal"/>
</template>
<template v-slot:append-inner>
<v-btn variant="outlined" :text="timeUnits[timeUnitIndex]" @click="toggleTimeUnits" class="time-units"/>
</template>
</v-text-field>
<v-text-field v-model="limitPrice" :label="(limitIsMinimum?'Minimum':'Maximum')+' Price'" type="number" variant="outlined" aria-valuemin="0" min="0"
clearable :rules="[validateAmount, validateMin]">
<template v-slot:append-inner>
<v-btn variant="outlined" @click="inverted=!inverted">
{{ inverted ? pair.tokenB.symbol + '/' + pair.tokenA.symbol : pair.tokenA.symbol + '/' + pair.tokenB.symbol }}
</v-btn>
</template>
</v-text-field>
<!--
<v-text-field v-model="minPrice" label="Minimum Price" type="number" variant="outlined" aria-valuemin="0" min="0"
clearable :rules="[validateAmount, validateMin]">
<template v-slot:append-inner>
<v-btn variant="outlined" @click="inverted=!inverted">
{{ inverted ? pair.tokenB.symbol + '/' + pair.tokenA.symbol : pair.tokenA.symbol + '/' + pair.tokenB.symbol }}
</v-btn>
</template>
</v-text-field>
<v-text-field v-model="maxPrice" label="Maximum Price" type="number" variant="outlined" aria-valuemin="0" min="0"
clearable :rules="[validateAmount, validateMax]">
<template v-slot:append-inner>
<v-btn variant="outlined" @click="inverted=!inverted">
{{ inverted ? pair.tokenB.symbol + '/' + pair.tokenA.symbol : pair.tokenA.symbol + '/' + pair.tokenB.symbol }}
</v-btn>
</template>
</v-text-field>
-->
<NeedsQueryHelper>
<PhoneCard>
<v-card-title class="big">DCA / TWAP</v-card-title>
<v-card-subtitle>Split order across time</v-card-subtitle>
<v-card-text>
</v-card-text>
<pair-entry v-model="pair"/>
<v-card-actions class="d-flex justify-space-evenly mb-4">
<v-btn variant="outlined" color="red">Cancel</v-btn>
<v-btn variant="flat" color="green">Place Order</v-btn>
</v-card-actions>
</v-card>
<v-chip v-for="r in routes" variant="text">
{{ s.chain.name }}
<v-img src="https://upload.wikimedia.org/wikipedia/commons/e/e7/Uniswap_Logo.svg" width="1.5em"/>
<span class="uniswap-pink ml-0 mr-1">v3</span>
{{pairSymbol}} {{r.fee/10000}}%
</v-chip>
<div v-if="routeFinder.pending()">
<v-progress-circular indeterminate/> Searching for {{pairSymbol}} pools...
</div>
<v-alert v-if="routes.length===0 && !routeFinder.pending()" text="No pool found!" type=""/>
<div v-if="routes.length">
<v-text-field label='Amount' type="number" step="1" variant="outlined" aria-valuemin="0" min="0"
:model-value="amount" :rules="[validateRequired,validateAmount]">
<template v-slot:append-inner>
<v-btn @click="amountIsBase=!amountIsBase" variant="outlined" class="mr-2">
{{ amountIsBase ? pair.tokenA.symbol : pair.tokenB.symbol }}
</v-btn>
<v-btn :text="amountIsTotal ? 'total' : 'per tranche'" variant="outlined"
@click="amountIsTotal=!amountIsTotal" class="total"/>
</template>
</v-text-field>
<v-text-field label="Tranches" type="number" variant="outlined" aria-valuemin="1" min="1" max="255"
:model-value="tranches" :rules="[validateRequired,validateTranches]">
<!-- <template v-slot:prepend-inner>-->
<!-- <div>{{ amountIsTotal ? 'Split into' : 'Times' }}</div>-->
<!-- </template>-->
<template v-slot:append-inner>tranches</template>
</v-text-field>
<v-text-field type="number" variant="outlined" :min="1" v-model="interval" class="interval"
:label="intervalIsTotal ? 'Completion time' : 'Time between tranches'">
<!-- <template v-slot:append>APART</template>-->
<template v-slot:prepend-inner>
<v-btn variant="outlined" :text="intervalIsTotal ? 'Within' : 'Spaced apart'" class="within mr-2"
@click="intervalIsTotal=!intervalIsTotal"/>
</template>
<template v-slot:append-inner>
<v-btn variant="outlined" :text="timeUnits[timeUnitIndex]" @click="toggleTimeUnits" class="time-units"/>
</template>
</v-text-field>
<v-text-field v-model="limitPrice" :label="(limitIsMinimum?'Minimum':'Maximum')+' Price'" type="number"
variant="outlined" aria-valuemin="0" min="0"
clearable :rules="[validateAmount, validateMin]">
<template v-slot:append-inner>
<v-btn variant="outlined" @click="inverted=!inverted">
{{
inverted ? pair.tokenB.symbol + '/' + pair.tokenA.symbol : pair.tokenA.symbol + '/' + pair.tokenB.symbol
}}
</v-btn>
</template>
</v-text-field>
<!--
<v-text-field v-model="minPrice" label="Minimum Price" type="number" variant="outlined" aria-valuemin="0" min="0"
clearable :rules="[validateAmount, validateMin]">
<template v-slot:append-inner>
<v-btn variant="outlined" @click="inverted=!inverted">
{{ inverted ? pair.tokenB.symbol + '/' + pair.tokenA.symbol : pair.tokenA.symbol + '/' + pair.tokenB.symbol }}
</v-btn>
</template>
</v-text-field>
<v-text-field v-model="maxPrice" label="Maximum Price" type="number" variant="outlined" aria-valuemin="0" min="0"
clearable :rules="[validateAmount, validateMax]">
<template v-slot:append-inner>
<v-btn variant="outlined" @click="inverted=!inverted">
{{ inverted ? pair.tokenB.symbol + '/' + pair.tokenA.symbol : pair.tokenA.symbol + '/' + pair.tokenB.symbol }}
</v-btn>
</template>
</v-text-field>
-->
</div>
</v-card-text>
<v-card-actions class="d-flex justify-space-evenly mb-4">
<v-btn variant="outlined" color="red">Cancel</v-btn>
<v-btn variant="flat" color="green" :disabled="!routes.length">Place Order</v-btn>
</v-card-actions>
</PhoneCard>
</NeedsQueryHelper>
</template>
<script setup>
import {useStore} from "@/store/store";
import {computed, ref} from "vue";
import PairEntry from "@/components/PairEntry.vue";
import PhoneCard from "@/components/PhoneCard.vue";
import {queryHelperContract} from "@/blockchain/contract.js";
import {SingletonCoroutine} from "@/misc.js";
import NeedsQueryHelper from "@/components/NeedsQueryHelper.vue";
const s = useStore()
const pair = ref({tokenA: s.tokens[0], tokenB: s.tokens[1], buy: true})
// pair is computed to allow for dynamic computation of a default value based on current chainId
const _pair = ref({buy:true})
const pair = computed({
get() {
console.log('te getpair',_pair.value)
return _pair.value
},
set(value) {
console.log('set pair',value)
_pair.value = value
routes.value = []
routeFinder.invoke()
}
})
const pairSelected = computed(()=>_pair.value.tokenA && _pair.value.tokenB && routes.value)
const base = computed(()=>!pairSelected?{}:_pair.value.tokenA)
const quote = computed(()=>!pairSelected?{}:_pair.value.tokenB)
const pairSymbol = computed(()=>base.value?.symbol+'/'+quote.value?.symbol)
const routes = ref([])
const amount = ref(1)
const amountIsBase = ref(false)
const amountIsTotal = ref(true)
@@ -88,12 +132,46 @@ const intervalIsTotal = ref(true)
const timeUnits = ['minutes', 'hours', 'days']
const timeUnitIndex = ref(1)
const limitIsMinimum = computed(()=>!(pair.value.buy ^ inverted.value))
const limitIsMinimum = computed(() => !(pair.value.buy ^ inverted.value))
async function findRoute() {
if( !pair.value || !pair.value.tokenA || !pair.value.tokenB )
return null
const helper = await queryHelperContract()
console.log('helper',helper)
let rawRoutes
try {
rawRoutes = await helper.getRoutes(pair.value.tokenA.address, pair.value.tokenB.address)
}
catch (e) {
routes.value = []
console.log('routes exception', e)
return
}
// todo expose all available pools
let result = {} // we actually only find a single pool for now
for (let [exchange, fee, pool] of rawRoutes) {
exchange = Number(exchange)
fee = Number(fee)
if (result.fee === undefined || result.fee > fee) {
switch (exchange) {
case 0: // UniswapV2
break
case 1: // UniswapV3
result = {exchange: 'UniswapV3', pool, fee,}
break
}
}
}
routes.value = [result]
}
const routeFinder = new SingletonCoroutine(findRoute,50)
function toggleTimeUnits() {
timeUnitIndex.value++
if( timeUnitIndex.value >= timeUnits.length )
if (timeUnitIndex.value >= timeUnits.length)
timeUnitIndex.value = 0
}
@@ -104,14 +182,26 @@ function isEmpty(v) {
function validateRequired(v) {
if( isEmpty(v) )
if (isEmpty(v))
return 'Required'
return true
}
function validateTranches(v) {
const i = parseInt(v)
if (parseFloat(v) !== i)
return 'Whole numbers only'
if (i < 1)
return 'Must have at least one tranche'
if (i > 255)
return 'Maximum 255 tranches'
return true
}
function validateAmount(v) {
if( isEmpty(v) )
if (isEmpty(v))
return true
const floatRegex = /^-?\d*(?:[.,]\d*?)?$/
if (!floatRegex.test(v))
@@ -122,14 +212,14 @@ function validateAmount(v) {
}
function validateMax(v) {
if( !isEmpty(minPrice.value) && !isEmpty(v) && parseFloat(v) < parseFloat(minPrice.value) )
return 'Must be greater than the minimum price'
if (!isEmpty(minPrice.value) && !isEmpty(v) && parseFloat(v) < parseFloat(minPrice.value))
return 'Must be greater than the minimum price'
return true
}
function validateMin(v) {
if( !isEmpty(maxPrice.value) && !isEmpty(v) && parseFloat(v) > parseFloat(maxPrice.value) )
return 'Must be less than the maximum price'
if (!isEmpty(maxPrice.value) && !isEmpty(v) && parseFloat(v) > parseFloat(maxPrice.value))
return 'Must be less than the maximum price'
return true
}
@@ -139,10 +229,6 @@ function validateMin(v) {
<style scoped lang="scss">
@use "@/styles/vars" as *;
.order-card {
width: 25em;
}
.amount {
width: 23em;
}