From aacf5b71680d6c7647caba9f36e6bb08a3804171 Mon Sep 17 00:00:00 2001 From: mosi Date: Mon, 9 Dec 2024 17:45:09 +0800 Subject: [PATCH] fix: error handle --- packages/db/src/main/cfxjs/db/queries.cljs | 19 +++++++ .../wallet_handleUnfinishedCFXTx/index.js | 54 ++++++++++++++----- .../wallet_handleUnfinishedETHTx/index.js | 48 ++++++++++++----- 3 files changed, 95 insertions(+), 26 deletions(-) diff --git a/packages/db/src/main/cfxjs/db/queries.cljs b/packages/db/src/main/cfxjs/db/queries.cljs index 81ea66b82..91a5949e1 100644 --- a/packages/db/src/main/cfxjs/db/queries.cljs +++ b/packages/db/src/main/cfxjs/db/queries.cljs @@ -2126,6 +2126,24 @@ (assoc :app (and app-id (e :app app-id))) (assoc :token (and token-id (e :token token-id)))))) +(defn get-tx-with-same-nonce [{:keys [hash]}] + (let [txs + (q '[:find [?txs ...] + :in $ ?tx + :where + [?address :address/tx ?tx] + [?tx :tx/txPayload ?payload] + [?payload :txPayload/nonce ?nonce] + [?address :address/tx ?txs] + [?txs :tx/txPayload ?tx-payload] + [?tx-payload :txPayload/nonce ?nonce] + [?address :address/value ?addrv] + [?tx-payload :txPayload/from ?addrv] + [?txs :tx/hash ?hash]] + [:tx/hash hash])] + (when txs + (mapv tx-id->data txs)))) + (defn get-tx [{:keys [hash]}] (let [tx (q '[:find ?tx . :in $ ?tx @@ -2315,6 +2333,7 @@ :upsertMemo upsert-memo :queryNonceGapTxs query-nonce-gap-txs :insertExternalTx insert-external-tx + :queryTxWithSameNonce get-tx-with-same-nonce :queryqueryRecentInterestingAddress get-recent-interesting-address-from-tx :queryqueryApp get-apps diff --git a/packages/rpcs/wallet_handleUnfinishedCFXTx/index.js b/packages/rpcs/wallet_handleUnfinishedCFXTx/index.js index 2520150e4..902c544e9 100644 --- a/packages/rpcs/wallet_handleUnfinishedCFXTx/index.js +++ b/packages/rpcs/wallet_handleUnfinishedCFXTx/index.js @@ -16,6 +16,11 @@ import {identity} from '@fluent-wallet/compose' export const NAME = 'wallet_handleUnfinishedCFXTx' +function getGasPrice(tx) { + const payload = tx.txPayload + return payload.type === '0x2' ? payload.maxFeePerGas : payload.gasPrice +} + function defs(...args) { const s = stream({ id: args.length === 2 ? args[0] : undefined, @@ -75,6 +80,7 @@ export const permissions = { 'setTxConfirmed', 'setTxUnsent', 'setTxChainSwitched', + 'queryTxWithSameNonce', ], } @@ -102,6 +108,7 @@ export const main = ({ setTxConfirmed, setTxUnsent, setTxChainSwitched, + queryTxWithSameNonce, }, params: {tx, address, okCb, failedCb}, network, @@ -207,16 +214,34 @@ export const main = ({ // failed to send setTxUnsent({hash}) - const {errorType, shouldDiscard} = processError(err) + let {errorType, shouldDiscard} = processError(err) const isDuplicateTx = errorType === 'duplicateTx' const resendNonceTooStale = tx.resendAt && errorType === 'tooStaleNonce' - - const sameAsSuccess = isDuplicateTx || resendNonceTooStale + const resendPriceTooLow = + tx.resendAt && errorType === 'replacedWithHigherGasPriceTx' + if (resendPriceTooLow) errorType = 'replacedByAnotherTx' + const sameNonceTxs = queryTxWithSameNonce({hash}) || [] + const latestTx = sameNonceTxs + .filter(_tx => _tx.hash !== hash) + .sort((a, b) => + BigNumber.from(getGasPrice(a)).sub(getGasPrice(b)).toNumber(), + )[0] + const sameAsSuccess = + isDuplicateTx || + resendNonceTooStale || + (resendPriceTooLow && + latestTx && + latestTx.status >= 0 && + latestTx.status < 5) const failed = !sameAsSuccess && shouldDiscard defs({ - failed: failed && {errorType, err}, + failed: failed && { + errorType, + err, + disableNotification: !!latestTx, + }, sameAsSuccess, resend: !shouldDiscard && !sameAsSuccess, }) @@ -227,17 +252,18 @@ export const main = ({ ({err}) => typeof failedCb === 'function' && failedCb(err), ), - sideEffect(({errorType}) => { + sideEffect(({errorType, disableNotification}) => { if (setTxFailed({hash, error: errorType})) { - getExt().then(ext => - ext.notifications.create(hash, { - title: 'Failed transaction', - message: `Transaction ${parseInt( - tx.txPayload.nonce, - 16, - )} failed! ${err?.data || err?.message || ''}`, - }), - ) + !disableNotification && + getExt().then(ext => + ext.notifications.create(hash, { + title: 'Failed transaction', + message: `Transaction ${parseInt( + tx.txPayload.nonce, + 16, + )} failed! ${err?.data || err?.message || ''}`, + }), + ) } }), sideEffect(() => { diff --git a/packages/rpcs/wallet_handleUnfinishedETHTx/index.js b/packages/rpcs/wallet_handleUnfinishedETHTx/index.js index 7cd39f86e..d059be0e0 100644 --- a/packages/rpcs/wallet_handleUnfinishedETHTx/index.js +++ b/packages/rpcs/wallet_handleUnfinishedETHTx/index.js @@ -16,6 +16,11 @@ import {identity} from '@fluent-wallet/compose' export const NAME = 'wallet_handleUnfinishedETHTx' +function getGasPrice(tx) { + const payload = tx.txPayload + return payload.type === '0x2' ? payload.maxFeePerGas : payload.gasPrice +} + function defs(...args) { const s = stream({ id: args.length === 2 ? args[0] : undefined, @@ -75,6 +80,7 @@ export const permissions = { 'setTxConfirmed', 'setTxUnsent', 'setTxChainSwitched', + 'queryTxWithSameNonce', ], } @@ -102,6 +108,7 @@ export const main = ({ setTxConfirmed, setTxUnsent, setTxChainSwitched, + queryTxWithSameNonce, }, params: {tx, address, okCb, failedCb}, network, @@ -216,13 +223,29 @@ export const main = ({ const resendPriceTooLow = tx.resendAt && errorType === 'replaceUnderpriced' if (resendPriceTooLow) errorType = 'replacedByAnotherTx' + const sameNonceTxs = queryTxWithSameNonce({hash}) || [] + const latestTx = sameNonceTxs + .filter(_tx => _tx.hash !== hash) + .sort((a, b) => + BigNumber.from(getGasPrice(a)).sub(getGasPrice(b)).toNumber(), + )[0] + const sameAsSuccess = + isDuplicateTx || + resendNonceTooStale || + (resendPriceTooLow && + latestTx && + latestTx.status >= 0 && + latestTx.status < 5) - const sameAsSuccess = isDuplicateTx || resendNonceTooStale const failed = !sameAsSuccess && (shouldDiscard || resendPriceTooLow) defs({ - failed: failed && {errorType, err}, + failed: failed && { + errorType, + err, + disableNotification: !!latestTx, + }, sameAsSuccess, resend: !shouldDiscard && !sameAsSuccess, }) @@ -233,17 +256,18 @@ export const main = ({ ({err}) => typeof failedCb === 'function' && failedCb(err), ), - sideEffect(({errorType}) => { + sideEffect(({errorType, disableNotification}) => { if (setTxFailed({hash, error: errorType})) { - getExt().then(ext => - ext.notifications.create(hash, { - title: 'Failed transaction', - message: `Transaction ${parseInt( - tx.txPayload.nonce, - 16, - )} failed! ${err?.data || err?.message || ''}`, - }), - ) + !disableNotification && + getExt().then(ext => + ext.notifications.create(hash, { + title: 'Failed transaction', + message: `Transaction ${parseInt( + tx.txPayload.nonce, + 16, + )} failed! ${err?.data || err?.message || ''}`, + }), + ) } }), sideEffect(() => {