withdrawls

This commit is contained in:
Tim Olson
2023-11-01 00:33:53 -04:00
parent ee61c96d38
commit 16e04b0f90
20 changed files with 438 additions and 189 deletions

View File

@@ -1,7 +1,6 @@
import {ethers} from "ethers";
import {factoryAbi, queryHelperAbi, vaultAbi} from "@/blockchain/abi.js";
import {useStore} from "@/store/store.js";
import {provider} from "@/blockchain/wallet.js";
export function vaultAddress( owner, num=0) {
@@ -11,7 +10,7 @@ export function vaultAddress( owner, num=0) {
}
function contractOrNull(addr,abi,provider) {
export function contractOrNull(addr,abi,provider) {
try {
return new ethers.Contract(addr,abi,provider)
}
@@ -22,16 +21,17 @@ function contractOrNull(addr,abi,provider) {
export async function factoryContract() {
const s = useStore()
return contractOrNull(s.factory, factoryAbi, provider)
return contractOrNull(s.factory, factoryAbi, s.provider)
}
export async function queryHelperContract() {
const s = useStore()
return contractOrNull(s.helper, queryHelperAbi, provider)
return contractOrNull(s.helper, queryHelperAbi, s.provider)
}
export async function poolContract(addr) {
return contractOrNull(addr, poolAbi, provider)
const s = useStore()
return contractOrNull(addr, poolAbi, s.provider)
}
export async function vaultContract(num, signer) {

44
src/blockchain/token.js Normal file
View File

@@ -0,0 +1,44 @@
import {socket} from "@/socket.js";
import {useStore} from "@/store/store.js";
import {erc20Abi} from "@/blockchain/abi.js";
import {ethers} from "ethers";
export async function getToken(addr) {
const s = useStore()
if (!(addr in s.tokens))
await addExtraToken(addr)
return s.tokens[addr]
}
export async function addExtraToken(addr) {
const prom = new Promise((resolve) => {
const s = useStore()
const chainId = s.chainId
console.log('querying token', addr)
socket.emit('lookupToken', chainId, addr, (info) => {
console.log('server token info', info)
if (info !== null) {
s.addToken(chainId, info)
resolve(info)
}
else {
if( s.provider===null ) {
console.log('warning: token lookup cancelled due to null provider', addr)
resolve(null)
return
}
const token = new ethers.Contract(addr, erc20Abi, s.provider)
Promise.all( [token.symbol(), token.decimals()] ).then((symbol,decimals)=>{
info = {
address: addr,
symbol: symbol,
decimals: decimals,
}
s.addToken(chainId, info)
resolve(info)
})
}
})
})
return await prom
}

View File

@@ -1,26 +1,26 @@
import {ethers} from "ethers";
import {useStore} from "@/store/store";
import {setProvider, useStore} from "@/store/store";
import {socket} from "@/socket.js";
import {vaultContract} from "@/blockchain/contract.js";
import {contractOrNull} from "@/blockchain/contract.js";
import {vaultAbi} from "@/blockchain/abi.js";
export let provider = null
export function onChainChanged(chainId) {
chainId = Number(chainId)
console.log('chain changed', chainId)
const store = useStore()
if( chainId !== store.chainId ) {
store.chainId = chainId
const provider = new ethers.BrowserProvider(window.ethereum, chainId);
setProvider(provider, chainId)
store.account = null
provider = new ethers.BrowserProvider(window.ethereum, chainId)
provider.listAccounts().then(changeAccounts)
new ethers.Interface([
// 'event DexorderSwapCreated' // todo
])
store.chainId = chainId // touch the chainId last. will cause any clients of the store's provider getter to refresh
}
}
function changeAccounts(accounts) {
console.log('change accounts', accounts)
const store = useStore()
if( accounts.length === 0 ) {
store.account = null
store.vaults = []
@@ -29,16 +29,15 @@ function changeAccounts(accounts) {
else {
const store = useStore()
store.account = accounts[0].address
flushOrders()
flushTransactions()
socket.emit('address', store.chainId, accounts[0].address)
}
}
function onAccountsChanged(accounts) {
// console.log('accounts changed', accounts)
const store = useStore()
if (accounts.length === 0 || accounts[0] !== store.account)
changeAccounts(store, accounts);
changeAccounts(accounts);
}
export async function watchWallet() {
@@ -82,31 +81,56 @@ const errorHandlingProxy = {
export async function connectWallet() {
return provider.getSigner()
console.log('TODO connect wallet')
// eth_getaccounts
}
export async function pendOrder(order) {
export function pendOrder(order) {
console.log('order', JSON.stringify(order))
const s = useStore()
s.pendingOrders.push(order)
flushOrders()
}
export function flushOrders() {
// noinspection JSIgnoredPromiseFromCall
asyncFlushOrders()
}
export async function asyncFlushOrders() {
const s = useStore()
const orders = s.pendingOrders
if (!orders.length)
const vault = s.vault;
if(vault === null ) {
console.error('vault was null during pendOrder')
return
}
pendTransaction(async (signer)=> {
const contract = contractOrNull(vault, vaultAbi, signer)
if( contract === null ) {
console.error('vault contract was null while sending order transaction', vault)
return null
}
return await contract.placeOrder(order)
})
}
export function pendTransaction(sender) {
const s = useStore()
s.transactionSenders.push(sender)
flushTransactions()
}
export function flushTransactions() {
// noinspection JSIgnoredPromiseFromCall
asyncFlushTransactions()
}
export async function asyncFlushTransactions() {
// todo rework into flushTransactions()
const s = useStore()
if( s.provider === null ) {
console.log('warning: asyncFlushOrders() cancelled due to null provider')
return
}
const senders = s.transactionSenders
if (!senders.length)
return
console.log(`flushing ${s.transactionSenders.length} transactions`)
let signer
try {
signer = await provider.getSigner();
signer = await s.provider.getSigner();
} catch (e) {
// {
// "code": -32002,
@@ -119,28 +143,27 @@ export async function asyncFlushOrders() {
socket.emit('ensureVault', s.chainId, await signer.getAddress(), 0)
return
}
const contract = await vaultContract(0, signer)
if (!contract) {
console.error(`no contract for vault 0 of ${signer.address}`)
return
}
for (const order of orders)
doPlaceOrder(s, contract, order)
for (const sender of senders)
doSendTransaction(sender, signer)
}
function doPlaceOrder(s, contract, order) {
contract.placeOrder(order).then((tx)=>{
console.log('placed order', tx)
s.removePendingOrder(order)
function doSendTransaction(sender, signer) {
const s = useStore();
sender(signer).then((tx)=>{
console.log('sent transaction', tx)
s.removeTransactionSender(sender)
tx.wait().then((tr)=>console.log('tx receipt',tr))
}).catch((e)=>{
if( e.info.error.code === 4001 ) {
console.log(`user rejected order`, order)
s.removePendingOrder(order)
if( e.info?.error?.code === 4001 ) {
console.log(`user rejected transaction`)
s.removeTransactionSender(sender)
}
else {
console.error('error placing order', order, e.reason, e.info)
s.removePendingOrder(order)
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?
}
})
@@ -150,5 +173,5 @@ socket.on('vaults', (vaults)=>{
const s = useStore()
console.log('vaults', vaults)
s.vaults = vaults
flushOrders()
flushTransactions()
})

View File

@@ -0,0 +1,34 @@
<template>
<div :class="['chain-block','chain-'+chainId]"><v-avatar class='chain-avatar' v-if="media.img" :image="media.img" rounded="0" size="1em"/>&nbsp;{{media.name}}</div>
</template>
<script setup>
import {useStore} from "@/store/store";
import {computed} from "vue";
const unsupportedMedia = {}
const chainMedia = {
1: {name: 'Ethereum', img: null},
42161: { name: 'Arbitrum One', img: '/arbitrum-logo.svg'},
31331: { name: 'Mockchain', img: null},
}
const s = useStore()
const props = defineProps(['chainId'])
const media = computed(()=>chainMedia[props.chainId] || unsupportedMedia)
</script>
<style lang="scss"> // NOT 'scoped'
@use "src/styles/vars" as *;
//.chain-avatar {
// max-height: 1em;
// max-width: 1em;
//}
.chain-block {
display: inline;
}
.chain-42161 {
color: #12aaff;
}
</style>

View File

@@ -1,24 +0,0 @@
<template>
<v-btn prepend-icon="mdi-lightbulb-on" text="Connect Wallet" @click="connectWallet" :disabled="disabled"/>
</template>
<script setup>
import {useStore} from "@/store/store";
import {ref} from "vue";
import {provider} from "@/blockchain/wallet.js";
const s = useStore()
const disabled = ref(false)
async function connectWallet() {
disabled.value = true
await provider.getSigner()
disabled.value = false
}
</script>
<style scoped lang="scss">
@use "src/styles/vars" as *;
</style>

View File

@@ -1,7 +1,12 @@
<template>
<v-btn v-if="permitted" rounded variant="text" size="small" density="compact" @click="copy()"
<v-tooltip :model-value="!error&&copied" :open-on-hover="false">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" v-if="permitted" rounded variant="text" size="small" density="compact" @click="copy()"
:class="error?'error':copied?'success':''"
:icon="error?'mdi-close-box-outline':copied?'mdi-check-circle-outline':'mdi-content-copy'"/>
</template>
<span>Copied!</span>
</v-tooltip>
</template>
<script setup lang="ts">

View File

@@ -0,0 +1,52 @@
<template>
<slot v-if="ok"/>
<phone-card v-if="!walletOk">
<v-card-title>Install Wallet</v-card-title>
<v-card-text>
A cryptocurrency wallet such as <a href="https://metamask.io/download/">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>
<v-btn prepend-icon="mdi-reload" text="Reload After Installing Wallet"/>
</v-card-actions>
</phone-card>
<phone-card v-if="walletOk && !providerOk">
<v-card-text>
Please log in to your crypto wallet.
</v-card-text>
<v-card-actions v-if="walletOk && !providerOk">
<v-btn prepend-icon="mdi-power" text="Connect Wallet" @click="connectWallet"/>
</v-card-actions>
</phone-card>
<phone-card v-if="walletOk && providerOk && !chainOk">
<v-card-title><v-icon icon="mdi-reload-alert" color="warning"/> Change Blockchain</v-card-title>
<v-card-text>
Dexorder works only with <blockchain chain-id="42161"/>. Please switch to the
<blockchain chain-id="42161"/> blockchain in your wallet.
</v-card-text>
</phone-card>
</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 Blockchain from "@/components/Blockchain.vue";
const s = useStore()
const walletOk = typeof window.ethereum !== 'undefined'
const providerOk = computed(()=>s.provider!==null)
const chainOk = computed(()=>providerOk.value && s.helper!==null)
const ok = computed(()=>{
return walletOk && providerOk.value && chainOk.value
})
</script>
<style scoped lang="scss">
@use "src/styles/vars" as *;
.arbitrum {
color: $arbitrum-color;
}
</style>

View File

@@ -1,21 +0,0 @@
<template>
<!-- <v-card v-if="!s.account" prepend-icon="mdi-connection" title="Connect Wallet"-->
<!-- text="Please connect your wallet to an Arbitrum account.">-->
<!-- <v-card-actions><v-btn text="Connect Wallet" color="green" variant="elevated" @click="connectWallet" prepend-icon="mdi-power"/></v-card-actions>-->
<!-- </v-card>-->
<slot v-if="s.helper"/>
<v-card v-if="!s.helper" prepend-icon='mdi-reload-alert' title="Change Blockchain"
text="Dexorder works only with Arbitrum. Please choose the Arbitrum blockchain in your wallet."/>
</template>
<script setup>
import {useStore} from "@/store/store";
const s = useStore()
</script>
<style scoped lang="scss">
@use "src/styles/vars" as *;
</style>

View File

@@ -1,5 +1,5 @@
<template>
<v-btn prepend-icon="mdi-plus" text="New Order"/>
</template>
<script setup>

View File

@@ -1,8 +1,10 @@
<template>
<v-card class="d-none d-md-block" :elevation="4">
<!-- tablets and desktops get a card outline -->
<v-card class="d-none d-sm-block phone-card" :elevation="4">
<slot/>
</v-card>
<v-container class="d-md-none">
<!-- phones use the entire screen -->
<v-container class="d-sm-none">
<slot/>
</v-container>
</template>
@@ -16,4 +18,7 @@ const s = useStore()
<style scoped lang="scss">
@use "src/styles/vars" as *;
.phone-card {
max-width: $card-maxw;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<NeedsQueryHelper>
<PhoneCard>
<needs-provider>
<phone-card>
<v-card-title class="big">DCA / TWAP</v-card-title>
<v-card-subtitle>Multiple tranches over a time range</v-card-subtitle>
<v-card-text>
@@ -20,7 +20,7 @@
<v-chip v-for="r in routes" variant="text">
{{ s.chain.name }}
<v-img src="https://upload.wikimedia.org/wikipedia/commons/e/e7/Uniswap_Logo.svg" width="1.5em"/>
<span class="uniswap-pink ml-0 mr-1">v3</span>
<span class="uniswap-color ml-0 mr-1">v3</span>
{{pairSymbol}} {{r.fee/10000}}%
</v-chip>
@@ -78,8 +78,8 @@
<v-btn variant="outlined" color="red">Cancel</v-btn>
<v-btn variant="flat" color="green" :disabled="!validOrder" @click="placeOrder">Place Order</v-btn>
</v-card-actions>
</PhoneCard>
</NeedsQueryHelper>
</phone-card>
</needs-provider>
</template>
<script setup>
@@ -90,15 +90,15 @@ import PhoneCard from "@/components/PhoneCard.vue";
import {queryHelperContract} from "@/blockchain/contract.js";
// noinspection ES6UnusedImports
import {SingletonCoroutine, vAutoSelect} from "@/misc.js";
import NeedsQueryHelper from "@/components/NeedsQueryHelper.vue";
import {Exchange, newOrder, newTimeConstraint, TimeMode} from "@/blockchain/orderlib.js";
import {FixedNumber} from "ethers";
import {pendOrder} from "@/blockchain/wallet.js";
import NeedsProvider from "@/components/NeedsProvider.vue";
const s = useStore()
const buy = ref(false)
let _tokenA = ref(s.tokens !== undefined && s.tokens.length >= 1 ? s.tokens[0] : null)
let _tokenB = ref(s.tokens !== undefined && s.tokens.length >= 2 ? s.tokens[1] : null)
let _tokenA = ref(Object.values(s.tokens).length >= 1 ? Object.values(s.tokens)[0] : null)
let _tokenB = ref(Object.values(s.tokens).length >= 2 ? Object.values(s.tokens)[1] : null)
const tokenA = computed({
get() {
return _tokenA.value
@@ -149,19 +149,22 @@ const limitIsMinimum = computed(() => !(buy.value ^ inverted.value))
const validOrder = computed(()=>amount.value > 0 && routes.value.length > 0 )
async function findRoute() {
console.log('finding route', _tokenA.value, _tokenB.value)
routes.value = []
if( !_tokenA.value || !_tokenB.value )
return
const helper = await queryHelperContract()
if( !helper )
if( !helper ) {
console.log('no helper')
return
}
routesPending.value = true
let rawRoutes
try {
rawRoutes = await helper.getRoutes(tokenA.value.address, tokenB.value.address)
}
catch (e) {
// console.log('routes exception', e)
console.log('routes exception', e)
routesPending.value = false
return
}
@@ -242,7 +245,7 @@ function validateMin(v) {
return true
}
async function placeOrder() {
function placeOrder() {
const ta = tokenA.value;
const tb = tokenB.value;
const tokenIn = buy.value ? tb.address : ta.address
@@ -278,7 +281,7 @@ async function placeOrder() {
ts.push([amtPerTranche, cs])
}
const order = newOrder(tokenIn, tokenOut, route.exchange, route.fee, amt, amountIsInput, ts)
await pendOrder(order)
pendOrder(order)
}
</script>

View File

@@ -1,5 +1,5 @@
<template>
<v-combobox :items="Object.values(s.tokens)" :auto-select-first="true"
<v-combobox :items="tokens" :auto-select-first="true"
item-title="symbol"
:filter-keys="['raw.name','raw.symbol','raw.address']"
:model-value="modelValue"
@@ -21,7 +21,7 @@
<script setup>
import {useStore as useStore2} from "@/store/store";
import {ref} from "vue";
import {computed, ref} from "vue";
import {ethers} from "ethers";
// noinspection ES6UnusedImports
import {vAutoSelect} from "@/misc.js";
@@ -32,6 +32,8 @@ const props = defineProps(['modelValue', 'label'])
const emit = defineEmits(['update:modelValue'])
const loading = ref(false)
const errors = ref([])
const tokens = computed(()=>Object.values(s.tokens))
function good() {
errors.value = []

View File

@@ -1,10 +1,24 @@
<template>
<tr>
<td><v-img v-if="imageSrc" :src="imageSrc"/></td>
<td>{{token.symbol}}</td>
<td class="d-none d-sm-table-cell">{{token.name||''}}</td>
<td>{{fixed}}</td>
<td><!-- todo actions --></td>
<td>
<v-avatar v-if="imageSrc" :image="imageSrc"/>
</td>
<td class="d-none d-sm-table-cell">{{ token.name || '' }}</td>
<td class="text-right">{{ fixed }}</td>
<td class="text-left">{{ token.symbol }}</td>
<!-- todo price and value columns -->
<td>
<v-menu>
<template v-slot:activator="{ props }">
<v-btn variant="plain" v-bind="props" icon="mdi-menu"/> <!-- mdi-dots-vertical -->
</template>
<v-list>
<v-list-subheader :title="token.symbol"/>
<v-list-item title="Withdraw" key="withdraw" value="withdraw" prepend-icon="mdi-arrow-down-bold"
@click="()=>onWithdraw(token.address)"/>
</v-list>
</v-menu>
</td>
</tr>
</template>
@@ -12,13 +26,17 @@
import {useStore} from "@/store/store";
import {getToken} from "@/blockchain/token.js";
import {FixedNumber} from "ethers";
import {computed} from "vue";
import {computed, ref} from "vue";
const s = useStore()
const props = defineProps(['addr','amount'])
const props = defineProps(['addr', 'amount', 'onWithdraw'])
const token = await getToken(props.addr)
console.log('token', props.addr, token)
const fixed = computed(()=>FixedNumber.fromValue(props.amount, token.decimals, {width:256, decimals: token.decimals}))
const imageSrc = computed(()=>null )
const fixed = computed(() => FixedNumber.fromValue(props.amount, token.decimals, {
width: 256,
decimals: token.decimals
}))
const imageSrc = computed(() => null)
const withdrawing = ref(false)
</script>
<style scoped lang="scss">

View File

@@ -1,5 +1,6 @@
<template>
<!-- todo we can use something like this for ethereum where the vault creation is too expensive to subsidize
<needs-provider>
<!-- todo we can use something like this for ethereum where the vault creation is too expensive to subsidize
<PhoneCard v-if="s.vault===null || s.vault.length === 0">
<v-card-title><v-icon color="warning" icon="mdi-alert"/>&nbsp;Setup&nbsp;Vault</v-card-title>
<v-card-subtitle>Create Your Own Personal Dexorder Vault</v-card-subtitle>
@@ -21,7 +22,7 @@
easy access.
</v-card-text>
</PhoneCard>
-->
-->
<v-card v-if="s.vaults.length<num">
<v-card-title>No Vault Yet</v-card-title>
<v-card-text v-if="num!==0"><!--todo-->Multiple vaults are not yet supported</v-card-text>
@@ -34,25 +35,36 @@
<p>There are no funds currently in your vault.</p>
<p>Send tokens to the address above to fund your vault.</p>
</v-card-text>
<v-card-item>
<v-table v-if="!empty">
<v-card-item v-if="!empty">
<v-table>
<tbody>
<suspense v-for="(amount,addr) of balances">
<token-row :addr="addr" :amount="amount"/>
<token-row :addr="addr" :amount="amount" :onWithdraw="onWithdraw"/>
</suspense>
</tbody>
</v-table>
</v-card-item>
</v-card>
<withdraw :vault="addr" :token="withdrawToken" v-model="withdrawShow"/>
<!--
<div>
addr {{ addr }}<br/>
empty {{ empty }}<br/>
exists {{ exists }}<br/>
</div>
-->
</needs-provider>
</template>
<script setup>
import {useStore} from "@/store/store.js";
import PhoneCard from "@/components/PhoneCard.vue";
import {computed, defineAsyncComponent} from "vue";
import {computed, defineAsyncComponent, ref} from "vue";
import {vaultAddress} from "@/blockchain/contract.js";
import CopyButton from "@/components/CopyButton.vue";
import NeedsProvider from "@/components/NeedsProvider.vue";
import Withdraw from "@/components/Withdraw.vue";
console.log('vault setup')
const TokenRow = defineAsyncComponent(()=>import('./TokenRow.vue'))
const s = useStore()
@@ -63,9 +75,17 @@ const balances = computed(()=>{
console.log('balances', addr.value, s.vaultBalances, bs)
return bs || {}
})
const tokenAddrs = computed(()=>Object.keys(balances))
const empty = computed(()=>Object.keys(balances.value).length===0)
const exists = computed(()=>s.vaults.length>0)
const withdrawToken = ref(null)
const withdrawShow = ref(false)
function onWithdraw(addr) {
const token = s.tokens[addr]
console.log('withdraw', addr, token)
withdrawToken.value = token
withdrawShow.value = true
}
</script>
<style scoped lang="scss">

View File

@@ -0,0 +1,64 @@
<template>
<v-dialog :model-value="modelValue" @update:modelValue="$emit('update:modelValue', $event)">
<v-card>
<v-card-title>
<v-icon icon="mdi-down-arrow"/>&nbsp;Withdraw {{ token.symbol }}
</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>{{ token.symbol }}</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 {tokenFloat} from "@/misc.js";
import {contractOrNull} from "@/blockchain/contract.js"
import {vaultAbi} from "@/blockchain/abi.js";
import {pendTransaction} from "@/blockchain/wallet.js";
import {FixedNumber} from "ethers";
const s = useStore()
const props = defineProps(['modelValue', 'vault', 'token'])
const emit = defineEmits(['update:modelValue'])
const balance = computed(() => {
console.log('balance', props.vault, props.token, s.vaultBalances)
return s.vaultBalances[props.vault][props.token.address] || 0
})
const balanceFloat = computed(() => tokenFloat(props.token, balance.value))
const floatAmount = ref(0)
function withdraw() {
const vaultAddr = props.vault
const valueStr = floatAmount.value.toString();
const amount = FixedNumber.fromString(valueStr,{decimals:props.token.decimals, width:256, signed: false}).value;
console.log('pending withdrawl', valueStr, amount, props.token.symbol)
if( amount === 0n )
return
pendTransaction(async (signer)=>{
const vault = contractOrNull(vaultAddr, vaultAbi, signer)
return await vault['withdraw(address,uint256)'](props.token.address, amount)
})
emit('update:modelValue', false)
}
</script>
<style scoped lang="scss">
@use "src/styles/vars" as *;
</style>

View File

@@ -4,7 +4,7 @@
<div>
<Alerts/>
<router-view/>
<v-skeleton-loader v-if="!store.chainInfo" type="card" class="order-card"/>
<v-skeleton-loader v-if="!store.chainInfo" type="card" class="order-card"/> <!--todo static chainInfo -->
</div>
</v-container>
</v-main>
@@ -14,6 +14,7 @@
import Alerts from "@/components/Alerts.vue";
import {VSkeletonLoader} from "vuetify/labs/VSkeletonLoader";
import {useStore} from "@/store/store.js";
import NeedsProvider from "@/components/NeedsProvider.vue";
const store = useStore()
</script>

View File

@@ -1,3 +1,4 @@
import {FixedNumber} from "ethers";
export class SingletonCoroutine {
constructor(f, delay=10, retry=true) {
@@ -43,3 +44,11 @@ export const vAutoSelect = {
}
export const uint64max = 18446744073709551615n
export const uint32max = 4294967295n
export function tokenNumber(token, balance) {
return FixedNumber.fromValue(balance, token.decimals, {decimals:token.decimals, width: 256})
}
export function tokenFloat(token, balance) {
return tokenNumber(token,balance).toUnsafeFloat()
}

View File

@@ -2,6 +2,14 @@
import { defineStore } from 'pinia'
import {knownTokens} from "@/knownTokens.js";
let rawProvider = null
let rawProviderChainId = null
export function setProvider( provider, chainId ) {
rawProvider = provider
rawProviderChainId = chainId
}
export const useStore = defineStore('app', {
state: () => ({
chainId: null,
@@ -9,17 +17,18 @@ export const useStore = defineStore('app', {
vaultInitCodeHash: null,
account: null,
vaults: [],
pendingOrders: [], // created but not yet sent to metamask. maybe waiting on vault creation.
errors: [{
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: false
}],
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
}),
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 : []
@@ -37,8 +46,8 @@ export const useStore = defineStore('app', {
helper: (s)=>!s.chain?null:s.chain.helper,
},
actions: {
removePendingOrder(order) {
this.pendingOrders = this.pendingOrders.filter((v) => v !== order)
removeTransactionSender(sender) {
this.transactionSenders = this.transactionSenders.filter((v) => v !== sender)
},
error(title, text, closeable=true) {
this.errors.push({title, text, closeable})

View File

@@ -33,8 +33,11 @@
justify-content: center;
}
.v-text-field.text-end input {
text-align: end;
}
}
.uniswap-pink {
color: v.$uniswap-pink;
.uniswap-color {
color: v.$uniswap-color;
}

View File

@@ -4,10 +4,11 @@ $green: #00CC33;
$red: #CC0033;
$yellow: #ffcc00;
$blue: #0033CC;
$white: #fffefd; // just a touch green
$black: #000102;
$white: #fdfffe; // just a touch greenblue
$black: #000201; // just a touch greenblue
$uniswap-pink: #ff007a;
$arbitrum-color: #12aaff;
$uniswap-color: #ff007a;
$primary: $blue;
$primary-50: transparentize($primary,0.5);
@@ -31,7 +32,7 @@ $all-colors: (
green: $green,
yellow: $yellow,
red: $red,
uniswap: $uniswap-pink,
uniswap: $uniswap-color,
);
$body-font-family: 'Saira Semi Condensed', monospace, sans-serif; // fairly geometric, horizontal s's, clean sans, readable
@@ -50,8 +51,9 @@ $body-font-family: 'Saira Semi Condensed', monospace, sans-serif; // fairly geom
//$body-font-family: 'Chakra Petch', sans-serif;
//$heading-font-family: 'Tektur', sans-serif;
$heading-font-family: 'Orbitron', sans-serif;
//$heading-font-family: 'Exo 2', sans-serif;
//$heading-font-family: 'Quantico', sans-serif;
//$heading-font-family: 'Chakra Petch', sans-serif;
$sm-breakpoint: 600px;
$card-maxw: 25em;
$card-maxw: 34em;