diagonal line support
This commit is contained in:
@@ -4,31 +4,43 @@
|
||||
<div class="title">Line Point A</div>
|
||||
<v-divider/>
|
||||
<time-entry v-model="time1" class="mb-0" hide-details="true"/>
|
||||
<limit-price v-model="price1" label="Price A" :required="true"/>
|
||||
<limit-price v-model="price1" label="Price A" :required="true" :show-price="false"/>
|
||||
<div class="title">Line Point B</div>
|
||||
<v-divider/>
|
||||
<time-entry v-model="time2" hide-details="true"/>
|
||||
<limit-price v-model="price2" label="Price B" :required="true"/>
|
||||
<div><i>Backend support for diagonal lines is coming soon...</i></div>
|
||||
<limit-price v-model="price2" label="Price B" :required="true" :show-price="false"/>
|
||||
<div v-if="curLimit">
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<span>Current line value</span> <span class="text-green">{{curLimit ? curLimit.toPrecision(5) : curLimit}}</span>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<span>Current price</span> <route-price class="text-green"/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
{{os.limitIsMinimum}}
|
||||
</order>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useOrderStore} from "@/store/store";
|
||||
import {useOrderStore, useStore} from "@/store/store";
|
||||
import LimitPrice from "@/components/LimitPrice.vue";
|
||||
import Order from "@/components/Order.vue";
|
||||
import {computed, ref} from "vue";
|
||||
import {applyLimit, applyLinePoints} from "@/orderbuild.js";
|
||||
import {validateRequired, validateTranches} from "@/validate.js";
|
||||
import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
|
||||
import {applyLinePoints, linePointsValue} from "@/orderbuild.js";
|
||||
import {newTranche} from "@/blockchain/orderlib.js";
|
||||
import TimeEntry from "@/components/TimeEntry.vue";
|
||||
import RoutePrice from "@/components/RoutePrice.vue";
|
||||
|
||||
const s = useStore()
|
||||
const os = useOrderStore()
|
||||
|
||||
const time1 = ref(new Date())
|
||||
const price1 = ref(null)
|
||||
const time2 = ref(new Date())
|
||||
const price2 = ref(null)
|
||||
const curLimit = computed(()=>linePointsValue(time1.value, price1.value, time2.value, price2.value, s.time))
|
||||
|
||||
function buildTranches() {
|
||||
const t = newTranche();
|
||||
@@ -37,7 +49,7 @@ function buildTranches() {
|
||||
}
|
||||
|
||||
function validOrder() {
|
||||
return false
|
||||
return time1.value && (price1.value || price1.value===0) && time2.value && (price2.value || price2.value===0)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
</suspense>
|
||||
</template>
|
||||
<template v-slot:item.avg="{ item }">
|
||||
{{ pairPrice(item.order.tokenIn, item.order.tokenOut, vaultAddr, item.avg) }}
|
||||
<btn v-if="pairPrice(item.order.tokenIn, item.order.tokenOut, vaultAddr, item.avg)!==''" size="small"
|
||||
{{ pairPrice(item.order.tokenIn, item.order.tokenOut, item.avg) }}
|
||||
<btn v-if="pairPrice(item.order.tokenIn, item.order.tokenOut, item.avg)!==''" size="small"
|
||||
variant="plain"
|
||||
@click="inverted[[vaultAddr,item.index]] = !inverted[[vaultAddr,item.index]]">
|
||||
{{ pair(item.order.tokenIn, item.order.tokenOut, vaultAddr, item.index) }}
|
||||
@@ -117,7 +117,7 @@ const inverted = reactive({})
|
||||
|
||||
|
||||
// todo create a Price component that keeps inversion flags in the store and defaults to stablecoins as the quote
|
||||
function pairPrice(inTokenAddr, outTokenAddr, vaultAddr, price) {
|
||||
function pairPrice(inTokenAddr, outTokenAddr, price) {
|
||||
if( price === null )
|
||||
return ''
|
||||
const inToken = token(inTokenAddr)
|
||||
@@ -125,7 +125,7 @@ function pairPrice(inTokenAddr, outTokenAddr, vaultAddr, price) {
|
||||
if( !inToken || !outToken )
|
||||
return ''
|
||||
|
||||
const decimals = outToken.decimals-inToken.decimals
|
||||
const decimals = inToken.decimals-outToken.decimals
|
||||
if( decimals > 0 )
|
||||
price /= 10 ** decimals
|
||||
else
|
||||
@@ -268,11 +268,14 @@ function describeTrancheTime(st, isStart, t) {
|
||||
}
|
||||
|
||||
function describeTrancheLine(st, isMin, b, m) {
|
||||
if( b===0 && m===0 ) return ''
|
||||
// todo slopes
|
||||
console.log('tranche line', isMin, b, m)
|
||||
// todo make this a PairPrice
|
||||
return (isMin === st.order.amountIsInput ? 'dont-chase ' : 'limit ') + pairPrice(st.order.tokenIn, st.order.tokenOut, s.vault, b)
|
||||
if( b===0 && m===0 ) return ''
|
||||
// console.log('tranche line', isMin, b, m)
|
||||
if( m !== 0 ) {
|
||||
const limit = b + m * s.time
|
||||
return 'diagonal ' + pairPrice(st.order.tokenIn, st.order.tokenOut, limit)
|
||||
}
|
||||
return (isMin === st.order.amountIsInput ? 'dont-chase ' : 'limit ') + pairPrice(st.order.tokenIn, st.order.tokenOut, b)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
45
src/components/PairPrice.vue
Normal file
45
src/components/PairPrice.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<span>{{adjValue}}</span>
|
||||
<!-- todo optional pair label and inversion button -->
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {usePrefStore, useStore} from "@/store/store";
|
||||
import {computed} from "vue";
|
||||
import {token} from "@/blockchain/token.js";
|
||||
|
||||
const props = defineProps(['value','tokenA','tokenB','addrA','addrB',])
|
||||
|
||||
const s = useStore()
|
||||
const prefs = usePrefStore()
|
||||
|
||||
const adjValue = computed(()=>{
|
||||
const a = props.tokenA ? props.tokenA : token(s.chainId,props.addrA)
|
||||
const b = props.tokenB ? props.tokenB : token(s.chainId,props.addrB)
|
||||
if( !a || !b )
|
||||
return ''
|
||||
let price = props.value
|
||||
const decimals = b.decimals-a.decimals
|
||||
if( decimals > 0 )
|
||||
price /= 10 ** decimals
|
||||
else
|
||||
price *= 10 ** -decimals
|
||||
const token0 = a.address < b.address ? a.address : b.address
|
||||
const token1 = a.address > b.address ? a.address : b.address
|
||||
const invertedKey = [token0, token1];
|
||||
if( !(invertedKey in prefs.inverted) ) {
|
||||
// todo prefer stablecoins as the quote asset
|
||||
prefs.inverted[invertedKey] = false
|
||||
}
|
||||
if( prefs.inverted[invertedKey] )
|
||||
price = 1/price
|
||||
return price.toPrecision(5)
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "src/styles/vars" as *;
|
||||
|
||||
</style>
|
||||
@@ -3,24 +3,27 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store";
|
||||
import {useOrderStore, useStore} from "@/store/store";
|
||||
import {subPrices, unsubPrices, WIDE_PRICE_FORMAT} from "@/blockchain/prices.js";
|
||||
import {computed, onBeforeUnmount} from "vue";
|
||||
import {FixedNumber} from "ethers";
|
||||
import {routeInverted} from "@/misc.js";
|
||||
|
||||
const s = useStore()
|
||||
const os = useOrderStore()
|
||||
|
||||
const props = defineProps({
|
||||
route: {type: Object, required:true},
|
||||
inverted: {type: Boolean, required:true},
|
||||
route: {type: Object, required:false},
|
||||
inverted: {type: Boolean, required:false},
|
||||
precision: {type: Number, default:5, required:false},
|
||||
})
|
||||
|
||||
const route = computed(()=>props.route ? props.route : os.route)
|
||||
|
||||
const price = computed(()=>{
|
||||
const route = props.route;
|
||||
if( !route )
|
||||
if( !route.value )
|
||||
return ''
|
||||
const routeKey = [route.chainId, route.pool]
|
||||
const routeKey = [route.value.chainId, route.value.pool]
|
||||
if( !(routeKey in s.poolPrices) )
|
||||
return ''
|
||||
let p = s.poolPrices[routeKey]
|
||||
@@ -28,19 +31,20 @@ const price = computed(()=>{
|
||||
if( !p )
|
||||
return ''
|
||||
p = FixedNumber.fromString(p, WIDE_PRICE_FORMAT).toUnsafeFloat()
|
||||
if( props.inverted )
|
||||
const inverted = props.inverted === undefined ? routeInverted(route.value) : props.inverted;
|
||||
if( inverted )
|
||||
p = 1/p
|
||||
return p.toPrecision(props.precision)
|
||||
})
|
||||
|
||||
if( props.route )
|
||||
subPrices([props.route])
|
||||
if( route.value )
|
||||
subPrices([route.value])
|
||||
else
|
||||
console.log('route is empty: no price')
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if( props.route )
|
||||
unsubPrices([props.route])
|
||||
if( route.value )
|
||||
unsubPrices([route.value])
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,18 @@ const year = computed({
|
||||
set(v) { update(v, month.value, day.value, time.value)},
|
||||
})
|
||||
const time = computed({
|
||||
get() { return '' + (s.utc ? `${props.modelValue.getUTCHours()}:${props.modelValue.getUTCMinutes()}` : `${props.modelValue.getHours()}:${props.modelValue.getMinutes()}` ) },
|
||||
get() {
|
||||
let hour, min
|
||||
if( s.utc ) {
|
||||
hour = props.modelValue.getUTCHours()
|
||||
min = props.modelValue.getUTCMinutes()
|
||||
}
|
||||
else {
|
||||
hour = props.modelValue.getHours()
|
||||
min = props.modelValue.getMinutes()
|
||||
}
|
||||
return hour.toString().padStart(2,'0') + ':' + min.toString().padStart(2,'0')
|
||||
},
|
||||
set(v) { update(year.value, month.value, day.value, v) }
|
||||
})
|
||||
|
||||
@@ -45,7 +56,6 @@ const monthItems = monthsForLocale(undefined, 'short').map((v)=>{return {title:v
|
||||
|
||||
function buildDate(y, m, d, t) {
|
||||
const [hours,minutes] = t.split(':')
|
||||
console.log('buildDate', y, m, d, hours, minutes)
|
||||
return s.utc ? new Date(Date.UTC(y, m, d, hours, minutes))
|
||||
: new Date(y, m, d, hours, minutes);
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@ export function dateString(seconds) {
|
||||
}
|
||||
|
||||
|
||||
export function timestamp(date) {
|
||||
export function timestamp(date=null) {
|
||||
if(date===null)
|
||||
date = new Date()
|
||||
return Math.round(date.getTime() / 1000)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {useOrderStore} from "@/store/store.js";
|
||||
import {encodeIEE754} from "@/blockchain/common.js";
|
||||
|
||||
|
||||
export function applyLimit(tranche, price=null, isAbove=null) {
|
||||
export function applyLimit(tranche, price=null, isMinimum=null) {
|
||||
if( price === null ) {
|
||||
const os = useOrderStore()
|
||||
price = os.limitPrice
|
||||
@@ -12,38 +12,62 @@ export function applyLimit(tranche, price=null, isAbove=null) {
|
||||
return
|
||||
}
|
||||
|
||||
applyLine(tranche, price, 0, isAbove)
|
||||
applyLine(tranche, price, 0, isMinimum)
|
||||
}
|
||||
|
||||
|
||||
export function applyLinePoints(tranche, date0, price0, date1, price1, isAbove=null) {
|
||||
const os = useOrderStore()
|
||||
function computeInterceptSlope(date0, price0, date1, price1) {
|
||||
if (!date0 || !price0 && price0 !== 0 || !date1 || !price1 && price1 !== 0)
|
||||
return
|
||||
|
||||
throw Error(`invalid line points data ${date0} ${price0} ${date1} ${price1}`)
|
||||
const t0 = timestamp(date0);
|
||||
const t1 = timestamp(date1);
|
||||
if (t0 === t1)
|
||||
throw Error("line points' times must be different")
|
||||
const slope = (price1 - price0) / (t1 - t0)
|
||||
const intercept = price1 - slope * t1
|
||||
applyLine(tranche, intercept, slope, isAbove)
|
||||
return [intercept, slope]
|
||||
}
|
||||
|
||||
|
||||
export function applyLine(tranche, intercept, slope, isAbove=null) {
|
||||
export function linePointsValue(date0, price0, date1, price1, unixTime=null) {
|
||||
if(unixTime===null)
|
||||
unixTime = timestamp()
|
||||
try {
|
||||
const [intercept, slope] = computeInterceptSlope(date0, price0, date1, price1)
|
||||
return intercept + unixTime * slope
|
||||
}
|
||||
catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function applyLinePoints(tranche, date0, price0, date1, price1, isMinimum=null) {
|
||||
const [intercept, slope] = computeInterceptSlope(date0, price0, date1, price1);
|
||||
applyLine(tranche, intercept, slope, isMinimum)
|
||||
}
|
||||
|
||||
|
||||
export function applyLine(tranche, intercept, slope, isMinimum=null) {
|
||||
console.log('intercept, slope', intercept, slope)
|
||||
// intercept and slope are still in "human" units of decimal-adjusted prices
|
||||
const os = useOrderStore()
|
||||
const route = os.route
|
||||
const inverted = routeInverted(route)
|
||||
const scale = 10 ** (os.tokenA.decimals - os.tokenB.decimals)
|
||||
const m = encodeIEE754(inverted ? scale / slope : slope / scale)
|
||||
const b = encodeIEE754(inverted ? scale / intercept : intercept / scale)
|
||||
if( isAbove === null )
|
||||
isAbove = os.limitIsMinimum ^ inverted
|
||||
if( isAbove ) {
|
||||
let m = inverted ? -scale / slope : slope / scale
|
||||
let b = inverted ? scale / intercept : intercept / scale
|
||||
const cur = b + timestamp() * m
|
||||
console.log('inverted b, m, cur', inverted, b, m, cur)
|
||||
m = encodeIEE754(m)
|
||||
b = encodeIEE754(b)
|
||||
if( isMinimum === null )
|
||||
isMinimum = os.limitIsMinimum
|
||||
console.log('limit is minimum', isMinimum)
|
||||
if (isMinimum) {
|
||||
tranche.minIntercept = b;
|
||||
tranche.minSlope = m;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
tranche.maxIntercept = b;
|
||||
tranche.maxSlope = m;
|
||||
}
|
||||
|
||||
@@ -39,10 +39,8 @@ socket.on('vb', async (chainId, vault, balances) => {
|
||||
if( s.chainId.value !== chainId )
|
||||
return
|
||||
console.log('vb', vault, balances)
|
||||
const vb = {}
|
||||
vb[vault] = JSON.parse(balances)
|
||||
s.$patch({vaultBalances:vb})
|
||||
console.log('vault balances', vault, vb)
|
||||
s.vaultBalances[vault] = JSON.parse(balances)
|
||||
console.log('vault balances', vault, s.vaultBalances[vault])
|
||||
})
|
||||
|
||||
socket.on('vaults', (chainId, owner, vaults)=>{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import {knownTokens} from "@/knownTokens.js";
|
||||
import {computed, ref} from "vue";
|
||||
import {timestamp} from "@/misc.js";
|
||||
|
||||
|
||||
// USING THE STORE:
|
||||
@@ -29,6 +30,14 @@ import {computed, ref} from "vue";
|
||||
|
||||
|
||||
export const useStore = defineStore('app', ()=> {
|
||||
const time = ref(timestamp())
|
||||
console.log('starting clock')
|
||||
setInterval(()=>{
|
||||
const now = timestamp();
|
||||
// console.log('clock', now)
|
||||
time.value= now
|
||||
}, 10*1000)
|
||||
|
||||
const nav = ref(false) // controls opening navigation drawer
|
||||
|
||||
const _chainId = ref(null)
|
||||
@@ -111,7 +120,7 @@ export const useStore = defineStore('app', ()=> {
|
||||
return {
|
||||
nav, chainId, chainInfo, chain, provider, vaultInitCodeHash, account, vaults, transactionSenders, errors,
|
||||
extraTokens, poolPrices, vaultBalances, orders, vault, tokens, factory, helper, mockenv, mockCoins,
|
||||
removeTransactionSender, error, closeError, addToken,
|
||||
removeTransactionSender, error, closeError, addToken, time,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -168,3 +177,12 @@ export const useOrderStore = defineStore('order', ()=> {
|
||||
limitIsMinimum, amountToken, amountIsInput, setDefaultTokens, totalAmount, trancheAmount, utc,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
export const usePrefStore = defineStore('order', ()=> {
|
||||
// user preferences
|
||||
const inverted = ref({})
|
||||
|
||||
return {inverted,}
|
||||
})
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<!-- todo needs account -->
|
||||
<needs-signer>
|
||||
<vault :owner="s.account" :num="0"/>
|
||||
<new-order class="my-6"/>
|
||||
<faucet class="mt-3"/>
|
||||
</needs-signer>
|
||||
</template>
|
||||
@@ -12,6 +13,7 @@ import Vault from "@/components/Vault.vue";
|
||||
import NeedsSigner from "@/components/NeedsSigner.vue";
|
||||
import Faucet from "@/components/Faucet.vue";
|
||||
import NeedsProvider from "@/components/NeedsProvider.vue";
|
||||
import NewOrder from "@/components/NewOrder.vue";
|
||||
|
||||
const s = useStore()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user