order status updates working
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
"name": "dexorder",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
|
||||
11
src/blockchain/common.js
Normal file
11
src/blockchain/common.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export function applyFills( orderStatus, filled ) {
|
||||
console.log('apply fills', orderStatus, filled)
|
||||
orderStatus[4] = filled[0][0]
|
||||
orderStatus[5] = filled[0][1]
|
||||
for( const i in filled[1] ) {
|
||||
const {filledIn, filledOut} = filled[1][i]
|
||||
orderStatus[6][i] = filledIn
|
||||
orderStatus[7][i] = filledOut
|
||||
}
|
||||
console.log('applied fills', orderStatus)
|
||||
}
|
||||
@@ -3,6 +3,19 @@ import {useStore} from "@/store/store.js";
|
||||
import {erc20Abi} from "@/blockchain/abi.js";
|
||||
import {ethers} from "ethers";
|
||||
|
||||
|
||||
// synchronous version may return null but will trigger a lookup
|
||||
export function token(addr) {
|
||||
const s = useStore()
|
||||
if( !(addr in s.tokens) ) {
|
||||
getToken(addr)
|
||||
return null
|
||||
}
|
||||
return s.tokens[addr]
|
||||
}
|
||||
|
||||
|
||||
// async version doesnt return until it has a token value
|
||||
export async function getToken(addr) {
|
||||
const s = useStore()
|
||||
if (!(addr in s.tokens))
|
||||
|
||||
@@ -88,14 +88,27 @@ export async function connectWallet() {
|
||||
|
||||
const pendingOrders = []
|
||||
|
||||
|
||||
export function ensureVault() {
|
||||
const s = useStore()
|
||||
if( !s.chain ) {
|
||||
console.log('cannot create vault: no chain selected')
|
||||
return
|
||||
}
|
||||
if( !s.account ) {
|
||||
console.log('cannot create vault: no account logged in')
|
||||
return
|
||||
}
|
||||
socket.emit('ensureVault', s.chainId, s.account, 0)
|
||||
}
|
||||
|
||||
|
||||
export async function pendOrder(order) {
|
||||
console.log('order', JSON.stringify(order))
|
||||
const s = useStore()
|
||||
const signer = await s.provider.getSigner()
|
||||
if (!s.vaults.length) {
|
||||
pendingOrders.push(order)
|
||||
const owner = await signer.getAddress();
|
||||
socket.emit('ensureVault', s.chainId, owner, 0)
|
||||
ensureVault()
|
||||
}
|
||||
else {
|
||||
const vault = s.vaults[0];
|
||||
|
||||
20
src/components/Btn.vue
Normal file
20
src/components/Btn.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<v-btn variant="outlined">
|
||||
<v-icon v-if="icon" :icon="icon" :color="color"></v-icon>
|
||||
<slot/>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store";
|
||||
import {useAttrs} from "vue";
|
||||
|
||||
const s = useStore()
|
||||
const props = defineProps(['icon', 'color'])
|
||||
const attrs = useAttrs()
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "src/styles/vars" as *;
|
||||
</style>
|
||||
@@ -2,22 +2,121 @@
|
||||
<v-table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>#</th>
|
||||
<th>Buy Token</th>
|
||||
<th>Sell Token</th>
|
||||
<th>Amount</th>
|
||||
<th>Filled</th>
|
||||
<th>Remaining</th>
|
||||
<th>Average Price</th>
|
||||
<th> </th> <!-- actions -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="order in orders">
|
||||
<td></td>
|
||||
<tr v-for="[index, inTokenAddr, outTokenAddr, amount, amountTokenAddr, filled, avgPrice] in orders">
|
||||
<td>{{parseInt(index)+1}}</td>
|
||||
<td>{{tokenSymbol(inTokenAddr)}}</td>
|
||||
<td>{{tokenSymbol(outTokenAddr)}}</td>
|
||||
<td>{{tokenAmount(amountTokenAddr, amount)}}</td>
|
||||
<td>{{tokenAmount(amountTokenAddr, filled)}}</td>
|
||||
<td>{{tokenAmount(amountTokenAddr, amount-filled)}}</td>
|
||||
<td>
|
||||
{{pairPrice(inTokenAddr, outTokenAddr, vaultAddr, index, avgPrice)}}
|
||||
<btn v-if="pairPrice(inTokenAddr, outTokenAddr, vaultAddr, index, avgPrice)!==''" size="small"
|
||||
@click="inverted[[vaultAddr,index]] = !inverted[[vaultAddr,index]]">
|
||||
{{pair(inTokenAddr, outTokenAddr, vaultAddr,index)}}
|
||||
</btn>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {FixedNumber} from "ethers";
|
||||
import {useStore} from "@/store/store";
|
||||
import {computed, reactive} from "vue";
|
||||
import {token} from "@/blockchain/token.js";
|
||||
import Btn from "@/components/Btn.vue"
|
||||
|
||||
const s = useStore()
|
||||
const orders = 0; // todo tim here
|
||||
const props = defineProps(['vault'])
|
||||
const vaultAddr = computed(()=>props.vault?props.vault:s.vault)
|
||||
const inverted = reactive({})
|
||||
|
||||
function tokenSymbol(addr) {
|
||||
const t = token(addr)
|
||||
return t ? t.symbol : addr
|
||||
}
|
||||
|
||||
function tokenAmount(tokenAddr, amount) {
|
||||
const t = token(tokenAddr)
|
||||
if( !t )
|
||||
return ''
|
||||
const amt = FixedNumber.fromValue(amount, t.decimals, {width:256, decimals:t.decimals, signed:false})
|
||||
return `${amt} ${t.symbol}`
|
||||
}
|
||||
|
||||
// todo create a Price component that keeps inversion flags in the store and defaults to stablecoins as the quote
|
||||
function pairPrice(inTokenAddr, outTokenAddr, vaultAddr, index, price) {
|
||||
if( price === null )
|
||||
return ''
|
||||
const inToken = token(inTokenAddr)
|
||||
const outToken = token(outTokenAddr)
|
||||
if( !inToken || !outToken )
|
||||
return ''
|
||||
|
||||
console.log('inout tokens', inToken, outToken)
|
||||
console.log('price1', price, outToken.decimals, inToken.decimals)
|
||||
const decimals = outToken.decimals-inToken.decimals
|
||||
if( decimals > 0 )
|
||||
price /= 10 ** decimals
|
||||
else
|
||||
price *= 10 ** -decimals
|
||||
console.log('price2', price)
|
||||
const invertedKey = [vaultAddr,index];
|
||||
if( !(invertedKey in inverted) ) {
|
||||
// todo prefer stablecoins as the quote asset
|
||||
inverted[invertedKey] = false
|
||||
}
|
||||
const inv = inverted[invertedKey]
|
||||
return inv ? (1 / price).toPrecision(5) : price.toPrecision(5)
|
||||
}
|
||||
|
||||
function pair(inTokenAddr, outTokenAddr, vaultAddr, index) {
|
||||
const inToken = token(inTokenAddr)
|
||||
const outToken = token(outTokenAddr)
|
||||
return !inToken || !outToken ? null : inverted[[vaultAddr,index]] ?
|
||||
outToken.symbol+'/'+inToken.symbol : inToken.symbol+'/'+outToken.symbol
|
||||
}
|
||||
|
||||
const orders = computed(()=>{
|
||||
if( !(vaultAddr.value in s.orders) )
|
||||
return {}
|
||||
const result = []
|
||||
for( const [index,status] of Object.entries(s.orders[vaultAddr.value]) ) {
|
||||
console.log('order status', status)
|
||||
// [index, symbolA, symbolB, amount, amountSymbol, filled]
|
||||
const inTokenAddr = status[0][0]
|
||||
const outTokenAddr = status[0][1]
|
||||
const amountIsInput = !!(status[0][4])
|
||||
const amountTokenAddr = amountIsInput ? inTokenAddr : outTokenAddr
|
||||
console.log('getamount', status[0][3])
|
||||
const amount = status[0][3]
|
||||
console.log('amount', amount)
|
||||
console.log('getfilled', amountIsInput ? status[4] : status[5])
|
||||
const filled = amountIsInput ? status[4] : status[5]
|
||||
console.log('filled', filled)
|
||||
const amountIn = BigInt(status[4])
|
||||
const amountOut = BigInt(status[5])
|
||||
const fmtX18 = {decimals:18, width: 256, signed:false};
|
||||
const avg = !amountIn || !amountOut ? null :
|
||||
FixedNumber.fromValue(status[5],0, fmtX18).div(FixedNumber.fromValue(status[4], 0, fmtX18))
|
||||
result.push([index, inTokenAddr, outTokenAddr, amount, amountTokenAddr, filled, avg])
|
||||
}
|
||||
console.log('result', result)
|
||||
return result
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ function placeOrder() {
|
||||
window = duration
|
||||
duration *= n // duration is the total time for all tranches
|
||||
} else {
|
||||
window = duration / n
|
||||
window = Math.round(duration / n)
|
||||
}
|
||||
const oneHundredPercent = 65535n // by contract definition of uint16 fraction
|
||||
const ceil = oneHundredPercent % BigInt(n) ? 1n : 0n
|
||||
|
||||
@@ -23,18 +23,40 @@
|
||||
</v-card-text>
|
||||
</PhoneCard>
|
||||
-->
|
||||
<v-card v-if="s.vaults.length<=num">
|
||||
<v-card-title><v-icon icon="mdi-safe-square-outline" size="small" color="red"/> No Vault Yet</v-card-title>
|
||||
<phone-card v-if="s.vaults.length<=num">
|
||||
<v-card-title><v-icon icon="mdi-safe-square" size="small" color="grey-darken-1"/> Create a Dexorder Vault</v-card-title>
|
||||
<v-card-text v-if="num!==0"><!--todo-->Multiple vaults are not yet supported</v-card-text>
|
||||
<!-- todo restore the vault-on-order approach for public beta
|
||||
<v-card-text v-if="num===0">Create an order first, then your vault account will appear here to accept a deposit of trading funds.</v-card-text>
|
||||
<v-card-actions><v-btn prepend-icon="mdi-plus" text="Create Order" @click="$router.push('/twap')"/></v-card-actions>
|
||||
</v-card>
|
||||
<v-card v-if="s.vaults.length>num">
|
||||
<v-card-title><v-icon icon="mdi-safe-square-outline" size="small"/> My Vault {{s.vaults.length>1?'#'+(num+1):''}}</v-card-title> <!-- todo vault nicknames -->
|
||||
-->
|
||||
<!-- User-actioned but dexorder executed
|
||||
<v-card-text v-if="num===0">
|
||||
Your vault is a smart contract that securely holds your funds plus any orders you place. When your order
|
||||
conditions are met, Dexorder creates a blockchain transaction for <code>vault.execute()</code>,
|
||||
asking your vault to the order. Your vault then checks that order against the
|
||||
current blockchain time and pool price, and only trades if everything looks good.
|
||||
|
||||
Start placing dexorders by clicking the button below to create your own personal Vault!
|
||||
</v-card-text>
|
||||
<v-card-actions><btn icon="mdi-safe-square" color="grey-darken-1" text="Create Vault" @click="ensureVault"/></v-card-actions>
|
||||
-->
|
||||
<v-card-text v-if="num===0">
|
||||
Please wait while your vault is being created. This should only take a few seconds.
|
||||
</v-card-text>
|
||||
</phone-card>
|
||||
<v-card v-if="s.vaults.length>num" :class="empty?'maxw':''">
|
||||
<v-card-title><v-icon icon="mdi-safe-square" color="grey-darken-2" size="small"/> My Vault {{s.vaults.length>1?'#'+(num+1):''}}</v-card-title> <!-- todo vault nicknames -->
|
||||
<v-card-subtitle v-if="exists" class="overflow-x-hidden"><copy-button :text="addr"/>{{addr}}</v-card-subtitle>
|
||||
<v-card-text v-if="empty">
|
||||
<p>There are no funds currently in your vault.</p>
|
||||
<p>Send tokens to the address above to fund your vault.</p>
|
||||
<p>
|
||||
Your vault is a smart contract that securely holds your funds plus any orders you place. When your order
|
||||
conditions are met, Dexorder creates a blockchain transaction for <code>vault.execute()</code>,
|
||||
asking your vault to the order. Your vault then checks that order against the
|
||||
current blockchain time and pool price, and only trades if everything looks good.
|
||||
</p>
|
||||
<p v-if="!s.mockenv">There are no funds currently in your vault. Send tokens to the address above to fund your vault.</p>
|
||||
<p v-if="s.mockenv">There are no funds currently in your vault. Use the faucet below to mint some fake coins into your vault.</p>
|
||||
</v-card-text>
|
||||
<v-card-item v-if="!empty">
|
||||
<v-table>
|
||||
@@ -61,9 +83,12 @@
|
||||
import {useStore} from "@/store/store.js";
|
||||
import {computed, defineAsyncComponent, ref} from "vue";
|
||||
import {vaultAddress} from "@/blockchain/contract.js";
|
||||
import {ensureVault} from "@/blockchain/wallet.js";
|
||||
import CopyButton from "@/components/CopyButton.vue";
|
||||
import NeedsProvider from "@/components/NeedsProvider.vue";
|
||||
import Withdraw from "@/components/Withdraw.vue";
|
||||
import PhoneCard from "@/components/PhoneCard.vue";
|
||||
import Btn from "@/components/Btn.vue";
|
||||
|
||||
const TokenRow = defineAsyncComponent(()=>import('./TokenRow.vue'))
|
||||
const s = useStore()
|
||||
@@ -86,8 +111,16 @@ function onWithdraw(addr) {
|
||||
withdrawToken.value = token
|
||||
withdrawShow.value = true
|
||||
}
|
||||
|
||||
// todo remove automatic vault creation for Alpha 2
|
||||
if(props.num===0 && !s.vault)
|
||||
ensureVault()
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "../styles/vars" as *;
|
||||
p {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<v-chip text="ALPHA" size="x-small" color="red" class="mx-1"/>
|
||||
</v-app-bar-title>
|
||||
|
||||
<v-btn icon="mdi-safe-square-outline" text="Vault" @click="$router.push('/vault')"></v-btn>
|
||||
<v-btn icon="mdi-safe-square" color="grey-darken-2" text="Vault" @click="$router.push('/vault')"></v-btn>
|
||||
<v-btn icon="mdi-swap-horizontal-circle-outline" text="New Order" @click="$router.push('/twap')"></v-btn>
|
||||
<v-btn icon="mdi-menu" text="Order Status" @click="$router.push('/orders')"></v-btn>
|
||||
|
||||
|
||||
@@ -12,19 +12,29 @@ import 'vuetify/styles'
|
||||
import { createVuetify } from 'vuetify'
|
||||
|
||||
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
||||
const black = '#333'
|
||||
export default createVuetify({
|
||||
theme: {
|
||||
themes: {
|
||||
light: {
|
||||
colors: {
|
||||
background: '#fffefd',
|
||||
surface: '#fffefd',
|
||||
primary: '#1A6CAB',
|
||||
background: '#fdfffe',
|
||||
surface: '#fdfffe',
|
||||
// primary: '#1A6CAB',
|
||||
primary: '#00CC33',
|
||||
// secondary: '#59B8FF',
|
||||
success: '#00CC33',
|
||||
// info: '#fba92c',
|
||||
warning: '#ffcc00',
|
||||
error: '#CC0033',
|
||||
"on-background": black,
|
||||
"on-surface": black,
|
||||
"on-primary": black,
|
||||
"on-secondary": black,
|
||||
"on-success": black,
|
||||
"on-info": black,
|
||||
"on-warning": black,
|
||||
"on-error": black,
|
||||
},
|
||||
dark: false,
|
||||
variables: {},
|
||||
|
||||
@@ -2,6 +2,7 @@ import {io} from "socket.io-client";
|
||||
import {useStore} from "@/store/store.js";
|
||||
import {flushOrders, onChainChanged} from "@/blockchain/wallet.js";
|
||||
import {ethers} from "ethers";
|
||||
import {applyFills} from "@/blockchain/common.js";
|
||||
|
||||
export const socket = io(import.meta.env.VITE_WS_URL || undefined, {transports: ["websocket"]})
|
||||
|
||||
@@ -59,16 +60,42 @@ socket.on('vaults', (chainId, owner, vaults)=>{
|
||||
}
|
||||
})
|
||||
|
||||
socket.on( 'o', (chainId, vault, orderIndex, status)=>{
|
||||
|
||||
function handleOrderStatus(chainId, vault, orderIndex, status) {
|
||||
const s = useStore()
|
||||
if( s.chainId !== chainId )
|
||||
return
|
||||
console.log('o', chainId, vault, orderIndex, status)
|
||||
const orders = s.orders
|
||||
if( !(vault in orders) )
|
||||
orders[vault] = {}
|
||||
orders[vault][orderIndex] = status
|
||||
s.orders = orders
|
||||
}
|
||||
|
||||
socket.on('os', (chainId, vault, orders) => {
|
||||
console.log('os', orders)
|
||||
for( const [orderIndex, status] of orders )
|
||||
handleOrderStatus(chainId, vault, orderIndex, status)
|
||||
})
|
||||
|
||||
socket.on( 'o', handleOrderStatus)
|
||||
|
||||
socket.on( 'of', (chainId, vault, orderIndex, fills)=>{
|
||||
const s = useStore()
|
||||
if( s.chainId !== chainId )
|
||||
return
|
||||
console.log('of', chainId, vault, orderIndex, fills)
|
||||
const orders = s.orders
|
||||
if( !(vault in orders) ) {
|
||||
console.log('warning: got fill on an order in an unknown vault')
|
||||
return
|
||||
}
|
||||
if( !(orderIndex in orders[vault]) ) {
|
||||
console.log(`warning: orderIndex ${orderIndex} missing from vault ${vault}`)
|
||||
return
|
||||
}
|
||||
const order = orders[vault][orderIndex]
|
||||
applyFills(order, fills)
|
||||
orders[vault][orderIndex] = order
|
||||
})
|
||||
|
||||
@@ -25,6 +25,7 @@ export const useStore = defineStore('app', {
|
||||
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
|
||||
}),
|
||||
getters: {
|
||||
vault: (s)=>s.vaults.length===0 ? null : s.vaults[0],
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
.v-text-field.text-end input {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.maxw {
|
||||
max-width: v.$card-maxw;
|
||||
}
|
||||
}
|
||||
|
||||
.uniswap-color {
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
<template>
|
||||
<orders/>
|
||||
<needs-provider>
|
||||
<needs-signer>
|
||||
<orders/>
|
||||
</needs-signer>
|
||||
</needs-provider>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Orders from "@/components/Orders.vue";
|
||||
import NeedsSigner from "@/components/NeedsSigner.vue";
|
||||
import NeedsProvider from "@/components/NeedsProvider.vue";
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
Reference in New Issue
Block a user