wallet flow; new faucet; placing chart orders works!

This commit is contained in:
Tim
2024-03-25 21:04:14 -04:00
parent 6ee442d7ec
commit d11ad7cf40
21 changed files with 444 additions and 201 deletions

View File

@@ -36,7 +36,7 @@ async function componentFindRoute() {
os.routes = []
if (!tokenA || !tokenB)
return
console.log('finding route', s.chainId.value, tokenA, tokenB)
console.log('finding route', s.chainId, tokenA, tokenB)
os.routesPending = true
try {
console.log('getting query helper')
@@ -44,7 +44,7 @@ async function componentFindRoute() {
if (!helper) {
console.log('no helper')
} else {
const result = await findRoute(helper, s.chainId.value, tokenA, tokenB)
const result = await findRoute(helper, s.chainId, tokenA, tokenB)
console.log('found route', result)
os.routes = result
}

View File

@@ -6,6 +6,8 @@ import {ethers} from "ethers";
// synchronous version may return null but will trigger a lookup
export function token(addr) {
// todo deprecated. use metadataMap[chainId][addr]
console.warn('token() is deprecated')
console.log('token', addr)
if( !addr ) {
// console.log('ignoring call to token', addr)
@@ -23,6 +25,8 @@ export function token(addr) {
// async version doesnt return until it has a token value
export async function getToken(addr) {
// todo deprecated. use metadataMap[chainId][addr]
console.warn('getToken() is deprecated')
const s = useStore()
if (!(addr in s.tokens))
await addExtraToken(addr)

View File

@@ -6,20 +6,26 @@ import {vaultAbi} from "@/blockchain/abi.js";
import {SingletonCoroutine} from "@/misc.js";
import {defineStore} from "pinia";
import {ref} from "vue";
import {metadata, metadataMap} from "@/version.js";
export const useWalletStore = defineStore('wallet', ()=>{
// this is what the wallet is logged into. it could be different than the application's store.chainId.
const chainId = ref(0)
// Pending Order Format
// {
// chainId: 31337, // must never be null, even if no wallet plugin exists. chosen by app, not wallet.
// placementTime: Date.now(),
// submitted: false // true after the order has started to be sent to the wallet
// tx: null // transaction ID
// vault: '0x...', // or null if account not logged in yet
// order: {tokenIn:..., tokenOut:..., ...} // blockchain binary order object
// }
const pendingOrders = ref([])
return {
pendingOrders,
chainId, pendingOrders,
}
})
@@ -27,27 +33,31 @@ export const useWalletStore = defineStore('wallet', ()=>{
export function onChainChanged(chainId) {
chainId = Number(chainId)
const store = useStore()
if( chainId !== store.chainId.value ) {
// todo check pending orders and confirm cancellation
const ws = useWalletStore()
if( chainId !== ws.chainId ) {
console.log('chain changed', chainId)
store.chainId.value = chainId
ws.chainId = chainId
if (chainId.toString() in metadataMap) {
console.log('app chain changed', chainId)
store.chainId = chainId
store.account = null
const provider = new ethers.BrowserProvider(window.ethereum, chainId);
store.provider = provider
provider.listAccounts().then((accounts)=>changeAccounts(chainId, accounts.map((a)=>a.address)))
}
}
}
function changeAccounts(chainId, accounts) {
// console.log('changeAccounts', chainId, accounts)
const store = useStore()
if( accounts.length === 0 ) {
console.log('account logged out')
store.account = null
store.vaults = []
store.vaultBalances = {}
export function updateAccounts(chainId, provider) {
provider.listAccounts().then((accounts) => changeAccounts(chainId, accounts.map((a) => a.address)))
}
else {
function changeAccounts(chainId, accounts) {
// this is a notification from the wallet that the user selected a different blockchain. that chain may or may not
// be supported. we store this value in walletStore.chainId, which may or may not be the same as
// the application's useStore().chainId
console.log('changeAccounts', chainId, accounts)
const store = useStore()
if (chainId === store.chainId && accounts.length) {
const addr = accounts[0]
console.log('account logged in', addr)
store.account = addr
@@ -55,12 +65,19 @@ function changeAccounts(chainId, accounts) {
flushTransactions()
socket.emit('address', chainId, addr)
}
else {
console.log('account logged out')
store.account = null
store.vaults = []
store.vaultBalances = {}
}
}
function onAccountsChanged(accounts) {
const store = useStore()
const ws = useWalletStore()
if (accounts.length === 0 || accounts[0] !== store.account)
changeAccounts(store.chainId.value, accounts);
changeAccounts(ws.chainId, accounts);
}
export function detectChain() {
@@ -105,8 +122,49 @@ const errorHandlingProxy = {
}
export async function connectProvider(chainId) {
console.log('connecting provider to chainId', chainId)
try {
return new ethers.BrowserProvider(window.ethereum, chainId)
}
catch (e) {
console.log('provider error', e)
}
return null
}
export async function connectWallet(chainId) {
await new ethers.BrowserProvider(window.ethereum, chainId).getSigner();
console.log('connectWallet', chainId)
const ws = useWalletStore()
if (ws.chainId !== chainId && !await switchChain(chainId))
return null
const p = await connectProvider(chainId)
if (p!==null) {
try {
return await p.getSigner();
}
catch (e) {
if (e.reason!=='rejected')
console.error(e, e.reason)
return null
}
}
}
export async function switchChain(chainId) {
if (useWalletStore().chainId === chainId)
return true
try {
await window.ethereum.request({
"method": "wallet_switchEthereumChain",
"params": [{"chainId": '0x' + chainId.toString(16)}]
})
return true
} catch (e) {
return false
}
}
@@ -123,7 +181,7 @@ const doDiscoverVaults = new SingletonCoroutine(_discoverVaults, 50, false)
async function _discoverVaults(owner) {
const result = []
const s = useStore()
if( !owner || !s.chainId.value || !s.account) {
if( !owner || !s.chainId || !s.account) {
s.vaults = []
return
}
@@ -159,7 +217,7 @@ async function _discoverVaults(owner) {
if( result.length )
flushOrders(result[0])
else
ensureVault2(s.chainId.value, owner, 0)
ensureVault2(s.chainId, owner, 0)
}
}
@@ -176,8 +234,8 @@ async function ensureVault1() {
const s = useStore()
const owner = s.account;
if (owner===null)
await connectWallet(s.chainId.value)
ensureVault2(s.chainId.value, owner, 0)
await connectWallet(s.chainId)
ensureVault2(s.chainId, owner, 0)
}
export function ensureVault2(chainId, owner, num) {
@@ -209,9 +267,10 @@ export async function pendOrder(order) {
console.log('order', JSON.stringify(order))
const s = useStore()
useWalletStore().pendingOrders.push({
chainId: s.chainId.value,
chainId: s.chainId,
placementTime: new Date(),
vault: s.vaults.length ? s.vaults[0] : null,
submitted: false,
order
})
ensureVault()
@@ -243,12 +302,17 @@ export async function cancelAll(vault) {
export function flushOrders(vault) {
const ws = useWalletStore();
let needsFlush = false
for( const order of ws.pendingOrders ) {
if (order.vault === null)
order.vault = vault
if (!order.submitted) {
pendOrderAsTransaction(order)
order.submitted = true
needsFlush = true
}
ws.pendingOrders = []
}
if (needsFlush)
flushTransactions()
}
@@ -260,8 +324,18 @@ function pendOrderAsTransaction(order) {
console.error('vault contract was null while sending order transaction', order.vault)
return null
}
if (!await switchChain(order.chainId)) {
console.log('user refused chain switch')
return null
}
console.log('placing order', order)
return await contract.placeOrder(order.order) // todo update status
const tx = await contract.placeOrder(order.order) // todo update status
order.tx = tx
tx.wait().then((txReceipt)=>{
const ws = useWalletStore();
ws.pendingOrders = ws.pendingOrders.filter((o)=>o!==order)
})
return tx
})
}
@@ -273,26 +347,15 @@ export function pendTransaction(sender) {
}
const flushTransactionsRoutine = new SingletonCoroutine(asyncFlushTransactions,1)
let flushing = 0 // semaphore
export function flushTransactions() {
flushing++
if( flushing === 1 )
// noinspection JSIgnoredPromiseFromCall
asyncFlushTransactions()
flushTransactionsRoutine.invoke()
}
export async function asyncFlushTransactions() {
let counter
do {
counter = flushing
await asyncFlushTransactions2()
} while( flushing > counter)
flushing = 0
}
export async function asyncFlushTransactions2() {
// todo rework into flushTransactions()
async function asyncFlushTransactions() {
const s = useStore()
if( s.provider === null ) {
console.log('warning: asyncFlushOrders() cancelled due to null provider')
@@ -319,22 +382,21 @@ export async function asyncFlushTransactions2() {
function doSendTransaction(sender, signer) {
const s = useStore();
sender(signer).then((tx)=>{
console.log('sent transaction', tx)
s.removeTransactionSender(sender)
sender(signer).then((tx)=>{
if (tx!==null) {
console.log('sent transaction', tx)
tx.wait().then((tr)=>console.log('tx receipt',tr))
}
}).catch((e)=>{
if( e.info?.error?.code === 4001 ) {
console.log(`user rejected transaction`)
s.removeTransactionSender(sender)
}
else {
if( e.reason && e.info )
console.error('error sending transaction', e.reason, e.info)
else
console.error('error sending transaction', e)
s.removeTransactionSender(sender)
// todo retry?
}
})
}

View File

@@ -152,7 +152,7 @@ function addSymbol(p, base, quote, inverted) {
async function getAllSymbols() {
if (_symbols===null) {
const chainId = useStore().chainId.value;
const chainId = useStore().chainId;
const md = metadata[chainId]
if(!md) {
console.log('could not get metadata for chain', chainId)
@@ -340,7 +340,7 @@ export const DataFeed = {
onResetCacheNeededCallback,
) => {
console.log('[subscribeBars]', symbolInfo, resolution, subscriberUID);
const chainId = useStore().chainId.value;
const chainId = useStore().chainId;
const poolAddr = useChartOrderStore().selectedPool[0];
const period = tvResolutionToPeriodString(resolution);
subscriptions[subscriberUID] = [chainId, poolAddr, period, onRealtimeCallback, onResetCacheNeededCallback]

View File

@@ -1,9 +1,9 @@
<template>
<v-btn :prepend-icon="icon" variant="plain" class="mx-2">
<v-btn :prepend-icon="icon" :variant="variant===undefined?'text':variant" class="mx-2">
<template v-slot:prepend>
<v-icon :color="color"></v-icon>
</template>
<slot name="text">{{text}}</slot>
<template v-slot>{{text}}<slot/></template>
</v-btn>
</template>
@@ -12,7 +12,12 @@ import {useStore} from "@/store/store";
import {useAttrs} from "vue";
const s = useStore()
const props = defineProps(['icon', 'color', 'text'])
const props = defineProps({
icon: {default:null},
color: {default:null},
text: {default:null},
variant: {default:'text'},
})
const attrs = useAttrs()
</script>

View File

@@ -1,9 +1,11 @@
<template>
<phone-card v-if="s.mockenv && s.vault" class="maxw">
<!-- <div>-->
<!--
<v-card-title><v-icon icon="mdi-faucet"/>&nbsp;Testnet Faucet</v-card-title>
<v-card-text>The Dexorder testnet faucet will send 1 TETH (Testnet ETH) to your account, plus 10 MEH (Mock Ethernet Hardfork) and 10,000 USXD (Joke Currency XD) to your vault.</v-card-text>
<v-card-text>Click below to get free test tokens: </v-card-text>
<v-card-item>
-->
<!--
<v-table plain>
<tbody>
@@ -14,9 +16,9 @@
</tbody>
</v-table>
-->
<btn icon='mdi-plus' color="green" :disabled="disabled" @click="gib">GIB!</btn>
</v-card-item>
</phone-card>
<btn icon='mdi-plus' color="green" :disabled="disabled" @click="gib" :text="text"/>
<!-- </v-card-item>-->
<!-- </div>-->
</template>
<script setup>
@@ -28,7 +30,11 @@ import {pendTransaction} from "@/blockchain/wallet.js";
import {mockErc20Abi} from "@/blockchain/abi.js";
import Btn from "@/components/Btn.vue";
import {socket} from "@/socket.js";
import {metadata} from "@/version.js";
const props = defineProps({
text: {default:'GIB!'},
})
const s = useStore()
/*
@@ -45,12 +51,37 @@ function gib(token) {
const disabled = ref(false)
const FAUCET_CONFIG = {
'': 1, // native coin
MEH: 1,
USXD: 1000,
WETH: 1,
ARB: 1000,
USDC: 1000,
}
function gib() {
const s = useStore()
if( s.account ) {
const chainId = s.chainId
const tokenAmounts = {}
const tmd = metadata[chainId].t // token metadata
for (const [symbol, amount] of Object.entries(FAUCET_CONFIG)) {
for (const t of tmd) {
if (t.s===symbol) {
if (t.x?.mock===true) {
tokenAmounts[t.a] = BigInt(Math.trunc(10 ** t.d * amount))
}
break
}
}
}
console.log('gib', s.chainId, s.account, s.vault, tokenAmounts )
if (Object.keys(tokenAmounts).length>0) {
disabled.value = true
socket.emit('faucet', s.chainId.value, s.account)
setTimeout(()=>disabled.value=false, 60*1000)
socket.emit('gib', s.chainId, s.account, s.vault, tokenAmounts )
setTimeout(()=>disabled.value=false, 1*1000) // todo disable longer
}
}
}

View File

@@ -1,24 +1,22 @@
<template>
<slot v-if="ok"/>
<phone-card v-if="!walletOk">
<v-card-title>Install Wallet</v-card-title>
<v-card-text>
<div v-if="!walletOk">
<h2>Install Wallet</h2>
<p>
A cryptocurrency wallet such as <a href="https://metamask.io/download/" target="MetaMask">MetaMask</a> is
required to use Dexorder. Please install a crypto wallet into your browser to experience the power of Dexorder.
</v-card-text>
<v-card-actions>
</p>
<p>
<v-btn prepend-icon="mdi-reload" text="Reload After Installing Wallet" @click="reload"/>
</v-card-actions>
</phone-card>
<phone-card v-if="walletOk && !providerOk">
<v-card-text>
</p>
</div>
<div v-if="walletOk && !providerOk">
<p>
Please log in to your crypto wallet.
</v-card-text>
<v-card-actions v-if="walletOk && !providerOk">
</p>
<v-btn prepend-icon="mdi-power" text="Connect Wallet" @click="connectWallet"/>
</v-card-actions>
</phone-card>
<phone-card v-if="walletOk && providerOk && !chainOk">
</div>
<div v-if="walletOk && chainOk && !providerOk">
<!-- todo Alpha Live
<v-card-title><v-icon icon="mdi-reload-alert" color="warning"/> Change Blockchain</v-card-title>
<v-card-text>
@@ -26,11 +24,14 @@
<blockchain chain-id="42161"/> blockchain in your wallet.
</v-card-text>
-->
<v-card-title><v-icon icon="mdi-hand-wave" color="warning"/>&nbsp;Welcome to Dexorder Alpha!</v-card-title>
<v-card-text>
This alpha test runs on a private blockchain, which you need to set up.
</v-card-text>
<v-card-item>
<h2><v-icon icon="mdi-hand-wave" color="warning"/>&nbsp;Welcome to Dexorder Alpha!</h2>
<p>
This alpha test runs on the Dexorder Testnet blockchain, which gives you free testnet tokens to trade.
</p>
<p>
<v-btn variant="tonal" @click="addChain">Setup Dexorder Testnet</v-btn>
</p>
<p>Manual Setup:</p>
<ol class="ml-6">
<li>Open Metamask</li>
<li>Click in the upper-left to choose a Network</li>
@@ -38,7 +39,7 @@
<li>Choose "Add a Network Manually"</li>
<li>Enter the following information:
<ul>
<li>Name: Dexorder Alpha</li>
<li>Name: Dexorder Alpha Testnet</li>
<li>New RPC URL: https://rpc.alpha.dexorder.trade</li>
<li>Chain ID: 1337</li>
<li>Currency Symbol: TETH</li>
@@ -51,15 +52,14 @@
Open Metamask again and select the "Dexorder Alpha" blockchain for use with this website.
</li>
</ol>
</v-card-item>
</phone-card>
</div>
</template>
<script setup>
import {useStore} from "@/store/store";
import PhoneCard from "@/components/PhoneCard.vue";
import {connectWallet} from "@/blockchain/wallet.js";
import {computed} from "vue";
import {computed, watch} from "vue";
import Blockchain from "@/components/Blockchain.vue";
const s = useStore()
@@ -67,11 +67,42 @@ const walletOk = typeof window.ethereum !== 'undefined'
const providerOk = computed(()=>s.provider!==null)
const chainOk = computed(()=>providerOk.value && s.helper!==null)
const ok = computed(()=>{
console.log('recompute provider ok')
return walletOk && providerOk.value && chainOk.value
})
function reload() {
window.location.reload()
}
async function addChain() {
await window.ethereum.request({
"method": "wallet_addEthereumChain",
"params": [
{
"chainId": "0x539",
"chainName": "Dexorder Alpha Testnet",
"rpcUrls": ["https://rpc.alpha.dexorder.trade"],
"nativeCurrency": {
"name": "Test Ethereum",
"symbol": "TETH",
"decimals": 18
}
}
]
});
}
watch([providerOk, chainOk], async () => {
console.log('checking chain')
if (walletOk && providerOk.value && !chainOk.value) {
console.log('switching chain')
const result = await window.ethereum.request({
"method": "wallet_switchEthereumChain",
"params": [{"chainId": '0x' + Object.keys(metadata)[0].toString(16)}]
});
console.log('chain switch result', result)
}
})
</script>
<style scoped lang="scss">

View File

@@ -1,29 +1,32 @@
<template>
<NeedsProvider>
<slot v-if="ok"/>
<phone-card v-if="!ok">
<v-card-title><v-icon icon="mdi-reload-alert" color="warning"/> Connect Wallet</v-card-title>
<v-card-text>
Please select an account to use from your wallet.
</v-card-text>
<v-card-actions>
<btn icon="mdi-wallet-outline" color="warning" @click="connectWallet">Connect Wallet</btn>
</v-card-actions>
</phone-card>
</NeedsProvider>
<needs-provider>
<slot v-if="ok" v-bind="$props"/>
<div v-if="!ok">
<btn icon="mdi-wallet-outline" color="warning" variant="outlined" @click="connect" :disabled="disabled" text="Connect Wallet"/>
</div>
</needs-provider>
</template>
<script setup>
import {useStore} from "@/store/store";
import NeedsProvider from "@/components/NeedsProvider.vue";
import {computed} from "vue";
import PhoneCard from "@/components/PhoneCard.vue";
import {computed, ref} from "vue";
import {connectWallet} from "@/blockchain/wallet.js";
import Btn from "@/components/Btn.vue";
const s = useStore()
const ok = computed(()=>s.account!==null)
const disabled = ref(false)
async function connect() {
disabled.value = true
try {
await connectWallet(s.chainId)
}
finally {
disabled.value = false
}
}
</script>

View File

@@ -40,7 +40,7 @@ const price = computed(()=>{
if( route.value ) {
subPrices([route.value])
subOHLCs( s.chainId.value, [[route.value.pool,'1D']])
subOHLCs( s.chainId, [[route.value.pool,'1D']])
}
else
console.log('route is empty: no price')

View File

@@ -22,8 +22,12 @@
</v-card-text>
</PhoneCard>
-->
<phone-card v-if="s.vaults.length<=num" class="maxw">
<v-card-title><v-icon icon="mdi-safe-square" size="small" color="grey-darken-1"/> Create a Dexorder Vault</v-card-title>
<div>
<div 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>
@@ -43,10 +47,15 @@
<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"/> Vault Assets {{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>
</div>
<div v-if="s.vaults.length>num" :class="empty?'maxw':''">
<v-card-title>
Your Deposit Address {{ 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>
@@ -56,8 +65,10 @@
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 testnet coins into your vault.</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 testnet coins
into your vault.</p>
</v-card-text>
<v-card-item v-if="!empty">
<v-table>
@@ -68,8 +79,9 @@
</tbody>
</v-table>
</v-card-item>
</v-card>
</div>
<withdraw :vault="addr" :token="withdrawToken" v-model="withdrawShow"/>
</div>
<!--
<div>
addr {{ addr }}<br/>

View File

@@ -0,0 +1,17 @@
<template>
<div class="d-flex flex-column h-100">
<toolbar title="Orders" icon="mdi-format-list-bulleted-square">
</toolbar>
<orders-view/>
</div>
</template>
<script setup>
import Toolbar from "@/components/chart/Toolbar.vue";
import OrdersView from "@/views/OrdersView.vue";
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,25 @@
<template>
<div class="d-flex flex-column h-100">
<toolbar title="Assets" icon="mdi-currency-btc"></toolbar>
<needs-signer>
<vault :owner="s.account" :num="0"/>
<faucet variant="outlined" text="Get Free Testnet Coins!" style="width: 15em"/>
</needs-signer>
</div>
</template>
<script setup>
import Toolbar from "@/components/chart/Toolbar.vue";
import Vault from "@/components/Vault.vue";
import NeedsSigner from "@/components/NeedsSigner.vue";
import Faucet from "@/components/Faucet.vue";
import {useStore} from "@/store/store.js";
const s = useStore()
</script>
<style scoped lang="scss">
</style>

View File

@@ -1,12 +1,21 @@
<template>
<div class="d-flex mb-1">
<div class="d-flex mb-1 align-center w-100">
<div class="d-flex align-end clickable" @click="$router.push('/place')">
<span class="arrow align-self-start"><v-icon icon="mdi-arrow-up-bold" :color="theme.colors.success"/></span>
<span class="logo">dexorder</span>
<v-chip text="ALPHA" size='x-small' color="red" class="align-self-start pr-6" variant="text"/>
</div>
<!--
<div class="d-flex align-center">
</div>
-->
<slot/>
<div class="w-100 d-flex justify-end">
<v-btn variant="text" icon="mdi-safe-square" color="grey-darken-2" text="Vault" @click="$router.push('/vault')"></v-btn>
<v-btn variant="text" icon="mdi-information-outline" text="Order Status" @click="$router.push('/orders')"></v-btn>
<div class="ml-auto d-flex align-center">
<!-- <v-icon :icon="icon" size="small"/>&nbsp;-->
<span class="title">{{title}}</span>
<toolbar-button icon="mdi-currency-btc" path="/vault"/>
<toolbar-button icon="mdi-format-list-bulleted-square" path="/orders"/>
<v-btn variant="text" icon="mdi-help-circle-outline" text="Info" @click="showCorp" disabled></v-btn>
</div>
</div>
</template>
@@ -19,21 +28,17 @@ import {timestamp} from "@/misc.js";
import {ShapeType} from "@/charts/shape.js";
import {computed, ref} from "vue";
import {useTheme} from "vuetify";
import ToolbarButton from "@/components/chart/ToolbarButton.vue";
const props = defineProps(['title', 'icon'])
const s = useStore()
const co = useChartOrderStore()
const showCancel = ref(false)
const theme = useTheme().current
function cancelOrder() {
showCancel.value = true
}
function placeOrder() {
function showCorp() {
window.open('https://dexorder.trade', 'dexorder')
}
</script>

View File

@@ -0,0 +1,15 @@
<template>
<v-btn :color="isCurrent?'primary':undefined" variant="text" :icon="icon" @click="$router.push(path)"/>
</template>
<script setup>
import {computed} from "vue";
import {useRoute} from "vue-router";
const props = defineProps(['icon', 'path'])
const isCurrent = computed(() => useRoute().path === props.path)
</script>
<style scoped lang="scss">
</style>

View File

@@ -11,7 +11,7 @@ const routes = [
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "chartorder" */ '@/components/chart/ChartOrderPane.vue'),
component: () => import(/* webpackChunkName: "chartorder" */ '@/components/chart/ChartPlaceOrder.vue'),
},
{
path: '/place',
@@ -19,7 +19,7 @@ const routes = [
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "chartorder" */ '@/components/chart/ChartOrderPane.vue'),
component: () => import(/* webpackChunkName: "chartorder" */ '@/components/chart/ChartPlaceOrder.vue'),
},
{
path: '/vault',
@@ -27,7 +27,7 @@ const routes = [
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "vaultview" */ '@/views/VaultView.vue'),
component: () => import(/* webpackChunkName: "vaultview" */ '@/components/chart/ChartVault.vue'),
},
{
path: '/orders',
@@ -35,13 +35,13 @@ const routes = [
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "ordersview" */ '@/views/OrdersView.vue'),
component: () => import(/* webpackChunkName: "ordersview" */ '@/components/chart/ChartOrders.vue'),
},
/*
{
path: '/create',
name: 'Create',
component: () => import(/!* webpackChunkName: "chartorder" *!/ '@/components/chart/ChartOrderPane.vue'),
component: () => import(/!* webpackChunkName: "chartorder" *!/ '@/components/chart/ChartPlaceOrder.vue'),
},
{
path: '/twap',

View File

@@ -17,7 +17,7 @@ socket.on('disconnect', () => {
socket.on('p', async (chainId, pool, price) => {
console.log('pool price from message', chainId, pool, price)
const s = useStore()
if( s.chainId.value !== chainId )
if( s.chainId !== chainId )
return
s.poolPrices[[chainId,pool]] = price
})
@@ -29,7 +29,7 @@ socket.on('ohlc', async (chainId, pool, ohlcs) => {
socket.on('vb', async (chainId, vault, balances) => {
const s = useStore()
if( s.chainId.value !== chainId )
if( s.chainId !== chainId )
return
console.log('vb', vault, balances)
s.vaultBalances[vault] = JSON.parse(balances)
@@ -39,7 +39,7 @@ socket.on('vb', async (chainId, vault, balances) => {
socket.on('vaults', (chainId, owner, vaults)=>{
const s = useStore()
console.log('vaults', vaults)
if( s.chainId.value !== chainId || s.account !== owner )
if( s.chainId !== chainId || s.account !== owner )
return
if( vaults.length > s.vaults.length ) {
s.vaults = vaults
@@ -53,7 +53,7 @@ socket.on('vaults', (chainId, owner, vaults)=>{
function handleOrderStatus(chainId, vault, orderIndex, status) {
const s = useStore()
if( s.chainId.value !== chainId )
if( s.chainId !== chainId )
return
// message 'o' is a single order status
const parsed = parseOrderStatus(status);
@@ -74,7 +74,7 @@ socket.on( 'o', handleOrderStatus)
socket.on( 'of', (chainId, vault, orderIndex, filled)=>{
const s = useStore()
if( s.chainId.value !== chainId )
if( s.chainId !== chainId )
return
console.log('of', chainId, vault, orderIndex, filled)
if( !(vault in s.orders) ) {

View File

@@ -3,6 +3,8 @@ import {defineStore} from 'pinia'
import {knownTokens} from "@/knownTokens.js";
import {computed, ref} from "vue";
import {version} from "@/version.js";
import {ethers} from "ethers";
import {updateAccounts} from "@/blockchain/wallet.js";
// USING THE STORE:
//
@@ -31,11 +33,15 @@ function timestamp() {
return Math.round(new Date().getTime() / 1000)
}
const UNKNOWN_PROVIDER = {}
export const useStore = defineStore('app', ()=> {
const clock = ref(timestamp())
const nav = ref(false) // controls opening navigation drawer
const _chainId = ref(Object.keys(version.chainInfo)[0])
const _chainId = ref(Number(Object.keys(version.chainInfo)[0]))
const _chainInfo = ref(version.chainInfo)
function getTokenList() {
@@ -54,25 +60,42 @@ export const useStore = defineStore('app', ()=> {
result[token.address] = token
return result
}
function refreshChain() {
useOrderStore().setDefaultTokens(getTokenList())
}
const chainId = computed({
get() {return _chainId},
set(v) {_chainId.value=v; refreshChain()}
get() {return _chainId.value},
set(v) {
console.log('setting chainid',_chainId.value, v)
if (_chainId.value!==v) {
console.log('do set')
_chainId.value = v
account.value = null
}
if (_chainId.value!==v || _provider.value === null) {
console.log('update provider')
_provider.value = UNKNOWN_PROVIDER
}
}
})
const chainInfo = computed({
get() {return _chainInfo},
set(v) {_chainInfo.value=v; refreshChain()}
get() {return _chainInfo.value},
set(v) {_chainInfo.value=v}
})
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}
// this provider is for the app's chainId not the wallet's chainId.
let _provider = UNKNOWN_PROVIDER // null indicates a known-unavailable provider whereas undefined means provider status is unknown
const provider = computed(()=>{
if (_provider===UNKNOWN_PROVIDER) {
// unknown provider status
try {
_provider = new ethers.BrowserProvider(window.ethereum, _chainId.value);
updateAccounts(_chainId.value, _provider)
}
catch (e) {
console.error('could not get provider', _chainId.value, e)
_provider = null
}
}
// if _provider is null then it is known to be an unavailable chain
return _provider
})
const vaultInitCodeHash = computed(() => !chain.value ? null : chain.value.vaultInitCodeHash)
const account = ref(null)

View File

@@ -7,6 +7,12 @@
font-size: 24px;
}
.title {
font-family: v.$heading-font-family;
font-weight: 500;
font-size: 21px;
}
.clickable {
:hover {
cursor: pointer;

View File

@@ -25,6 +25,6 @@ for (const [chain, info] of Object.entries(metadata)) {
map[poolMeta.a] = poolMeta
for (const tokenMeta of info.t)
map[tokenMeta.a] = tokenMeta
metadataMap[chain] = map
metadataMap[Number(chain)] = map
}
console.log('metadataMap', metadataMap)

View File

@@ -1,8 +1,11 @@
<template>
<!-- todo needs account -->
<needs-signer>
<phone-card class="maxw">
<vault :owner="s.account" :num="0"/>
</phone-card>
<phone-card v-if="s.mockenv && s.vault" class="maxw">
<faucet class="mt-3"/>
</phone-card>
<new-order class="my-6"/>
</needs-signer>
</template>
@@ -14,6 +17,7 @@ import NeedsSigner from "@/components/NeedsSigner.vue";
import Faucet from "@/components/Faucet.vue";
import NeedsProvider from "@/components/NeedsProvider.vue";
import NewOrder from "@/components/NewOrder.vue";
import PhoneCard from "@/components/PhoneCard.vue";
const s = useStore()