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) {
|
||||
console.log('va', owner, num)
|
||||
if( !owner )
|
||||
return null
|
||||
const s = useStore()
|
||||
|
||||
@@ -15,10 +15,10 @@ export function subPrices( routes ) {
|
||||
const subRoutes = []
|
||||
let chainId = null
|
||||
for( const route of routes ) {
|
||||
console.log('sub route', route, subscriptionCounts)
|
||||
// console.log('sub route', route, subscriptionCounts)
|
||||
if( !(route in subscriptionCounts) || subscriptionCounts[route] === 0 ) {
|
||||
subscriptionCounts[route] = 1
|
||||
console.log('subscribing to pool', route.pool)
|
||||
// console.log('subscribing to pool', route.pool)
|
||||
subRoutes.push(route)
|
||||
}
|
||||
else {
|
||||
@@ -33,9 +33,7 @@ export function subPrices( routes ) {
|
||||
// perform a local query if necessary
|
||||
for( const route of subRoutes ) {
|
||||
const s = useStore()
|
||||
console.log('route in prices?', route.pool in s.poolPrices, route.pool, 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)
|
||||
}
|
||||
}
|
||||
@@ -46,7 +44,7 @@ export function unsubPrices( routes ) {
|
||||
let chainId = null
|
||||
const unsubAddrs = []
|
||||
for( const route of routes ) {
|
||||
console.log('unsub route', route, subscriptionCounts)
|
||||
// console.log('unsub route', route, subscriptionCounts)
|
||||
if( !(route in subscriptionCounts) ) {
|
||||
console.error('unsubscribed to a nonexistent route', route)
|
||||
}
|
||||
@@ -56,7 +54,7 @@ export function unsubPrices( routes ) {
|
||||
unsubAddrs.push(route.pool)
|
||||
if( chainId !== null && route.chainId !== chainId )
|
||||
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
|
||||
}
|
||||
else if( subscriptionCounts[route] < 0 ) {
|
||||
@@ -71,7 +69,6 @@ export function unsubPrices( routes ) {
|
||||
|
||||
|
||||
async function getPriceForRoute(route) {
|
||||
console.log('route is',route)
|
||||
if( route.exchange === Exchange.UniswapV3 ) {
|
||||
const addr = uniswapV3PoolAddress(route.chainId, route.token0.address, route.token1.address, route.fee)
|
||||
const store = useStore();
|
||||
@@ -88,7 +85,7 @@ async function getPriceForRoute(route) {
|
||||
price = FixedNumber.fromValue(price,0,WIDE_PRICE_FORMAT)
|
||||
price = price.div(FixedNumber.fromValue(2n**(96n*2n),0,WIDE_PRICE_FORMAT))
|
||||
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
|
||||
return price
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export async function findRoute(tokenA, tokenB) {
|
||||
const chainId = useStore().chainId
|
||||
const rawRoutes = await helper.getRoutes(tokenA.address, tokenB.address)
|
||||
// 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
|
||||
for (let [exchange, fee, pool] of rawRoutes) {
|
||||
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 salt = ethers.keccak256(encoded)
|
||||
const factory = uniswapV3Addresses[chainId]?.factory
|
||||
console.log('uni3addr', addr0, addr1, fee, salt, factory)
|
||||
if (!factory) {
|
||||
console.log('no uniswap factory for chain', chainId)
|
||||
return null
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {ethers} from "ethers";
|
||||
import {setProvider, useStore} from "@/store/store";
|
||||
import {useStore} from "@/store/store";
|
||||
import {socket} from "@/socket.js";
|
||||
import {contractOrNull, vaultAddress} from "@/blockchain/contract.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.account = null
|
||||
const provider = new ethers.BrowserProvider(window.ethereum, chainId);
|
||||
setProvider(provider, chainId)
|
||||
store.provider = provider
|
||||
provider.listAccounts().then((accounts)=>changeAccounts(accounts.map((a)=>a.address)))
|
||||
}
|
||||
}
|
||||
@@ -30,12 +30,10 @@ function changeAccounts(accounts) {
|
||||
const addr = accounts[0]
|
||||
const store = useStore()
|
||||
store.account = addr
|
||||
console.log('set store.account to', addr, store.account)
|
||||
discoverVaults()
|
||||
flushTransactions()
|
||||
socket.emit('address', store.chainId, addr)
|
||||
}
|
||||
console.log('changeAccounts ended')
|
||||
}
|
||||
|
||||
function onAccountsChanged(accounts) {
|
||||
@@ -102,7 +100,6 @@ function discoverVaults() {
|
||||
s.vaults = []
|
||||
else
|
||||
_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
|
||||
s.vaults = result
|
||||
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) {
|
||||
if( !s.tokenA || s.tokenA.address !== value.address ) {
|
||||
s.tokenA.value = value
|
||||
s.tokenA = value
|
||||
routeFinder.invoke()
|
||||
}
|
||||
}
|
||||
@@ -64,25 +64,25 @@ const tokenB = computed({
|
||||
|
||||
const routes = computed({
|
||||
get() {
|
||||
return s.routes.value
|
||||
return s.routes
|
||||
},
|
||||
set(value) {
|
||||
console.log('setting new routes', s.routes.value, value)
|
||||
s.routes.value = value
|
||||
console.log('setting new routes', s.routes, value)
|
||||
s.routes = value
|
||||
}
|
||||
})
|
||||
|
||||
async function componentFindRoute() {
|
||||
const tokenA = s.tokenA
|
||||
const tokenB = s.tokenB
|
||||
console.log('finding route', tokenA, tokenB)
|
||||
// console.log('finding route', tokenA, tokenB)
|
||||
s.routes = []
|
||||
if (!tokenA || !tokenB)
|
||||
return
|
||||
s.routesPending = true
|
||||
try {
|
||||
const result = await findRoute(tokenA, tokenB)
|
||||
console.log('found route', result)
|
||||
// console.log('found route', result)
|
||||
s.routes = result
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
@@ -21,7 +21,7 @@ const price = computed(()=>{
|
||||
if( !route || !(route.pool in s.poolPrices) )
|
||||
return ''
|
||||
let p = s.poolPrices[route.pool]
|
||||
console.log('pool price is',typeof p, p)
|
||||
// console.log('pool price is',typeof p, p)
|
||||
if( !p )
|
||||
return ''
|
||||
p = FixedNumber.fromString(p, WIDE_PRICE_FORMAT).toUnsafeFloat()
|
||||
|
||||
@@ -6,26 +6,13 @@
|
||||
<v-card-item>
|
||||
<pair-choice/>
|
||||
<div v-if="s.route && !s.routesPending">
|
||||
<v-text-field label='Amount' type="number" step="1" variant="outlined" aria-valuemin="0" min="0"
|
||||
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>
|
||||
<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: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'" v-auto-select>
|
||||
<!-- <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"/>
|
||||
@@ -34,11 +21,11 @@
|
||||
<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"
|
||||
<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="inverted=!inverted">
|
||||
<v-btn variant="outlined" @click="s.inverted=!s.inverted">
|
||||
{{s.pairSymbol}}
|
||||
</v-btn>
|
||||
</template>
|
||||
@@ -64,9 +51,10 @@
|
||||
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 {routeInverted, SingletonCoroutine, vAutoSelect} from "@/misc.js";
|
||||
import {newLimitConstraint, newOrder, newTimeConstraint, sqrtX96, TimeMode} from "@/blockchain/orderlib.js";
|
||||
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";
|
||||
@@ -76,21 +64,14 @@ import PairChoice from "@/components/PairChoice.vue";
|
||||
|
||||
const s = useStore()
|
||||
|
||||
const amount = ref(100) // todo 0
|
||||
const amountIsTokenA = ref(false)
|
||||
const amountIsTotal = ref(true)
|
||||
const tranches = ref(3)
|
||||
const inverted = ref(false)
|
||||
const minPrice = ref(null)
|
||||
const maxPrice = ref(null)
|
||||
const limitPrice = ref(null)
|
||||
const interval = ref(1)
|
||||
const intervalIsTotal = ref(true)
|
||||
const timeUnits = ['minutes', 'hours', 'days']
|
||||
const timeUnitIndex = ref(0)
|
||||
const limitIsMinimum = computed(() => !(s.buy ^ s.inverted))
|
||||
const validOrder = computed(()=>amount.value > 0 && s.routes.length > 0 )
|
||||
|
||||
const validOrder = computed(()=>s.validOrder)
|
||||
|
||||
function toggleTimeUnits() {
|
||||
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) {
|
||||
const i = parseInt(v)
|
||||
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) {
|
||||
if (!isEmpty(minPrice.value) && !isEmpty(v) && parseFloat(v) < parseFloat(minPrice.value))
|
||||
return 'Must be greater than the minimum price'
|
||||
@@ -152,9 +110,7 @@ function placeOrder() {
|
||||
const tokenIn = s.buy ? tb.address : ta.address
|
||||
const tokenOut = s.buy ? ta.address : tb.address
|
||||
const route = s.route
|
||||
const amountToken = amountIsTokenA.value ? ta : tb
|
||||
const amt = FixedNumber.fromString(amount.value.toString(), {decimals: amountToken.decimals}).value
|
||||
const amountIsInput = amountIsTokenA.value !== s.buy
|
||||
const amt = FixedNumber.fromString(s.amount.toString(), {decimals: s.amountToken.decimals}).value
|
||||
|
||||
// build tranches
|
||||
const n = tranches.value // num tranches
|
||||
@@ -173,20 +129,18 @@ function placeOrder() {
|
||||
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
|
||||
console.log('duration', duration)
|
||||
let priceConstraint = null
|
||||
if( limitPrice.value ) {
|
||||
if( s.limitPrice ) {
|
||||
const inverted = routeInverted(route)
|
||||
const isAbove = limitIsMinimum.value ^ inverted
|
||||
const isAbove = s.limitIsMinimum ^ inverted
|
||||
const isRatio = false // todo ratios
|
||||
const decimals = 10 ** (s.tokenA.decimals - s.tokenB.decimals)
|
||||
const limit = inverted ? decimals/limitPrice.value : limitPrice.value/decimals
|
||||
priceConstraint = !limitPrice.value ? null : newLimitConstraint(isAbove, isRatio, limit)
|
||||
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
|
||||
console.log('tranche window', start, end, (end-start)/60)
|
||||
const cs = [newTimeConstraint(TimeMode.SinceOrderStart, start, TimeMode.SinceOrderStart, end)]
|
||||
if( priceConstraint !== null )
|
||||
cs.push(priceConstraint)
|
||||
|
||||
@@ -4,7 +4,7 @@ const known_chains = [
|
||||
{
|
||||
name: 'Arbitrum One',
|
||||
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()
|
||||
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) => {
|
||||
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()
|
||||
// todo put the vaultInitCodeHash into the chainInfo
|
||||
s.chainInfo = data.chainInfo
|
||||
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 network = await p.getNetwork()
|
||||
if (network !== null)
|
||||
|
||||
@@ -3,90 +3,123 @@ import { defineStore } from 'pinia'
|
||||
import {knownTokens} from "@/knownTokens.js";
|
||||
import {computed, ref} from "vue";
|
||||
|
||||
let rawProvider = null
|
||||
let rawProviderChainId = null
|
||||
|
||||
export function setProvider( provider, chainId ) {
|
||||
rawProvider = provider
|
||||
rawProviderChainId = chainId
|
||||
}
|
||||
export const useStore = defineStore('app', ()=> {
|
||||
const _chainId = ref(null)
|
||||
const _chainInfo = ref({})
|
||||
const tokenA = ref(null)
|
||||
const tokenB = ref(null)
|
||||
|
||||
export const useStore = defineStore('app', {
|
||||
state: () => ({
|
||||
chainId: null,
|
||||
chainInfo: {},
|
||||
vaultInitCodeHash: null,
|
||||
account: null,
|
||||
vaults: [],
|
||||
transactionSenders: [], // a list of function(signer) that send transactions
|
||||
errors: [
|
||||
// todo re-enable danger warning
|
||||
// {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}
|
||||
],
|
||||
extraTokens: {},
|
||||
poolPrices: {},
|
||||
vaultBalances: {}, // indexed by vault addr then by token addr. value is an int
|
||||
orders: {}, // indexed by vault, value is another dictionary with orderIndex as key and order status values
|
||||
|
||||
// Order Input Forms
|
||||
tokenA: null,
|
||||
tokenB: null,
|
||||
routes: [],
|
||||
routesPending: false,
|
||||
inverted: false,
|
||||
}),
|
||||
getters: {
|
||||
vault: (s)=>s.vaults.length===0 ? null : s.vaults[0],
|
||||
provider: (s)=>s.chainId===rawProviderChainId ? rawProvider : null,
|
||||
chain: (s)=> !s.chainInfo ? null : (s.chainInfo[s.chainId] || null),
|
||||
tokens: (s)=>{
|
||||
const chains = s.chainId in s.chainInfo && s.chainInfo[s.chainId].tokens !== undefined ? s.chainInfo[s.chainId].tokens : []
|
||||
let known = knownTokens[s.chainId]
|
||||
function getTokenList() {
|
||||
const chains = _chainId.value in _chainInfo.value && _chainInfo.value[_chainId.value].tokens !== undefined ?
|
||||
_chainInfo.value[_chainId.value].tokens : []
|
||||
let known = knownTokens[_chainId.value]
|
||||
known = known ? Object.values(known) : []
|
||||
let extras = s.extraTokens[s.chainId]
|
||||
let extras = extraTokens[_chainId.value]
|
||||
extras = extras ? Object.values(extras) : []
|
||||
return [...chains, ...known, ...extras]; // put chains first so the Mockcoin pool is automatically selected
|
||||
}
|
||||
function getTokens() {
|
||||
const result = {}
|
||||
const all = [...chains, ...known, ...extras]; // put chains first so the Mockcoin pool is automatically selected
|
||||
const all = getTokenList();
|
||||
for (const token of all)
|
||||
result[token.address] = token
|
||||
return result
|
||||
},
|
||||
factory: (s)=>!s.chain?null:s.chain.factory,
|
||||
helper: (s)=>!s.chain?null:s.chain.helper,
|
||||
mockenv: (s)=>!s.chain?null:s.chain.mockenv,
|
||||
mockCoins: (s)=>!s.chain?[]:!s.chain.mockCoins?[]:s.chain.mockCoins,
|
||||
route: (s)=>s.routes.length===0 ? null : s.routes[0],
|
||||
pairSymbol: (s)=>s.base?.symbol+'\\'+s.quote?.symbol,
|
||||
base: (s)=>{
|
||||
const token = s.inverted ? s.tokenB : s.tokenA
|
||||
}
|
||||
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
|
||||
// const tokenA = ref(null) // defined at top
|
||||
// const tokenB = ref(null)
|
||||
const buy = ref(false)
|
||||
const inverted = ref(false)
|
||||
const amount = ref(100) // todo
|
||||
const amountIsTokenA = ref(false) // todo
|
||||
const amountIsTotal = ref(true)
|
||||
const limitPrice = ref(null)
|
||||
const routes = ref([])
|
||||
const routesPending = ref(false)
|
||||
|
||||
const validOrder = computed(() => amount.value > 0 && routes.value.length > 0)
|
||||
const vault = computed(() => vaults.value.length === 0 ? null : vaults.value[0])
|
||||
const tokens = computed(getTokens)
|
||||
const factory = computed(() => !chain.value ? null : chain.value.factory)
|
||||
const helper = computed(() => !chain.value ? null : chain.value.helper)
|
||||
const mockenv = computed(() => !chain.value ? null : chain.value.mockenv)
|
||||
const mockCoins = computed(() => !chain.value ? [] : !chain.value.mockCoins ? [] : chain.value.mockCoins)
|
||||
const route = computed(() => routes.value.length === 0 ? null : routes.value[0])
|
||||
const base = computed(() => {
|
||||
const token = inverted.value ? tokenB.value : tokenA.value
|
||||
return !token ? {} : token
|
||||
},
|
||||
quote: (s)=> {
|
||||
const token = s.inverted ? s.tokenA : s.tokenB
|
||||
})
|
||||
const quote = computed(() => {
|
||||
const token = inverted.value ? tokenA.value : tokenB.value
|
||||
return !token ? {} : token
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
removeTransactionSender(sender) {
|
||||
})
|
||||
const pairSymbol = computed(() => base.value?.symbol + '\\' + quote.value?.symbol)
|
||||
const limitIsMinimum = computed(() => !(buy.value ^ inverted.value))
|
||||
const amountToken = computed(() => amountIsTokenA.value ? tokenA.value : tokenB.value)
|
||||
const amountIsInput = computed(() => amountIsTokenA.value !== buy.value)
|
||||
|
||||
function removeTransactionSender(sender) {
|
||||
this.transactionSenders = this.transactionSenders.filter((v) => v !== sender)
|
||||
},
|
||||
error(title, text, closeable=true) {
|
||||
}
|
||||
function error(title, text, closeable=true) {
|
||||
this.errors.push({title, text, closeable})
|
||||
},
|
||||
closeError(title, text) {
|
||||
}
|
||||
function closeError(title, text) {
|
||||
const result = []
|
||||
this.errors.forEach((i)=>{if(i.title!==title && i.text!==text) result.push(i)})
|
||||
this.errors = result
|
||||
},
|
||||
addToken(chainId, info) {
|
||||
this.$patch((s) => {
|
||||
let extras = s.extraTokens[chainId]
|
||||
}
|
||||
function addToken(chainId, info) {
|
||||
this.$patch(() => {
|
||||
let extras = extraTokens[chainId]
|
||||
if (extras === undefined) {
|
||||
extras = {}
|
||||
s.extraTokens[chainId] = extras
|
||||
extraTokens[chainId] = extras
|
||||
}
|
||||
extras[info.address] = info
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
chainId, chainInfo, chain, provider, vaultInitCodeHash, account, vaults, transactionSenders, errors, extraTokens,
|
||||
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