complete store refactor; moved form inputs into store; refactored components out of TimedOrderEntry
This commit is contained in:
@@ -4,7 +4,6 @@ import {useStore} from "@/store/store.js";
|
|||||||
|
|
||||||
|
|
||||||
export function vaultAddress( owner, num=0) {
|
export function vaultAddress( owner, num=0) {
|
||||||
console.log('va', owner, num)
|
|
||||||
if( !owner )
|
if( !owner )
|
||||||
return null
|
return null
|
||||||
const s = useStore()
|
const s = useStore()
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ export function subPrices( routes ) {
|
|||||||
const subRoutes = []
|
const subRoutes = []
|
||||||
let chainId = null
|
let chainId = null
|
||||||
for( const route of routes ) {
|
for( const route of routes ) {
|
||||||
console.log('sub route', route, subscriptionCounts)
|
// console.log('sub route', route, subscriptionCounts)
|
||||||
if( !(route in subscriptionCounts) || subscriptionCounts[route] === 0 ) {
|
if( !(route in subscriptionCounts) || subscriptionCounts[route] === 0 ) {
|
||||||
subscriptionCounts[route] = 1
|
subscriptionCounts[route] = 1
|
||||||
console.log('subscribing to pool', route.pool)
|
// console.log('subscribing to pool', route.pool)
|
||||||
subRoutes.push(route)
|
subRoutes.push(route)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -33,9 +33,7 @@ export function subPrices( routes ) {
|
|||||||
// perform a local query if necessary
|
// perform a local query if necessary
|
||||||
for( const route of subRoutes ) {
|
for( const route of subRoutes ) {
|
||||||
const s = useStore()
|
const s = useStore()
|
||||||
console.log('route in prices?', route.pool in s.poolPrices, route.pool, s.poolPrices)
|
|
||||||
if( !(route.pool in s.poolPrices) ) {
|
if( !(route.pool in s.poolPrices) ) {
|
||||||
console.log('querying initial route price', route.pool)
|
|
||||||
getPriceForRoute(route).then((price)=>s.poolPrices[route.pool]=price)
|
getPriceForRoute(route).then((price)=>s.poolPrices[route.pool]=price)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,7 +44,7 @@ export function unsubPrices( routes ) {
|
|||||||
let chainId = null
|
let chainId = null
|
||||||
const unsubAddrs = []
|
const unsubAddrs = []
|
||||||
for( const route of routes ) {
|
for( const route of routes ) {
|
||||||
console.log('unsub route', route, subscriptionCounts)
|
// console.log('unsub route', route, subscriptionCounts)
|
||||||
if( !(route in subscriptionCounts) ) {
|
if( !(route in subscriptionCounts) ) {
|
||||||
console.error('unsubscribed to a nonexistent route', route)
|
console.error('unsubscribed to a nonexistent route', route)
|
||||||
}
|
}
|
||||||
@@ -56,7 +54,7 @@ export function unsubPrices( routes ) {
|
|||||||
unsubAddrs.push(route.pool)
|
unsubAddrs.push(route.pool)
|
||||||
if( chainId !== null && route.chainId !== chainId )
|
if( chainId !== null && route.chainId !== chainId )
|
||||||
throw Error('cannot mix chainIds in a subscription list')
|
throw Error('cannot mix chainIds in a subscription list')
|
||||||
console.log('unsubscribing from pool', route.pool)
|
// console.log('unsubscribing from pool', route.pool)
|
||||||
chainId = route.chainId
|
chainId = route.chainId
|
||||||
}
|
}
|
||||||
else if( subscriptionCounts[route] < 0 ) {
|
else if( subscriptionCounts[route] < 0 ) {
|
||||||
@@ -71,7 +69,6 @@ export function unsubPrices( routes ) {
|
|||||||
|
|
||||||
|
|
||||||
async function getPriceForRoute(route) {
|
async function getPriceForRoute(route) {
|
||||||
console.log('route is',route)
|
|
||||||
if( route.exchange === Exchange.UniswapV3 ) {
|
if( route.exchange === Exchange.UniswapV3 ) {
|
||||||
const addr = uniswapV3PoolAddress(route.chainId, route.token0.address, route.token1.address, route.fee)
|
const addr = uniswapV3PoolAddress(route.chainId, route.token0.address, route.token1.address, route.fee)
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
@@ -88,7 +85,7 @@ async function getPriceForRoute(route) {
|
|||||||
price = FixedNumber.fromValue(price,0,WIDE_PRICE_FORMAT)
|
price = FixedNumber.fromValue(price,0,WIDE_PRICE_FORMAT)
|
||||||
price = price.div(FixedNumber.fromValue(2n**(96n*2n),0,WIDE_PRICE_FORMAT))
|
price = price.div(FixedNumber.fromValue(2n**(96n*2n),0,WIDE_PRICE_FORMAT))
|
||||||
price = price.round(18).toString()
|
price = price.round(18).toString()
|
||||||
console.log(`price for ${route.token0.symbol}/${route.token1.symbol}`,price)
|
// console.log(`price for ${route.token0.symbol}/${route.token1.symbol}`,price)
|
||||||
store.poolPrices[addr] = price
|
store.poolPrices[addr] = price
|
||||||
return price
|
return price
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export async function findRoute(tokenA, tokenB) {
|
|||||||
const chainId = useStore().chainId
|
const chainId = useStore().chainId
|
||||||
const rawRoutes = await helper.getRoutes(tokenA.address, tokenB.address)
|
const rawRoutes = await helper.getRoutes(tokenA.address, tokenB.address)
|
||||||
// todo expose all available pools
|
// todo expose all available pools
|
||||||
console.log('raw routes', rawRoutes)
|
// console.log('raw routes', rawRoutes)
|
||||||
let result = null // we actually only find a single pool for now
|
let result = null // we actually only find a single pool for now
|
||||||
for (let [exchange, fee, pool] of rawRoutes) {
|
for (let [exchange, fee, pool] of rawRoutes) {
|
||||||
exchange = Number(exchange)
|
exchange = Number(exchange)
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export function uniswapV3PoolAddress(chainId, tokenAddrA, tokenAddrB, fee) {
|
|||||||
const encoded = ethers.AbiCoder.defaultAbiCoder().encode(['address', 'address', 'uint24'], [addr0, addr1, fee]);
|
const encoded = ethers.AbiCoder.defaultAbiCoder().encode(['address', 'address', 'uint24'], [addr0, addr1, fee]);
|
||||||
const salt = ethers.keccak256(encoded)
|
const salt = ethers.keccak256(encoded)
|
||||||
const factory = uniswapV3Addresses[chainId]?.factory
|
const factory = uniswapV3Addresses[chainId]?.factory
|
||||||
console.log('uni3addr', addr0, addr1, fee, salt, factory)
|
|
||||||
if (!factory) {
|
if (!factory) {
|
||||||
console.log('no uniswap factory for chain', chainId)
|
console.log('no uniswap factory for chain', chainId)
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {ethers} from "ethers";
|
import {ethers} from "ethers";
|
||||||
import {setProvider, useStore} from "@/store/store";
|
import {useStore} from "@/store/store";
|
||||||
import {socket} from "@/socket.js";
|
import {socket} from "@/socket.js";
|
||||||
import {contractOrNull, vaultAddress} from "@/blockchain/contract.js";
|
import {contractOrNull, vaultAddress} from "@/blockchain/contract.js";
|
||||||
import {vaultAbi} from "@/blockchain/abi.js";
|
import {vaultAbi} from "@/blockchain/abi.js";
|
||||||
@@ -13,7 +13,7 @@ export function onChainChanged(chainId) {
|
|||||||
store.chainId = chainId // touch the chainId last. will cause any clients of the store's provider getter to refresh
|
store.chainId = chainId // touch the chainId last. will cause any clients of the store's provider getter to refresh
|
||||||
store.account = null
|
store.account = null
|
||||||
const provider = new ethers.BrowserProvider(window.ethereum, chainId);
|
const provider = new ethers.BrowserProvider(window.ethereum, chainId);
|
||||||
setProvider(provider, chainId)
|
store.provider = provider
|
||||||
provider.listAccounts().then((accounts)=>changeAccounts(accounts.map((a)=>a.address)))
|
provider.listAccounts().then((accounts)=>changeAccounts(accounts.map((a)=>a.address)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,12 +30,10 @@ function changeAccounts(accounts) {
|
|||||||
const addr = accounts[0]
|
const addr = accounts[0]
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
store.account = addr
|
store.account = addr
|
||||||
console.log('set store.account to', addr, store.account)
|
|
||||||
discoverVaults()
|
discoverVaults()
|
||||||
flushTransactions()
|
flushTransactions()
|
||||||
socket.emit('address', store.chainId, addr)
|
socket.emit('address', store.chainId, addr)
|
||||||
}
|
}
|
||||||
console.log('changeAccounts ended')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAccountsChanged(accounts) {
|
function onAccountsChanged(accounts) {
|
||||||
@@ -102,7 +100,6 @@ function discoverVaults() {
|
|||||||
s.vaults = []
|
s.vaults = []
|
||||||
else
|
else
|
||||||
_discoverVaults(owner).then((result)=>{
|
_discoverVaults(owner).then((result)=>{
|
||||||
console.log('read store.account', s.account)
|
|
||||||
if( s.account === owner ) { // double-check the account since it could have changed during our await
|
if( s.account === owner ) { // double-check the account since it could have changed during our await
|
||||||
s.vaults = result
|
s.vaults = result
|
||||||
if( pendingOrders.length )
|
if( pendingOrders.length )
|
||||||
|
|||||||
26
src/components/Amount.vue
Normal file
26
src/components/Amount.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<v-btn :text="s.amountIsTotal ? 'total' : 'per tranche'" variant="outlined"
|
||||||
|
@click="s.amountIsTotal=!s.amountIsTotal" class="total"/>
|
||||||
|
</template>
|
||||||
|
</v-text-field>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useStore} from "@/store/store";
|
||||||
|
import {validateRequired, validateAmount, vAutoSelect} from "@/misc.js";
|
||||||
|
|
||||||
|
const s = useStore()
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use "src/styles/vars" as *;
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -44,7 +44,7 @@ const tokenA = computed({
|
|||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
if( !s.tokenA || s.tokenA.address !== value.address ) {
|
if( !s.tokenA || s.tokenA.address !== value.address ) {
|
||||||
s.tokenA.value = value
|
s.tokenA = value
|
||||||
routeFinder.invoke()
|
routeFinder.invoke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,25 +64,25 @@ const tokenB = computed({
|
|||||||
|
|
||||||
const routes = computed({
|
const routes = computed({
|
||||||
get() {
|
get() {
|
||||||
return s.routes.value
|
return s.routes
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
console.log('setting new routes', s.routes.value, value)
|
console.log('setting new routes', s.routes, value)
|
||||||
s.routes.value = value
|
s.routes = value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
async function componentFindRoute() {
|
async function componentFindRoute() {
|
||||||
const tokenA = s.tokenA
|
const tokenA = s.tokenA
|
||||||
const tokenB = s.tokenB
|
const tokenB = s.tokenB
|
||||||
console.log('finding route', tokenA, tokenB)
|
// console.log('finding route', tokenA, tokenB)
|
||||||
s.routes = []
|
s.routes = []
|
||||||
if (!tokenA || !tokenB)
|
if (!tokenA || !tokenB)
|
||||||
return
|
return
|
||||||
s.routesPending = true
|
s.routesPending = true
|
||||||
try {
|
try {
|
||||||
const result = await findRoute(tokenA, tokenB)
|
const result = await findRoute(tokenA, tokenB)
|
||||||
console.log('found route', result)
|
// console.log('found route', result)
|
||||||
s.routes = result
|
s.routes = result
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const price = computed(()=>{
|
|||||||
if( !route || !(route.pool in s.poolPrices) )
|
if( !route || !(route.pool in s.poolPrices) )
|
||||||
return ''
|
return ''
|
||||||
let p = s.poolPrices[route.pool]
|
let p = s.poolPrices[route.pool]
|
||||||
console.log('pool price is',typeof p, p)
|
// console.log('pool price is',typeof p, p)
|
||||||
if( !p )
|
if( !p )
|
||||||
return ''
|
return ''
|
||||||
p = FixedNumber.fromString(p, WIDE_PRICE_FORMAT).toUnsafeFloat()
|
p = FixedNumber.fromString(p, WIDE_PRICE_FORMAT).toUnsafeFloat()
|
||||||
|
|||||||
@@ -6,26 +6,13 @@
|
|||||||
<v-card-item>
|
<v-card-item>
|
||||||
<pair-choice/>
|
<pair-choice/>
|
||||||
<div v-if="s.route && !s.routesPending">
|
<div v-if="s.route && !s.routesPending">
|
||||||
<v-text-field label='Amount' type="number" step="1" variant="outlined" aria-valuemin="0" min="0"
|
<amount/>
|
||||||
v-model="amount" :rules="[validateRequired,validateAmount]" v-auto-select>
|
|
||||||
<template v-slot:append-inner>
|
|
||||||
<v-btn @click="amountIsTokenA=!amountIsTokenA" variant="outlined" class="mr-2">
|
|
||||||
{{ amountIsTokenA ? s.tokenA.symbol : s.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"
|
<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>
|
v-model="tranches" :rules="[validateRequired,validateTranches]" v-auto-select>
|
||||||
<!-- <template v-slot:prepend-inner>-->
|
|
||||||
<!-- <div>{{ amountIsTotal ? 'Split into' : 'Times' }}</div>-->
|
|
||||||
<!-- </template>-->
|
|
||||||
<template v-slot:append-inner>tranches</template>
|
<template v-slot:append-inner>tranches</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
<v-text-field type="number" variant="outlined" :min="1" v-model="interval" class="interval"
|
<v-text-field type="number" variant="outlined" :min="1" v-model="interval" class="interval"
|
||||||
:label="intervalIsTotal ? 'Completion time' : 'Time between tranches'" v-auto-select>
|
:label="intervalIsTotal ? 'Completion time' : 'Time between tranches'" v-auto-select>
|
||||||
<!-- <template v-slot:append>APART</template>-->
|
|
||||||
<template v-slot:prepend-inner>
|
<template v-slot:prepend-inner>
|
||||||
<v-btn variant="outlined" :text="intervalIsTotal ? 'Within' : 'Spaced apart'" class="within mr-2"
|
<v-btn variant="outlined" :text="intervalIsTotal ? 'Within' : 'Spaced apart'" class="within mr-2"
|
||||||
@click="intervalIsTotal=!intervalIsTotal"/>
|
@click="intervalIsTotal=!intervalIsTotal"/>
|
||||||
@@ -34,11 +21,11 @@
|
|||||||
<v-btn variant="outlined" :text="timeUnits[timeUnitIndex]" @click="toggleTimeUnits" class="time-units"/>
|
<v-btn variant="outlined" :text="timeUnits[timeUnitIndex]" @click="toggleTimeUnits" class="time-units"/>
|
||||||
</template>
|
</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
<v-text-field v-model="limitPrice" :label="(limitIsMinimum?'Minimum':'Maximum')+' Price'" type="number"
|
<v-text-field v-model="s.limitPrice" :label="(s.limitIsMinimum?'Minimum':'Maximum')+' Price'" type="number"
|
||||||
variant="outlined" aria-valuemin="0" min="0"
|
variant="outlined" aria-valuemin="0" min="0"
|
||||||
clearable :rules="[validateAmount, validateMin]" v-auto-select>
|
clearable :rules="[validateAmount, validateMin]" v-auto-select>
|
||||||
<template v-slot:append-inner>
|
<template v-slot:append-inner>
|
||||||
<v-btn variant="outlined" @click="inverted=!inverted">
|
<v-btn variant="outlined" @click="s.inverted=!s.inverted">
|
||||||
{{s.pairSymbol}}
|
{{s.pairSymbol}}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
@@ -64,9 +51,10 @@
|
|||||||
import {useStore} from "@/store/store";
|
import {useStore} from "@/store/store";
|
||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import PhoneCard from "@/components/PhoneCard.vue";
|
import PhoneCard from "@/components/PhoneCard.vue";
|
||||||
|
import Amount from "@/components/Amount.vue"
|
||||||
// noinspection ES6UnusedImports
|
// noinspection ES6UnusedImports
|
||||||
import {routeInverted, SingletonCoroutine, vAutoSelect} from "@/misc.js";
|
import {isEmpty, routeInverted, SingletonCoroutine, vAutoSelect, validateRequired, validateAmount} from "@/misc.js";
|
||||||
import {newLimitConstraint, newOrder, newTimeConstraint, sqrtX96, TimeMode} from "@/blockchain/orderlib.js";
|
import {newLimitConstraint, newOrder, newTimeConstraint, TimeMode} from "@/blockchain/orderlib.js";
|
||||||
import {FixedNumber} from "ethers";
|
import {FixedNumber} from "ethers";
|
||||||
import {pendOrder} from "@/blockchain/wallet.js";
|
import {pendOrder} from "@/blockchain/wallet.js";
|
||||||
import NeedsProvider from "@/components/NeedsProvider.vue";
|
import NeedsProvider from "@/components/NeedsProvider.vue";
|
||||||
@@ -76,21 +64,14 @@ import PairChoice from "@/components/PairChoice.vue";
|
|||||||
|
|
||||||
const s = useStore()
|
const s = useStore()
|
||||||
|
|
||||||
const amount = ref(100) // todo 0
|
|
||||||
const amountIsTokenA = ref(false)
|
|
||||||
const amountIsTotal = ref(true)
|
|
||||||
const tranches = ref(3)
|
const tranches = ref(3)
|
||||||
const inverted = ref(false)
|
|
||||||
const minPrice = ref(null)
|
const minPrice = ref(null)
|
||||||
const maxPrice = ref(null)
|
const maxPrice = ref(null)
|
||||||
const limitPrice = ref(null)
|
|
||||||
const interval = ref(1)
|
const interval = ref(1)
|
||||||
const intervalIsTotal = ref(true)
|
const intervalIsTotal = ref(true)
|
||||||
const timeUnits = ['minutes', 'hours', 'days']
|
const timeUnits = ['minutes', 'hours', 'days']
|
||||||
const timeUnitIndex = ref(0)
|
const timeUnitIndex = ref(0)
|
||||||
const limitIsMinimum = computed(() => !(s.buy ^ s.inverted))
|
const validOrder = computed(()=>s.validOrder)
|
||||||
const validOrder = computed(()=>amount.value > 0 && s.routes.length > 0 )
|
|
||||||
|
|
||||||
|
|
||||||
function toggleTimeUnits() {
|
function toggleTimeUnits() {
|
||||||
timeUnitIndex.value++
|
timeUnitIndex.value++
|
||||||
@@ -99,18 +80,6 @@ function toggleTimeUnits() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function isEmpty(v) {
|
|
||||||
return v === null || typeof v === 'string' && v.trim() === ''
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function validateRequired(v) {
|
|
||||||
if (isEmpty(v))
|
|
||||||
return 'Required'
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function validateTranches(v) {
|
function validateTranches(v) {
|
||||||
const i = parseInt(v)
|
const i = parseInt(v)
|
||||||
if (parseFloat(v) !== i)
|
if (parseFloat(v) !== i)
|
||||||
@@ -123,17 +92,6 @@ function validateTranches(v) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function validateAmount(v) {
|
|
||||||
if (isEmpty(v))
|
|
||||||
return true
|
|
||||||
const floatRegex = /^-?\d*(?:[.,]\d*?)?$/
|
|
||||||
if (!floatRegex.test(v))
|
|
||||||
return 'Amount must be a number'
|
|
||||||
if (parseFloat(v) <= 0)
|
|
||||||
return 'Amount must be positive'
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateMax(v) {
|
function validateMax(v) {
|
||||||
if (!isEmpty(minPrice.value) && !isEmpty(v) && parseFloat(v) < parseFloat(minPrice.value))
|
if (!isEmpty(minPrice.value) && !isEmpty(v) && parseFloat(v) < parseFloat(minPrice.value))
|
||||||
return 'Must be greater than the minimum price'
|
return 'Must be greater than the minimum price'
|
||||||
@@ -152,9 +110,7 @@ function placeOrder() {
|
|||||||
const tokenIn = s.buy ? tb.address : ta.address
|
const tokenIn = s.buy ? tb.address : ta.address
|
||||||
const tokenOut = s.buy ? ta.address : tb.address
|
const tokenOut = s.buy ? ta.address : tb.address
|
||||||
const route = s.route
|
const route = s.route
|
||||||
const amountToken = amountIsTokenA.value ? ta : tb
|
const amt = FixedNumber.fromString(s.amount.toString(), {decimals: s.amountToken.decimals}).value
|
||||||
const amt = FixedNumber.fromString(amount.value.toString(), {decimals: amountToken.decimals}).value
|
|
||||||
const amountIsInput = amountIsTokenA.value !== s.buy
|
|
||||||
|
|
||||||
// build tranches
|
// build tranches
|
||||||
const n = tranches.value // num tranches
|
const n = tranches.value // num tranches
|
||||||
@@ -173,20 +129,18 @@ function placeOrder() {
|
|||||||
const ceil = oneHundredPercent % BigInt(n) ? 1n : 0n
|
const ceil = oneHundredPercent % BigInt(n) ? 1n : 0n
|
||||||
const amtPerTranche = oneHundredPercent / BigInt(n) + ceil
|
const amtPerTranche = oneHundredPercent / BigInt(n) + ceil
|
||||||
duration -= 15 // subtract 15 seconds so the last tranche completes before the deadline
|
duration -= 15 // subtract 15 seconds so the last tranche completes before the deadline
|
||||||
console.log('duration', duration)
|
|
||||||
let priceConstraint = null
|
let priceConstraint = null
|
||||||
if( limitPrice.value ) {
|
if( s.limitPrice ) {
|
||||||
const inverted = routeInverted(route)
|
const inverted = routeInverted(route)
|
||||||
const isAbove = limitIsMinimum.value ^ inverted
|
const isAbove = s.limitIsMinimum ^ inverted
|
||||||
const isRatio = false // todo ratios
|
const isRatio = false // todo ratios
|
||||||
const decimals = 10 ** (s.tokenA.decimals - s.tokenB.decimals)
|
const decimals = 10 ** (s.tokenA.decimals - s.tokenB.decimals)
|
||||||
const limit = inverted ? decimals/limitPrice.value : limitPrice.value/decimals
|
const limit = inverted ? decimals/s.limitPrice : s.limitPrice/decimals
|
||||||
priceConstraint = !limitPrice.value ? null : newLimitConstraint(isAbove, isRatio, limit)
|
priceConstraint = !s.limitPrice ? null : newLimitConstraint(isAbove, isRatio, limit)
|
||||||
}
|
}
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
const start = Math.floor(i * (duration / Math.max((n - 1), 1)))
|
const start = Math.floor(i * (duration / Math.max((n - 1), 1)))
|
||||||
const end = start + window
|
const end = start + window
|
||||||
console.log('tranche window', start, end, (end-start)/60)
|
|
||||||
const cs = [newTimeConstraint(TimeMode.SinceOrderStart, start, TimeMode.SinceOrderStart, end)]
|
const cs = [newTimeConstraint(TimeMode.SinceOrderStart, start, TimeMode.SinceOrderStart, end)]
|
||||||
if( priceConstraint !== null )
|
if( priceConstraint !== null )
|
||||||
cs.push(priceConstraint)
|
cs.push(priceConstraint)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const known_chains = [
|
|||||||
{
|
{
|
||||||
name: 'Arbitrum One',
|
name: 'Arbitrum One',
|
||||||
id: 42161,
|
id: 42161,
|
||||||
image: null,
|
image: '/arbitrum-logo.svg',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
21
src/misc.js
21
src/misc.js
@@ -58,3 +58,24 @@ export function routeInverted(route) {
|
|||||||
const s = useStore()
|
const s = useStore()
|
||||||
return route && (route.token0 === s.tokenA) === s.inverted
|
return route && (route.token0 === s.tokenA) === s.inverted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isEmpty(v) {
|
||||||
|
return v === null || typeof v === 'string' && v.trim() === ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateRequired(v) {
|
||||||
|
if (isEmpty(v))
|
||||||
|
return 'Required'
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateAmount(v) {
|
||||||
|
if (isEmpty(v))
|
||||||
|
return true
|
||||||
|
const floatRegex = /^-?\d*(?:[.,]\d*?)?$/
|
||||||
|
if (!floatRegex.test(v))
|
||||||
|
return 'Amount must be a number'
|
||||||
|
if (parseFloat(v) <= 0)
|
||||||
|
return 'Amount must be positive'
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -17,18 +17,10 @@ socket.on('disconnect', () => {
|
|||||||
|
|
||||||
socket.on('welcome', async (data) => {
|
socket.on('welcome', async (data) => {
|
||||||
console.log('welcome', data)
|
console.log('welcome', data)
|
||||||
const mockCoins = data['chainInfo'][31337].mockCoins
|
|
||||||
console.log('coin order:',
|
|
||||||
mockCoins[1] > mockCoins[0] ? "coin1 > coin0" : "coin0 > coin1 (inverted)",
|
|
||||||
mockCoins)
|
|
||||||
const s = useStore()
|
const s = useStore()
|
||||||
|
// todo put the vaultInitCodeHash into the chainInfo
|
||||||
s.chainInfo = data.chainInfo
|
s.chainInfo = data.chainInfo
|
||||||
s.vaultInitCodeHash = data.vaultInitCodeHash
|
s.vaultInitCodeHash = data.vaultInitCodeHash
|
||||||
// set default tokens in pair choice dropdown
|
|
||||||
if( s.tokenA === null && Object.values(s.tokens).length >= 1 )
|
|
||||||
s.tokenA = Object.values(s.tokens)[0]
|
|
||||||
if( s.tokenB === null && Object.values(s.tokens).length >= 2 )
|
|
||||||
s.tokenB = Object.values(s.tokens)[1]
|
|
||||||
const p = new ethers.BrowserProvider(window.ethereum)
|
const p = new ethers.BrowserProvider(window.ethereum)
|
||||||
const network = await p.getNetwork()
|
const network = await p.getNetwork()
|
||||||
if (network !== null)
|
if (network !== null)
|
||||||
|
|||||||
@@ -3,90 +3,123 @@ import { defineStore } from 'pinia'
|
|||||||
import {knownTokens} from "@/knownTokens.js";
|
import {knownTokens} from "@/knownTokens.js";
|
||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
|
|
||||||
let rawProvider = null
|
|
||||||
let rawProviderChainId = null
|
|
||||||
|
|
||||||
export function setProvider( provider, chainId ) {
|
export const useStore = defineStore('app', ()=> {
|
||||||
rawProvider = provider
|
const _chainId = ref(null)
|
||||||
rawProviderChainId = chainId
|
const _chainInfo = ref({})
|
||||||
}
|
const tokenA = ref(null)
|
||||||
|
const tokenB = ref(null)
|
||||||
|
|
||||||
export const useStore = defineStore('app', {
|
function getTokenList() {
|
||||||
state: () => ({
|
const chains = _chainId.value in _chainInfo.value && _chainInfo.value[_chainId.value].tokens !== undefined ?
|
||||||
chainId: null,
|
_chainInfo.value[_chainId.value].tokens : []
|
||||||
chainInfo: {},
|
let known = knownTokens[_chainId.value]
|
||||||
vaultInitCodeHash: null,
|
known = known ? Object.values(known) : []
|
||||||
account: null,
|
let extras = extraTokens[_chainId.value]
|
||||||
vaults: [],
|
extras = extras ? Object.values(extras) : []
|
||||||
transactionSenders: [], // a list of function(signer) that send transactions
|
return [...chains, ...known, ...extras]; // put chains first so the Mockcoin pool is automatically selected
|
||||||
errors: [
|
}
|
||||||
// todo re-enable danger warning
|
function getTokens() {
|
||||||
// {title: 'DANGER!', text: 'This is early development (alpha) software. There could be severe bugs that lose all your money. Thank you for testing a SMALL amount!', closeable: true}
|
const result = {}
|
||||||
],
|
const all = getTokenList();
|
||||||
extraTokens: {},
|
for (const token of all)
|
||||||
poolPrices: {},
|
result[token.address] = token
|
||||||
vaultBalances: {}, // indexed by vault addr then by token addr. value is an int
|
return result
|
||||||
orders: {}, // indexed by vault, value is another dictionary with orderIndex as key and order status values
|
}
|
||||||
|
function setDefaultTokens() {
|
||||||
|
const tokens = getTokenList()
|
||||||
|
if( tokens.length > 0 )
|
||||||
|
tokenA.value = tokens[0]
|
||||||
|
if( tokens.length > 1 )
|
||||||
|
tokenB.value = tokens[1]
|
||||||
|
}
|
||||||
|
const chainId = computed({
|
||||||
|
get() {return _chainId},
|
||||||
|
set(v) {_chainId.value=v; setDefaultTokens()}
|
||||||
|
})
|
||||||
|
const chainInfo = computed({
|
||||||
|
get() {return _chainInfo},
|
||||||
|
set(v) {_chainInfo.value=v; setDefaultTokens()}
|
||||||
|
})
|
||||||
|
const chain = computed(() => !_chainId.value ? null : (_chainInfo.value[_chainId.value] || null))
|
||||||
|
// making the provider directly reactive causes exceptions (calling private method...) when calling provider
|
||||||
|
// functions, so we use a separate ref to signal changes
|
||||||
|
let _provider = null
|
||||||
|
const _providerTouch = ref(false)
|
||||||
|
const provider = computed({
|
||||||
|
get() {_providerTouch.value; return _provider},
|
||||||
|
set(v) {_provider=v; _providerTouch.value = !_providerTouch.value}
|
||||||
|
})
|
||||||
|
const vaultInitCodeHash = ref(null)
|
||||||
|
const account = ref(null)
|
||||||
|
const vaults = ref([])
|
||||||
|
const transactionSenders = ref([]) // a list of function(signer) that send transactions
|
||||||
|
const errors = ref([])
|
||||||
|
const extraTokens = ref({})
|
||||||
|
const poolPrices = ref({})
|
||||||
|
const vaultBalances = ref({}) // indexed by vault addr then by token addr. value is an int
|
||||||
|
const orders = ref({}) // indexed by vault value is another dictionary with orderIndex as key and order status values
|
||||||
|
|
||||||
// Order Input Forms
|
// Order Input Forms
|
||||||
tokenA: null,
|
// const tokenA = ref(null) // defined at top
|
||||||
tokenB: null,
|
// const tokenB = ref(null)
|
||||||
routes: [],
|
const buy = ref(false)
|
||||||
routesPending: false,
|
const inverted = ref(false)
|
||||||
inverted: false,
|
const amount = ref(100) // todo
|
||||||
}),
|
const amountIsTokenA = ref(false) // todo
|
||||||
getters: {
|
const amountIsTotal = ref(true)
|
||||||
vault: (s)=>s.vaults.length===0 ? null : s.vaults[0],
|
const limitPrice = ref(null)
|
||||||
provider: (s)=>s.chainId===rawProviderChainId ? rawProvider : null,
|
const routes = ref([])
|
||||||
chain: (s)=> !s.chainInfo ? null : (s.chainInfo[s.chainId] || null),
|
const routesPending = ref(false)
|
||||||
tokens: (s)=>{
|
|
||||||
const chains = s.chainId in s.chainInfo && s.chainInfo[s.chainId].tokens !== undefined ? s.chainInfo[s.chainId].tokens : []
|
const validOrder = computed(() => amount.value > 0 && routes.value.length > 0)
|
||||||
let known = knownTokens[s.chainId]
|
const vault = computed(() => vaults.value.length === 0 ? null : vaults.value[0])
|
||||||
known = known ? Object.values(known) : []
|
const tokens = computed(getTokens)
|
||||||
let extras = s.extraTokens[s.chainId]
|
const factory = computed(() => !chain.value ? null : chain.value.factory)
|
||||||
extras = extras ? Object.values(extras) : []
|
const helper = computed(() => !chain.value ? null : chain.value.helper)
|
||||||
const result = {}
|
const mockenv = computed(() => !chain.value ? null : chain.value.mockenv)
|
||||||
const all = [...chains, ...known, ...extras]; // put chains first so the Mockcoin pool is automatically selected
|
const mockCoins = computed(() => !chain.value ? [] : !chain.value.mockCoins ? [] : chain.value.mockCoins)
|
||||||
for( const token of all)
|
const route = computed(() => routes.value.length === 0 ? null : routes.value[0])
|
||||||
result[token.address] = token
|
const base = computed(() => {
|
||||||
return result
|
const token = inverted.value ? tokenB.value : tokenA.value
|
||||||
},
|
return !token ? {} : token
|
||||||
factory: (s)=>!s.chain?null:s.chain.factory,
|
})
|
||||||
helper: (s)=>!s.chain?null:s.chain.helper,
|
const quote = computed(() => {
|
||||||
mockenv: (s)=>!s.chain?null:s.chain.mockenv,
|
const token = inverted.value ? tokenA.value : tokenB.value
|
||||||
mockCoins: (s)=>!s.chain?[]:!s.chain.mockCoins?[]:s.chain.mockCoins,
|
return !token ? {} : token
|
||||||
route: (s)=>s.routes.length===0 ? null : s.routes[0],
|
})
|
||||||
pairSymbol: (s)=>s.base?.symbol+'\\'+s.quote?.symbol,
|
const pairSymbol = computed(() => base.value?.symbol + '\\' + quote.value?.symbol)
|
||||||
base: (s)=>{
|
const limitIsMinimum = computed(() => !(buy.value ^ inverted.value))
|
||||||
const token = s.inverted ? s.tokenB : s.tokenA
|
const amountToken = computed(() => amountIsTokenA.value ? tokenA.value : tokenB.value)
|
||||||
return !token?{}:token
|
const amountIsInput = computed(() => amountIsTokenA.value !== buy.value)
|
||||||
},
|
|
||||||
quote: (s)=> {
|
function removeTransactionSender(sender) {
|
||||||
const token = s.inverted ? s.tokenA : s.tokenB
|
this.transactionSenders = this.transactionSenders.filter((v) => v !== sender)
|
||||||
return !token ? {} : token
|
}
|
||||||
},
|
function error(title, text, closeable=true) {
|
||||||
},
|
this.errors.push({title, text, closeable})
|
||||||
actions: {
|
}
|
||||||
removeTransactionSender(sender) {
|
function closeError(title, text) {
|
||||||
this.transactionSenders = this.transactionSenders.filter((v) => v !== sender)
|
const result = []
|
||||||
},
|
this.errors.forEach((i)=>{if(i.title!==title && i.text!==text) result.push(i)})
|
||||||
error(title, text, closeable=true) {
|
this.errors = result
|
||||||
this.errors.push({title, text, closeable})
|
}
|
||||||
},
|
function addToken(chainId, info) {
|
||||||
closeError(title, text) {
|
this.$patch(() => {
|
||||||
const result = []
|
let extras = extraTokens[chainId]
|
||||||
this.errors.forEach((i)=>{if(i.title!==title && i.text!==text) result.push(i)})
|
if (extras === undefined) {
|
||||||
this.errors = result
|
extras = {}
|
||||||
},
|
extraTokens[chainId] = extras
|
||||||
addToken(chainId, info) {
|
}
|
||||||
this.$patch((s) => {
|
extras[info.address] = info
|
||||||
let extras = s.extraTokens[chainId]
|
})
|
||||||
if (extras === undefined) {
|
}
|
||||||
extras = {}
|
|
||||||
s.extraTokens[chainId] = extras
|
return {
|
||||||
}
|
chainId, chainInfo, chain, provider, vaultInitCodeHash, account, vaults, transactionSenders, errors, extraTokens,
|
||||||
extras[info.address] = info
|
poolPrices, vaultBalances, orders, tokenA, tokenB, routes, routesPending, inverted, amount, amountIsTokenA,
|
||||||
})
|
amountIsTotal, limitPrice, validOrder, vault, tokens, factory, helper, mockenv, mockCoins, route,
|
||||||
},
|
pairSymbol, base, quote, limitIsMinimum, amountToken, amountIsInput, removeTransactionSender, error, closeError,
|
||||||
},
|
addToken,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user