major refactor of web store into vue setup style declaration; reactivity debugging; order view has known refresh issues
This commit is contained in:
@@ -1,21 +1,23 @@
|
||||
<template>
|
||||
<v-text-field label='Amount' type="number" step="1" variant="outlined" aria-valuemin="0" min="0"
|
||||
v-model="s.amount" :rules="[validateRequired,validateAmount]" v-auto-select>
|
||||
v-model="os.amount" :rules="[validateRequired,validateAmount]" v-auto-select>
|
||||
<template v-slot:append-inner>
|
||||
<v-btn @click="s.amountIsTokenA=!s.amountIsTokenA" variant="outlined" class="mr-2">
|
||||
{{ s.amountIsTokenA ? s.tokenA.symbol : s.tokenB.symbol }}
|
||||
<v-btn @click="os.amountIsTokenA=!os.amountIsTokenA" variant="outlined" class="mr-2">
|
||||
{{ os.amountIsTokenA ? os.tokenA.symbol : os.tokenB.symbol }}
|
||||
</v-btn>
|
||||
<v-btn :text="s.amountIsTotal ? 'total' : 'per tranche'" variant="outlined"
|
||||
@click="s.amountIsTotal=!s.amountIsTotal" class="total"/>
|
||||
<v-btn :text="os.amountIsTotal ? 'total' : 'per tranche'" variant="outlined"
|
||||
@click="os.amountIsTotal=!os.amountIsTotal" class="total"/>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store";
|
||||
import {validateRequired, validateAmount, vAutoSelect} from "@/misc.js";
|
||||
import {useOrderStore} from "@/store/store";
|
||||
// noinspection ES6UnusedImports
|
||||
import {vAutoSelect} from "@/misc.js";
|
||||
import {validateAmount, validateRequired} from "@/validate.js";
|
||||
|
||||
const s = useStore()
|
||||
const os = useOrderStore()
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@@ -49,7 +49,7 @@ function gib() {
|
||||
const s = useStore()
|
||||
if( s.account ) {
|
||||
disabled.value = true
|
||||
socket.emit('faucet', s.chainId, s.account)
|
||||
socket.emit('faucet', s.chainId.value, s.account)
|
||||
setTimeout(()=>disabled.value=false, 60*1000)
|
||||
}
|
||||
}
|
||||
|
||||
25
src/components/LadderOrder.vue
Normal file
25
src/components/LadderOrder.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<order :tranches="buildTranches">
|
||||
<limit-price :required="true" label=""/>
|
||||
<limit-price :show-price="false" store-var="limitPrice2" :required="true" label=""/>
|
||||
</order>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store";
|
||||
import LimitPrice from "@/components/LimitPrice.vue";
|
||||
import Order from "@/components/Order.vue";
|
||||
|
||||
const s = useStore()
|
||||
|
||||
function buildTranches() {
|
||||
const ts = []
|
||||
return ts
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "src/styles/vars" as *;
|
||||
|
||||
</style>
|
||||
38
src/components/LimitPrice.vue
Normal file
38
src/components/LimitPrice.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<v-text-field v-model="os[storeVar]" :label="getLabel" type="number"
|
||||
variant="outlined" aria-valuemin="0" min="0"
|
||||
clearable :rules="rules" v-auto-select>
|
||||
<template v-slot:append-inner>
|
||||
<v-btn variant="outlined" @click="os.inverted=!os.inverted">
|
||||
{{ os.pairSymbol }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<template #details style="flex-direction: column-reverse">
|
||||
<div>
|
||||
Current price <route-price :inverted="routeInverted(os.route)" :route="os.route" class="text-green"/>
|
||||
</div>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useOrderStore, useStore} from "@/store/store";
|
||||
import {routeInverted} from "@/misc.js";
|
||||
import RoutePrice from "@/components/RoutePrice.vue";
|
||||
import {validateAmount, validateRequired} from "@/validate.js";
|
||||
import {computed} from "vue";
|
||||
// noinspection ES6UnusedImports
|
||||
import {vAutoSelect} from "@/misc.js";
|
||||
|
||||
const os = useOrderStore()
|
||||
const props = defineProps({
|
||||
required: {default: false},
|
||||
label: {default: null},
|
||||
storeVar: {default: 'limitPrice'},
|
||||
showPrice: {default: true},
|
||||
})
|
||||
const rules = computed(()=>props.required ? [validateAmount, validateRequired] : [validateAmount])
|
||||
|
||||
const getLabel = computed(()=>props.label !== null ? props.label : (os.limitIsMinimum?'Minimum':'Maximum') +' Price')
|
||||
|
||||
</script>
|
||||
63
src/components/Order.vue
Normal file
63
src/components/Order.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<needs-provider>
|
||||
<phone-card class="tordercard">
|
||||
<v-card-title class="big">{{title}}</v-card-title>
|
||||
<v-card-subtitle>{{subtitle}}</v-card-subtitle>
|
||||
<v-card-item>
|
||||
<pair-choice/>
|
||||
<div v-if="os.route && !os.routesPending">
|
||||
<amount/>
|
||||
<slot/>
|
||||
</div>
|
||||
</v-card-item>
|
||||
<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="!valid" @click="placeOrder">Place Dexorder</v-btn>
|
||||
</v-card-actions>
|
||||
</phone-card>
|
||||
</needs-provider>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useOrderStore, useStore} from "@/store/store";
|
||||
import {computed, ref} from "vue";
|
||||
import PhoneCard from "@/components/PhoneCard.vue";
|
||||
import Amount from "@/components/Amount.vue"
|
||||
// noinspection ES6UnusedImports
|
||||
import {routeInverted, SingletonCoroutine, vAutoSelect} from "@/misc.js";
|
||||
import {newLimitConstraint, newOrder, newTimeConstraint, TimeMode} from "@/blockchain/orderlib.js";
|
||||
import {FixedNumber} from "ethers";
|
||||
import {pendOrder} from "@/blockchain/wallet.js";
|
||||
import NeedsProvider from "@/components/NeedsProvider.vue";
|
||||
import RoutePrice from "@/components/RoutePrice.vue";
|
||||
import router from "@/router/index.js";
|
||||
import PairChoice from "@/components/PairChoice.vue";
|
||||
import {isEmpty, validateAmount, validateRequired} from "@/validate.js";
|
||||
|
||||
const s = useStore()
|
||||
const os = useOrderStore()
|
||||
const props = defineProps(['title','subtitle','valid','tranches'])
|
||||
|
||||
function placeOrder() {
|
||||
const ta = os.tokenA;
|
||||
const tb = os.tokenB;
|
||||
const tokenIn = os.buy ? tb.address : ta.address
|
||||
const tokenOut = os.buy ? ta.address : tb.address
|
||||
const route = os.route
|
||||
const amt = FixedNumber.fromString(os.amount.toString(), {decimals: os.amountToken.decimals}).value
|
||||
const ts = props.tranches()
|
||||
const order = newOrder(tokenIn, tokenOut, route.exchange, route.fee, amt, os.amountIsInput, ts)
|
||||
pendOrder(order)
|
||||
router.push('/orders')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/vars" as *;
|
||||
|
||||
.tordercard {
|
||||
max-width: $card-maxw;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -70,8 +70,8 @@ function tokenAmount(tokenAddr, amount) {
|
||||
const t = token(tokenAddr)
|
||||
if( !t )
|
||||
return ''
|
||||
console.log('tokenAmount amount', typeof amount, amount)
|
||||
console.log('tokenAmount decimals', typeof t.decimals, t.decimals)
|
||||
// console.log('tokenAmount amount', typeof amount, amount)
|
||||
// console.log('tokenAmount decimals', typeof t.decimals, t.decimals)
|
||||
const amt = FixedNumber.fromValue(amount, t.decimals, {width:256, decimals:t.decimals, signed:false})
|
||||
return `${amt} ${t.symbol}`
|
||||
}
|
||||
@@ -111,7 +111,6 @@ function pair(inTokenAddr, outTokenAddr, vaultAddr, index) {
|
||||
|
||||
const orders = computed(()=>{
|
||||
const result = []
|
||||
console.log('computing orders')
|
||||
// for( const [status] of pendingOrders.reverse() ) {
|
||||
// console.log('adding pended order')
|
||||
// const inTokenAddr = status[0]
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<token-choice v-model="tokenA" class="token-choice mb-1">
|
||||
<template v-slot:prepend>
|
||||
<v-btn :text="s.buy ? 'Buy' : 'Sell'" :color="s.buy ? 'green' : 'red'"
|
||||
variant="outlined" @click="s.buy=!s.buy" class="bs-button"/>
|
||||
<v-btn :text="os.buy ? 'Buy' : 'Sell'" :color="os.buy ? 'green' : 'red'"
|
||||
variant="outlined" @click="os.buy=!os.buy" class="bs-button"/>
|
||||
</template>
|
||||
</token-choice>
|
||||
<token-choice v-model="tokenB" class="token-choice">
|
||||
<template v-slot:prepend>
|
||||
<v-btn :text="!s.buy ? 'Buy' : 'Sell'" :color="!s.buy ? 'green' : 'red'"
|
||||
variant="outlined" @click="s.buy=!s.buy" class="bs-button"/>
|
||||
<v-btn :text="!os.buy ? 'Buy' : 'Sell'" :color="!os.buy ? 'green' : 'red'"
|
||||
variant="outlined" @click="os.buy=!os.buy" class="bs-button"/>
|
||||
</template>
|
||||
</token-choice>
|
||||
|
||||
@@ -16,46 +16,48 @@
|
||||
{{ s.chain.name }}
|
||||
<v-img src="https://upload.wikimedia.org/wikipedia/commons/e/e7/Uniswap_Logo.svg" width="1.5em"/>
|
||||
<span class="uniswap-color ml-0 mr-1">v3</span>
|
||||
<span>{{ s.pairSymbol }} {{ r.fee / 10000 }}%</span>
|
||||
<route-price :route="r" :inverted="routeInverted(r)" class="text-green clickable" @click="s.inverted=!s.inverted"/>
|
||||
<span>{{ os.pairSymbol }} {{ r.fee / 10000 }}%</span>
|
||||
<route-price :route="r" :inverted="routeInverted(r)" class="text-green clickable" @click="os.inverted=!os.inverted"/>
|
||||
</v-chip>
|
||||
|
||||
<div v-if="s.routesPending">
|
||||
<v-progress-circular indeterminate/> Searching for {{s.pairSymbol}} pools...
|
||||
<div v-if="os.routesPending">
|
||||
<v-progress-circular indeterminate/>
|
||||
Searching for {{ os.pairSymbol }} pools...
|
||||
</div>
|
||||
|
||||
<v-alert v-if="!s.route && !s.routesPending" text="No pool found!"/>
|
||||
<v-alert v-if="!os.route && !os.routesPending" text="No pool found!"/>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import TokenChoice from "@/components/TokenChoice.vue"
|
||||
import {useStore} from "@/store/store";
|
||||
import {useOrderStore, useStore} from "@/store/store";
|
||||
import RoutePrice from "@/components/RoutePrice.vue";
|
||||
import {findRoute} from "@/blockchain/route.js";
|
||||
import {SingletonCoroutine, routeInverted} from "@/misc.js";
|
||||
import {computed, ref} from "vue";
|
||||
|
||||
const s = useStore()
|
||||
const os = useOrderStore()
|
||||
|
||||
const tokenA = computed({
|
||||
get() {
|
||||
return s.tokenA
|
||||
return os.tokenA
|
||||
},
|
||||
set(value) {
|
||||
if( !s.tokenA || s.tokenA.address !== value.address ) {
|
||||
s.tokenA = value
|
||||
if( !os.tokenA || os.tokenA.address !== value.address ) {
|
||||
os.tokenA = value
|
||||
routeFinder.invoke()
|
||||
}
|
||||
}
|
||||
})
|
||||
const tokenB = computed({
|
||||
get() {
|
||||
return s.tokenB
|
||||
return os.tokenB
|
||||
},
|
||||
set(value) {
|
||||
if( !s.tokenB || s.tokenB.address !== value.address ) {
|
||||
s.tokenB = value
|
||||
if( !os.tokenB || os.tokenB.address !== value.address ) {
|
||||
os.tokenB = value
|
||||
routeFinder.invoke()
|
||||
}
|
||||
}
|
||||
@@ -64,32 +66,32 @@ const tokenB = computed({
|
||||
|
||||
const routes = computed({
|
||||
get() {
|
||||
return s.routes
|
||||
return os.routes
|
||||
},
|
||||
set(value) {
|
||||
console.log('setting new routes', s.routes, value)
|
||||
s.routes = value
|
||||
console.log('setting new routes', os.routes, value)
|
||||
os.routes = value
|
||||
}
|
||||
})
|
||||
|
||||
async function componentFindRoute() {
|
||||
const tokenA = s.tokenA
|
||||
const tokenB = s.tokenB
|
||||
const tokenA = os.tokenA
|
||||
const tokenB = os.tokenB
|
||||
// console.log('finding route', tokenA, tokenB)
|
||||
s.routes = []
|
||||
os.routes = []
|
||||
if (!tokenA || !tokenB)
|
||||
return
|
||||
s.routesPending = true
|
||||
os.routesPending = true
|
||||
try {
|
||||
const result = await findRoute(tokenA, tokenB)
|
||||
// console.log('found route', result)
|
||||
s.routes = result
|
||||
const result = await findRoute(s.chainId.value, tokenA, tokenB)
|
||||
console.log('found route', result)
|
||||
os.routes = result
|
||||
}
|
||||
catch (e) {
|
||||
console.log('ignoring routes exception', e)
|
||||
}
|
||||
finally {
|
||||
s.routesPending = false
|
||||
os.routesPending = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,12 @@ const props = defineProps({
|
||||
|
||||
const price = computed(()=>{
|
||||
const route = props.route;
|
||||
if( !route || !(route.pool in s.poolPrices) )
|
||||
if( !route )
|
||||
return ''
|
||||
let p = s.poolPrices[route.pool]
|
||||
const routeKey = [route.chainId, route.pool]
|
||||
if( !(routeKey in s.poolPrices) )
|
||||
return ''
|
||||
let p = s.poolPrices[routeKey]
|
||||
// console.log('pool price is',typeof p, p)
|
||||
if( !p )
|
||||
return ''
|
||||
|
||||
63
src/components/TimedOrder.vue
Normal file
63
src/components/TimedOrder.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<order title="DCA / TWAP" subtitle="Multiple tranches over a time range" :tranches="buildTranches" :valid="validOrder">
|
||||
<Tranches/>
|
||||
<LimitPrice/>
|
||||
</order>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// noinspection ES6UnusedImports
|
||||
import {routeInverted, SingletonCoroutine, vAutoSelect} from "@/misc.js";
|
||||
import Order from "@/components/Order.vue";
|
||||
import LimitPrice from "@/components/LimitPrice.vue";
|
||||
import {timesliceTranches, limitConstraint} from "@/orderbuild.js";
|
||||
import Tranches from "@/components/Tranches.vue";
|
||||
import {useOrderStore} from "@/store/store.js";
|
||||
|
||||
const os = useOrderStore()
|
||||
|
||||
function buildTranches() {
|
||||
const ts = timesliceTranches();
|
||||
const priceConstraint = limitConstraint();
|
||||
if( priceConstraint !== null )
|
||||
for( let i=0; i<ts.length; i++)
|
||||
ts[i][1].push(priceConstraint)
|
||||
return ts
|
||||
}
|
||||
|
||||
function validOrder() {
|
||||
return os.validOrder
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/vars" as *;
|
||||
|
||||
.tordercard {
|
||||
max-width: $card-maxw;
|
||||
}
|
||||
.token-choice {
|
||||
width: 16em;
|
||||
}
|
||||
.bs-button {
|
||||
width: 6em;
|
||||
}
|
||||
.amount {
|
||||
width: 23em;
|
||||
}
|
||||
|
||||
.total {
|
||||
width: 9em;
|
||||
}
|
||||
|
||||
.split-into {
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
.v-input {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,196 +0,0 @@
|
||||
<template>
|
||||
<needs-provider>
|
||||
<phone-card class="toecard">
|
||||
<v-card-title class="big">DCA / TWAP</v-card-title>
|
||||
<v-card-subtitle>Multiple tranches over a time range</v-card-subtitle>
|
||||
<v-card-item>
|
||||
<pair-choice/>
|
||||
<div v-if="s.route && !s.routesPending">
|
||||
<amount/>
|
||||
<v-text-field label="Tranches" type="number" variant="outlined" aria-valuemin="1" min="1" max="255"
|
||||
v-model="tranches" :rules="[validateRequired,validateTranches]" v-auto-select>
|
||||
<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'" v-auto-select>
|
||||
<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="s.limitPrice" :label="(s.limitIsMinimum?'Minimum':'Maximum')+' Price'" type="number"
|
||||
variant="outlined" aria-valuemin="0" min="0"
|
||||
clearable :rules="[validateAmount, validateMin]" v-auto-select>
|
||||
<template v-slot:append-inner>
|
||||
<v-btn variant="outlined" @click="s.inverted=!s.inverted">
|
||||
{{s.pairSymbol}}
|
||||
</v-btn>
|
||||
</template>
|
||||
<template #details style="flex-direction: column-reverse">
|
||||
<div>
|
||||
Current price <route-price :inverted="routeInverted(s.route)" :route="s.route" class="text-green"/>
|
||||
</div>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</div>
|
||||
|
||||
</v-card-item>
|
||||
|
||||
<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="!validOrder" @click="placeOrder">Place Dexorder</v-btn>
|
||||
</v-card-actions>
|
||||
</phone-card>
|
||||
</needs-provider>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store";
|
||||
import {computed, ref} from "vue";
|
||||
import PhoneCard from "@/components/PhoneCard.vue";
|
||||
import Amount from "@/components/Amount.vue"
|
||||
// noinspection ES6UnusedImports
|
||||
import {isEmpty, routeInverted, SingletonCoroutine, vAutoSelect, validateRequired, validateAmount} from "@/misc.js";
|
||||
import {newLimitConstraint, newOrder, newTimeConstraint, TimeMode} from "@/blockchain/orderlib.js";
|
||||
import {FixedNumber} from "ethers";
|
||||
import {pendOrder} from "@/blockchain/wallet.js";
|
||||
import NeedsProvider from "@/components/NeedsProvider.vue";
|
||||
import RoutePrice from "@/components/RoutePrice.vue";
|
||||
import router from "@/router/index.js";
|
||||
import PairChoice from "@/components/PairChoice.vue";
|
||||
|
||||
const s = useStore()
|
||||
|
||||
const tranches = ref(3)
|
||||
const minPrice = ref(null)
|
||||
const maxPrice = ref(null)
|
||||
const interval = ref(1)
|
||||
const intervalIsTotal = ref(true)
|
||||
const timeUnits = ['minutes', 'hours', 'days']
|
||||
const timeUnitIndex = ref(0)
|
||||
const validOrder = computed(()=>s.validOrder)
|
||||
|
||||
function toggleTimeUnits() {
|
||||
timeUnitIndex.value++
|
||||
if (timeUnitIndex.value >= timeUnits.length)
|
||||
timeUnitIndex.value = 0
|
||||
}
|
||||
|
||||
|
||||
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 validateMax(v) {
|
||||
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'
|
||||
return true
|
||||
}
|
||||
|
||||
function placeOrder() {
|
||||
const ta = s.tokenA;
|
||||
const tb = s.tokenB;
|
||||
const tokenIn = s.buy ? tb.address : ta.address
|
||||
const tokenOut = s.buy ? ta.address : tb.address
|
||||
const route = s.route
|
||||
const amt = FixedNumber.fromString(s.amount.toString(), {decimals: s.amountToken.decimals}).value
|
||||
|
||||
// build tranches
|
||||
const n = tranches.value // num tranches
|
||||
const ts = []
|
||||
let duration = timeUnitIndex.value === 0 ? interval.value * 60 : // minutes
|
||||
timeUnitIndex.value === 1 ? interval.value * 60 * 60 : // hours
|
||||
interval.value * 24 * 60 * 60; // days
|
||||
let window
|
||||
if (!intervalIsTotal.value) {
|
||||
window = duration
|
||||
duration *= n // duration is the total time for all tranches
|
||||
} else {
|
||||
window = Math.round(duration / n)
|
||||
}
|
||||
const oneHundredPercent = 65535n // by contract definition of uint16 fraction
|
||||
const ceil = oneHundredPercent % BigInt(n) ? 1n : 0n
|
||||
const amtPerTranche = oneHundredPercent / BigInt(n) + ceil
|
||||
duration -= 15 // subtract 15 seconds so the last tranche completes before the deadline
|
||||
let priceConstraint = null
|
||||
if( s.limitPrice ) {
|
||||
const inverted = routeInverted(route)
|
||||
const isAbove = s.limitIsMinimum ^ inverted
|
||||
const isRatio = false // todo ratios
|
||||
const decimals = 10 ** (s.tokenA.decimals - s.tokenB.decimals)
|
||||
const limit = inverted ? decimals/s.limitPrice : s.limitPrice/decimals
|
||||
priceConstraint = !s.limitPrice ? null : newLimitConstraint(isAbove, isRatio, limit)
|
||||
}
|
||||
for (let i = 0; i < n; i++) {
|
||||
const start = Math.floor(i * (duration / Math.max((n - 1), 1)))
|
||||
const end = start + window
|
||||
const cs = [newTimeConstraint(TimeMode.SinceOrderStart, start, TimeMode.SinceOrderStart, end)]
|
||||
if( priceConstraint !== null )
|
||||
cs.push(priceConstraint)
|
||||
ts.push([amtPerTranche, cs])
|
||||
}
|
||||
const order = newOrder(tokenIn, tokenOut, route.exchange, route.fee, amt, amountIsInput, ts)
|
||||
pendOrder(order)
|
||||
router.push('/orders')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/vars" as *;
|
||||
|
||||
.toecard {
|
||||
max-width: $card-maxw;
|
||||
}
|
||||
.token-choice {
|
||||
width: 16em;
|
||||
}
|
||||
.bs-button {
|
||||
width: 6em;
|
||||
}
|
||||
.amount {
|
||||
width: 23em;
|
||||
}
|
||||
|
||||
.total {
|
||||
width: 9em;
|
||||
}
|
||||
|
||||
.split-into {
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
.v-input {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.interval {
|
||||
//width: 18em;
|
||||
}
|
||||
|
||||
.within {
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
.time-units {
|
||||
width: 8em;
|
||||
}
|
||||
</style>
|
||||
51
src/components/Tranches.vue
Normal file
51
src/components/Tranches.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<v-text-field label="Tranches" type="number" variant="outlined" aria-valuemin="1" min="1" max="255"
|
||||
v-model="os.tranches" :rules="[validateRequired,validateTranches]" v-auto-select>
|
||||
<template v-slot:append-inner>tranches</template>
|
||||
</v-text-field>
|
||||
<v-text-field type="number" variant="outlined" :min="1" v-model="os.interval" class="interval"
|
||||
:label="os.intervalIsTotal ? 'Completion time' : 'Time between tranches'" v-auto-select>
|
||||
<template v-slot:prepend-inner>
|
||||
<v-btn variant="outlined" :text="os.intervalIsTotal ? 'Within' : 'Spaced apart'" class="within mr-2"
|
||||
@click="os.intervalIsTotal=!os.intervalIsTotal"/>
|
||||
</template>
|
||||
<template v-slot:append-inner>
|
||||
<v-btn variant="outlined" :text="timeUnitsStr" @click="toggleTimeUnits" class="time-units"/>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useOrderStore, useStore} from "@/store/store.js";
|
||||
import {validateRequired, validateTranches} from "@/validate.js";
|
||||
// noinspection ES6UnusedImports
|
||||
import {vAutoSelect} from "@/misc.js";
|
||||
import {computed} from "vue";
|
||||
|
||||
const os = useOrderStore()
|
||||
|
||||
const timeUnits = ['minutes', 'hours', 'days']
|
||||
const timeUnitsStr = computed(()=>timeUnits[os.timeUnitIndex])
|
||||
|
||||
function toggleTimeUnits() {
|
||||
os.timeUnitIndex++
|
||||
if (os.timeUnitIndex >= timeUnits.length)
|
||||
os.timeUnitIndex = 0
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/styles/vars" as *;
|
||||
.interval {
|
||||
//width: 18em;
|
||||
}
|
||||
|
||||
.within {
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
.time-units {
|
||||
width: 8em;
|
||||
}
|
||||
</style>
|
||||
@@ -110,9 +110,19 @@ function onWithdraw(addr) {
|
||||
withdrawShow.value = true
|
||||
}
|
||||
|
||||
|
||||
function checkVault() {
|
||||
console.log('checkVault', props.num, s.account, s.vault)
|
||||
if(props.num===0 && s.account && !s.vault)
|
||||
ensureVault()
|
||||
}
|
||||
|
||||
// todo remove automatic vault creation for Alpha 2
|
||||
if(props.num===0 && !s.vault)
|
||||
ensureVault()
|
||||
s.$subscribe((mutation, state)=>{
|
||||
console.log('test')
|
||||
checkVault()
|
||||
})
|
||||
checkVault()
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user