import {nav, sleep, uuid} from "@/misc.js"; import {vaultContract} from "@/blockchain/contract.js"; import {ensureVault, provider, switchChain, useWalletStore} from "@/blockchain/wallet.js"; import {toRaw} from "vue"; import {useChartOrderStore} from "@/orderbuild.js"; import {placementFee} from "@/fees.js"; export const TransactionState = { Created: 0, // user requested a transaction Proposed: 1, // tx is sent to the wallet Signed: 2, // tx is awaiting blockchain mining Rejected: 3, // user refused to sign the tx Error: 3, // unknown error sending the tx to the wallet Mined: 4, // transaction has been confirmed on-chain } export const TransactionType = { PlaceOrder: 1, CancelOrder: 2, CancelAll: 3, Wrap: 4, Unwrap: 5, WithdrawNative: 6, Withdraw: 7, } export class Transaction { constructor(chainId, type) { this.id = uuid() this.type = type this.state = TransactionState.Created this.tx = null this.chainId = chainId this.owner = null this.vault = null this.error = null } submit() { const ws = useWalletStore(); if ( ws.transaction !== null ) { console.error('Transaction already in progress', ws.transaction) return } ws.transaction = this } // "propose" means attach the transaction to a specific vault propose(owner, vault) { if (this.vault !== null && this.vault !== vault) { this.failed('proposed vault did not match withdrawl vault', vault, this.vault) return } this.owner = owner this.vault = vault this.send().catch(this.catchSend.bind(this)) this.state = TransactionState.Proposed } async createTx(vaultContract) { throw Error('unimplemented') } signed(tx) { this.tx = tx this.state = TransactionState.Signed } rejected() { this.tx = null this.chainId = null this.owner = null this.vault = null this.end(TransactionState.Rejected) console.log('transaction rejected', this.id) } failed(e) { this.error = e this.end(TransactionState.Error) console.log('transaction failed', this.id, e) } mined(receipt) { this.receipt = receipt this.end(TransactionState.Mined) console.log('mined transaction', this.id, receipt) } isOpen() { return this.state >= TransactionState.Rejected } isClosed() { return this.state < TransactionState.Rejected } end(state) { this.state = state useWalletStore().transaction = null } async send() { console.log('sendTransaction', this) try { await switchChain(this.chainId) } catch (e) { if (e.code === 4001) { this.rejected() return null } else { this.failed(e) return null } } let signer try { signer = await provider.getSigner(); } catch (e) { // { // "code": -32002, // "message": "Already processing eth_requestAccounts. Please wait." // } this.rejected() return null } let contract try { contract = await vaultContract(this.vault, signer) } catch (e) { this.failed('vault contract was null while sending order transaction') return null } try { const tx = toRaw(await this.createTx(contract)) this.signed(tx) tx.wait().then(this.mined.bind(this)).catch(this.failed.bind(this)) console.log(`sent transaction`, tx) } catch (e) { this.failed(e) return null } return this.tx } catchSend(e) { this.error = e if (e.info?.error?.code === 4001) { console.log(`wallet refused transaction`, this.id) this.rejected() } else { this.failed(e) } } } export class PlaceOrderTransaction extends Transaction { constructor(chainId, order) { super(chainId, TransactionType.PlaceOrder) this.order = order this.placementTime = Date.now()/1000 this.fee = null // dexorder place and gas fee total } async createTx(vaultContract) { const tries = 65; let i; let success = false for (i=0; !success && i a + b)}) } end(state) { super.end(state) if (state === TransactionState.Mined) { useChartOrderStore().resetOrders() nav('Status') } } } export class CancelOrderTransaction extends Transaction { constructor(chainId, index) { super(chainId, TransactionType.CancelOrder) this.index = index } async createTx(vaultContract) { return await vaultContract.cancelDexorder(this.index) } } export class CancelAllTransaction extends Transaction { constructor(chainId, vault) { super(chainId, TransactionType.CancelAll) this.vault = vault } async createTx(vaultContract) { return await vaultContract.cancelAllDexorders() } } export class WithdrawTransaction extends Transaction { constructor(chainId, vault, token, amount) { super(chainId, TransactionType.Withdraw) this.token = token this.amount = amount this.vault = vault } async createTx(vaultContract) { return await vaultContract['withdraw(address,uint256)'](this.token.a, this.amount) } } export class WithdrawNativeTransaction extends Transaction { constructor(chainId, vault, amount) { super(chainId, TransactionType.WithdrawNative) this.amount = amount this.vault = vault } async createTx(vaultContract) { return await vaultContract['withdraw(uint256)'](this.amount) } } export class WrapTransaction extends Transaction { constructor(chainId, vault, amount) { super(chainId, TransactionType.Wrap) this.vault = vault this.amount = amount } async createTx(vaultContract) { return await vaultContract.wrap(this.amount) } } export class UnwrapTransaction extends Transaction { constructor(chainId, vault, amount) { super(chainId, TransactionType.Unwrap) this.amount = amount } async createTx(vaultContract) { return await vaultContract.unwrap(this.amount) } }