native coin handling

This commit is contained in:
tim
2024-09-06 19:42:33 -04:00
parent 93dfb8bdcd
commit 84cadc6e6f
13 changed files with 460 additions and 265 deletions

View File

@@ -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) {

View File

@@ -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) {

View 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>

View 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"/>&nbsp;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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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> &lt;!&ndash; todo vault nicknames &ndash;&gt;-->
<!--
<v-card-title>
Your Deposit Address {{ s.vaults.length > 1 ? '#' + (num + 1) : '' }}
</v-card-title> &lt;!&ndash; todo vault nicknames &ndash;&gt;
-->
<!-- 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">

View File

@@ -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

View 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"/>&nbsp;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>

View File

@@ -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";

View File

@@ -43,7 +43,7 @@
.clickable {
:hover {
cursor: pointer;
cursor: pointer !important;
}
}