native coin handling
This commit is contained in:
@@ -61,8 +61,7 @@ export function updateAccounts(chainId, provider) {
|
||||
|
||||
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
|
||||
// be supported.
|
||||
console.log('changeAccounts', chainId, accounts)
|
||||
const store = useStore()
|
||||
if (chainId === store.chainId && accounts.length) {
|
||||
|
||||
@@ -124,6 +124,7 @@ export class AbiURLCache extends AsyncURLCache {
|
||||
const files = {
|
||||
// If a contract is in a file different than its name, put the exception here
|
||||
// 'IVaultImpl' : 'IVault', // for example
|
||||
'IERC20Metadata' : 'interfaces/IERC20Metadata',
|
||||
}
|
||||
|
||||
export function abiPath(name) {
|
||||
|
||||
46
src/components/NativeRow.vue
Normal file
46
src/components/NativeRow.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<!-- like TokenRow but for native currency -->
|
||||
<template>
|
||||
<tr>
|
||||
<td>
|
||||
<v-avatar image="/media/ethereum-eth-logo-diamond-purple-64.png" size="x-small"/>
|
||||
</td>
|
||||
<td class="d-none d-sm-table-cell">Ether
|
||||
<small v-if="amount!==0n">
|
||||
<v-icon icon="mdi-alert" color="warning" size="x-small"/>
|
||||
Must <a @click="onWrap" class="clickable">wrap</a> before trading
|
||||
<v-icon icon="mdi-alert" color="warning" size="x-small"/>
|
||||
</small>
|
||||
</td>
|
||||
<td class="text-right">{{ fixed }}</td>
|
||||
<td class="text-left">ETH</td>
|
||||
<!-- todo price and value columns -->
|
||||
<td>
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn variant="plain" v-bind="props" icon="mdi-dots-vertical"/>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-subheader title="ETH"/>
|
||||
<v-list-item title="Wrap" key="wrap" value="wrap" prepend-icon="mdi-location-enter" @click="onWrap"/>
|
||||
<v-list-item title="Withdraw" key="withdraw" value="withdraw" prepend-icon="mdi-arrow-down-bold"
|
||||
@click="()=>onWithdraw()"/>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store";
|
||||
import {FixedNumber} from "ethers";
|
||||
import {computed} from "vue";
|
||||
|
||||
const s = useStore()
|
||||
const props = defineProps(['chainId', 'addr', 'amount', 'onWithdraw', 'onWrap'])
|
||||
const fixed = computed(() => props.amount===null ? '…' : FixedNumber.fromValue(props.amount, 18, {width: 256, decimals: 18}))
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "src/styles/vars" as *;
|
||||
|
||||
</style>
|
||||
60
src/components/NativeWrap.vue
Normal file
60
src/components/NativeWrap.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<v-dialog :model-value="modelValue" @update:modelValue="$emit('update:modelValue', $event)">
|
||||
<v-card style="max-width: 25em" class="mx-auto">
|
||||
<v-card-title>
|
||||
<v-icon icon="mdi-location-enter" color="grey-darken-1"/> Wrap ETH into WETH
|
||||
</v-card-title>
|
||||
<v-card-item>
|
||||
<v-text-field class="text-end" type="number" variant="outlined" :min="0" :max="balanceFloat"
|
||||
v-model="floatAmount" :step="balanceFloat/10">
|
||||
<template v-slot:prepend-inner>
|
||||
<v-btn variant="text" text="max" @click="floatAmount=balanceFloat"/>
|
||||
</template>
|
||||
<template v-slot:append-inner>
|
||||
<span>ETH</span>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<v-card-actions>
|
||||
<v-btn text="Cancel" @click="$emit('update:modelValue', false)"/>
|
||||
<v-btn text="Wrap" color="red" @click="wrapNative"/>
|
||||
</v-card-actions>
|
||||
</v-card-item>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store";
|
||||
import {computed, ref} from "vue";
|
||||
import {vaultContract} from "@/blockchain/contract.js"
|
||||
import {pendTransaction} from "@/blockchain/wallet.js";
|
||||
import {FixedNumber} from "ethers";
|
||||
|
||||
const s = useStore()
|
||||
const props = defineProps(['modelValue', 'vault', 'maxAmount'])
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const balanceFloat = computed(() => Number(props.maxAmount)/1e18)
|
||||
const floatAmount = ref(0)
|
||||
|
||||
function wrapNative() {
|
||||
const vaultAddr = props.vault
|
||||
const valueStr = floatAmount.value.toString();
|
||||
const amount = floatAmount.value === balanceFloat.value ? s.nativeBalance : // maximum
|
||||
FixedNumber.fromString(valueStr,{decimals:18, width:256, signed: false}).value;
|
||||
if( amount === 0n )
|
||||
return
|
||||
console.log('pending wrap', valueStr, amount)
|
||||
pendTransaction(async (signer)=>{
|
||||
const vault = await vaultContract(vaultAddr, signer)
|
||||
return await vault.wrap(amount)
|
||||
})
|
||||
floatAmount.value = 0
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "src/styles/vars" as *;
|
||||
|
||||
</style>
|
||||
@@ -117,8 +117,8 @@
|
||||
<td class="d-flex align-center text-left">
|
||||
<!-- todo describe rate limits -->
|
||||
<div class="text-right">
|
||||
<div class="mx-3">{{ describeTrancheTime(item, true, t) }}</div>
|
||||
<div class="mx-3">{{ describeTrancheTime(item, false, t) }}</div>
|
||||
<div class="mx-3">{{ describeTrancheTime(item, i, true) }}</div>
|
||||
<div class="mx-3">{{ describeTrancheTime(item, i, false) }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mx-3" v-if="t.marketOrder">market order</div>
|
||||
@@ -274,16 +274,17 @@ const orders = computed(()=>{
|
||||
return result
|
||||
})
|
||||
|
||||
function describeTrancheTime(st, isStart, t) {
|
||||
function describeTrancheTime(st, trancheIndex, isStart) {
|
||||
const t = st.order.tranches[trancheIndex]
|
||||
const ts = st.trancheStatus[trancheIndex]
|
||||
let result = ''
|
||||
if( t.minIsBarrier || t.maxIsBarrier )
|
||||
return 'barrier'
|
||||
const now = Math.round(Date.now()/1000)
|
||||
if( isStart && t.startTime > 0 ) {
|
||||
const start = t.startTimeIsRelative ? st.startTime + t.startTime : t.startTime
|
||||
result += now < start ? 'Activates ' :
|
||||
st.trancheStatus.activationTime < now ? 'Rate Limited ' : 'Activated '
|
||||
result += timestampString(st.trancheStatus.activationTime) + ' '
|
||||
result += now < start ? ts.activationTime < now ? 'Rate Limited ' : 'Activates ' : 'Activated '
|
||||
result += timestampString(ts.activationTime) + ' '
|
||||
}
|
||||
if( !isStart && t.endTime < 4294967295 ) {
|
||||
const ended = t.endTimeIsRelative ? st.startTime + t.endTime : t.endTime
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<tr>
|
||||
<td>
|
||||
<v-avatar v-if="imageSrc" :image="imageSrc"/>
|
||||
<v-avatar v-if="imageSrc" :image="imageSrc" size="x-small"/>
|
||||
</td>
|
||||
<td class="d-none d-sm-table-cell">{{ token.n || '' }}</td>
|
||||
<td class="text-right">{{ fixed }}</td>
|
||||
|
||||
@@ -11,20 +11,15 @@
|
||||
</v-card-text>
|
||||
</div>
|
||||
<div v-if="hasVault">
|
||||
<!-- <v-card-title>-->
|
||||
<!-- Your Deposit Address {{ s.vaults.length > 1 ? '#' + (num + 1) : '' }}-->
|
||||
<!-- </v-card-title> <!– todo vault nicknames –>-->
|
||||
<!--
|
||||
<v-card-title>
|
||||
Your Deposit Address {{ s.vaults.length > 1 ? '#' + (num + 1) : '' }}
|
||||
</v-card-title> <!– todo vault nicknames –>
|
||||
-->
|
||||
|
||||
<!-- todo re-enable Deposit address
|
||||
<v-card-subtitle class="overflow-x-hidden">
|
||||
<copy-button :text="addr">Deposit {{addr}}</copy-button>
|
||||
</v-card-subtitle>
|
||||
-->
|
||||
|
||||
<v-alert v-if="s.chainId===421614 && nativeBalance===0n" type="warning" icon="mdi-alert"
|
||||
text="Your wallet needs Sepolia ETH for gas to place orders. Use the Arbitrum-Sepolia faucet below."
|
||||
rounded="0"
|
||||
/>
|
||||
|
||||
<v-card-text v-if="empty">
|
||||
<!--
|
||||
@@ -45,6 +40,10 @@
|
||||
<v-card-item v-if="!empty">
|
||||
<v-table>
|
||||
<tbody>
|
||||
<suspense>
|
||||
<native-row :chain-id="s.chainId" :addr="s.vault" :amount="nativeBalance"
|
||||
:on-withdraw="onWithdrawNative" :on-wrap="()=>wrapShow=true"/>
|
||||
</suspense>
|
||||
<suspense v-for="(amount,addr) of balances">
|
||||
<token-row :chain-id="s.chainId" :addr="addr" :amount="amount" :onWithdraw="onWithdraw"/>
|
||||
</suspense>
|
||||
@@ -53,6 +52,8 @@
|
||||
</v-card-item>
|
||||
</div>
|
||||
<withdraw :vault="addr" :token="withdrawToken" v-model="withdrawShow"/>
|
||||
<withdraw-native :vault="addr" v-model="withdrawNativeShow" :balance="nativeBalance"/>
|
||||
<native-wrap :vault="addr" v-model="wrapShow" :max-amount="nativeBalance"/>
|
||||
</div>
|
||||
<!--
|
||||
<div>
|
||||
@@ -65,13 +66,15 @@
|
||||
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store.js";
|
||||
import {computed, defineAsyncComponent, ref, watchEffect} from "vue";
|
||||
import {newContract, vaultAddress, vaultContract} from "@/blockchain/contract.js";
|
||||
import {computed, defineAsyncComponent, onUnmounted, ref, watchEffect} from "vue";
|
||||
import {vaultAddress} from "@/blockchain/contract.js";
|
||||
import {ensureVault, provider} from "@/blockchain/wallet.js";
|
||||
import CopyButton from "@/components/CopyButton.vue";
|
||||
import Withdraw from "@/components/Withdraw.vue";
|
||||
import {getToken} from "@/blockchain/token.js";
|
||||
import {ethers} from "ethers";
|
||||
import NativeRow from "@/components/NativeRow.vue";
|
||||
import NativeWrap from "@/components/NativeWrap.vue";
|
||||
import WithdrawNative from "@/components/WithdrawNative.vue";
|
||||
|
||||
const TokenRow = defineAsyncComponent(()=>import('./TokenRow.vue'))
|
||||
const s = useStore()
|
||||
@@ -84,7 +87,7 @@ const balances = computed(()=>{
|
||||
console.log('balances', addr.value, s.vaultBalances, bs)
|
||||
return bs || {}
|
||||
})
|
||||
const empty = computed(()=>Object.values(balances.value).length===0)
|
||||
const empty = computed(()=>Object.values(balances.value).length===0 && nativeBalance.value===0)
|
||||
const hasVault = computed(()=>s.vault!==null)
|
||||
|
||||
const withdrawToken = ref(null)
|
||||
@@ -96,21 +99,33 @@ async function onWithdraw(addr) {
|
||||
withdrawShow.value = true
|
||||
}
|
||||
|
||||
|
||||
// todo use balance from server
|
||||
const _bal = ref(-1n)
|
||||
async function getNativeBalance(addr) {
|
||||
console.log('native balance of', addr)
|
||||
const balance = await provider.getBalance(addr);
|
||||
console.log('native balance', balance)
|
||||
if (s.account===addr) // check that we haven't switched vaults
|
||||
_bal.value = balance
|
||||
const withdrawNativeShow = ref(false)
|
||||
async function onWithdrawNative() {
|
||||
withdrawNativeShow.value = true
|
||||
}
|
||||
const nativeBalance = computed(()=>{
|
||||
if (!s.account) return -1n
|
||||
getNativeBalance(s.account)
|
||||
return _bal.value
|
||||
})
|
||||
|
||||
const wrapShow = ref(false)
|
||||
|
||||
const nativeBalance = ref(null)
|
||||
|
||||
async function updateNativeBalance() {
|
||||
// unfortunately there is no better way than polling, unless we start tracking in the backend. i assume the wallets
|
||||
// cache this data anyway and are not actually polling their backends
|
||||
if (provider) {
|
||||
const s = useStore();
|
||||
const vault = s.vault;
|
||||
if (vault) {
|
||||
const balance = await provider.getBalance(vault)
|
||||
console.log('native balance', vault, balance)
|
||||
if (s.vault===vault) // could have changed during async
|
||||
nativeBalance.value = balance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateNativeBalance()
|
||||
const timeout = setInterval(updateNativeBalance, 5000)
|
||||
onUnmounted(()=>clearInterval(timeout))
|
||||
|
||||
|
||||
function checkVault() {
|
||||
@@ -122,6 +137,8 @@ function checkVault() {
|
||||
watchEffect(checkVault)
|
||||
checkVault()
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -44,7 +44,8 @@ const floatAmount = ref(0)
|
||||
function withdraw() {
|
||||
const vaultAddr = props.vault
|
||||
const valueStr = floatAmount.value.toString();
|
||||
const amount = FixedNumber.fromString(valueStr,{decimals:props.token.d, width:256, signed: false}).value;
|
||||
const amount = floatAmount.value === balanceFloat.value ? balance.value :
|
||||
FixedNumber.fromString(valueStr,{decimals:props.token.d, width:256, signed: false}).value;
|
||||
console.log('pending withdrawal', valueStr, amount, props.token.s)
|
||||
if( amount === 0n )
|
||||
return
|
||||
|
||||
61
src/components/WithdrawNative.vue
Normal file
61
src/components/WithdrawNative.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<v-dialog :model-value="modelValue" @update:modelValue="$emit('update:modelValue', $event)">
|
||||
<v-card style="max-width: 25em" class="mx-auto">
|
||||
<v-card-title>
|
||||
<v-icon icon="mdi-arrow-down-bold" color="grey-darken-1"/> Withdraw Ether
|
||||
</v-card-title>
|
||||
<v-card-item>
|
||||
<v-text-field class="text-end" type="number" variant="outlined" :min="0" :max="balanceFloat"
|
||||
v-model="floatAmount" :step="balanceFloat/10">
|
||||
<template v-slot:prepend-inner>
|
||||
<v-btn variant="text" text="max" @click="floatAmount=balanceFloat"/>
|
||||
</template>
|
||||
<template v-slot:append-inner>
|
||||
<span>ETH</span>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<v-card-actions>
|
||||
<v-btn text="Cancel" @click="$emit('update:modelValue', false)"/>
|
||||
<v-btn text="Withdraw" color="red" @click="withdraw"/>
|
||||
</v-card-actions>
|
||||
</v-card-item>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store";
|
||||
import {computed, ref} from "vue";
|
||||
import {vaultContract} from "@/blockchain/contract.js"
|
||||
import {pendTransaction} from "@/blockchain/wallet.js";
|
||||
import {FixedNumber} from "ethers";
|
||||
|
||||
const s = useStore()
|
||||
const props = defineProps(['modelValue', 'vault', 'balance'])
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const balanceFloat = computed(() => Number(props.balance)/1e18)
|
||||
const floatAmount = ref(0)
|
||||
|
||||
function withdraw() {
|
||||
const vaultAddr = props.vault
|
||||
const valueStr = floatAmount.value.toString();
|
||||
const amount = floatAmount.value === balanceFloat.value ? props.balance :
|
||||
FixedNumber.fromString(valueStr,{decimals:18, width:256, signed: false}).value;
|
||||
console.log('pending native withdrawal', valueStr, amount)
|
||||
if( amount === 0n )
|
||||
return
|
||||
pendTransaction(async (signer)=>{
|
||||
const vault = await vaultContract(vaultAddr, signer)
|
||||
console.log('invoking withdraw', vaultAddr, amount)
|
||||
return await vault['withdraw(uint256)'](amount)
|
||||
})
|
||||
floatAmount.value = 0
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "src/styles/vars" as *;
|
||||
|
||||
</style>
|
||||
@@ -1,4 +1,4 @@
|
||||
import {FixedNumber} from "ethers";
|
||||
import {ethers, FixedNumber} from "ethers";
|
||||
import {usePrefStore, useStore} from "@/store/store.js";
|
||||
import {token} from "@/blockchain/token.js";
|
||||
import Color from "color";
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
.clickable {
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user