diff --git a/src/shared/utils/error.ts b/src/shared/utils/error.ts index c7b619c7..51d4f8f4 100644 --- a/src/shared/utils/error.ts +++ b/src/shared/utils/error.ts @@ -16,11 +16,10 @@ const _parseMessage = (error: unknown): string | null => { export const isNonceAlreadyUsedError = (error: unknown) => { const message = _parseMessage(error); - const errorPhrases = ["nonce too low", "already known"]; if (message) { - return errorPhrases.some((phrase) => - message.toLowerCase().includes(phrase), + return ( + message.includes("nonce too low") || message.includes("already known") ); } @@ -41,10 +40,7 @@ export const isReplacementGasFeeTooLow = (error: unknown) => { export const isInsufficientFundsError = (error: unknown) => { const message = _parseMessage(error); if (message) { - return ( - message.includes("insufficient funds for gas * price + value") || - message.includes("insufficient funds for intrinsic transaction cost") - ); + return message.includes("insufficient funds"); } return isEthersErrorCode(error, ethers.errors.INSUFFICIENT_FUNDS); }; diff --git a/src/worker/tasks/cancel-recycled-nonces-worker.ts b/src/worker/tasks/cancel-recycled-nonces-worker.ts index 9a445c60..0757cdb9 100644 --- a/src/worker/tasks/cancel-recycled-nonces-worker.ts +++ b/src/worker/tasks/cancel-recycled-nonces-worker.ts @@ -1,7 +1,13 @@ import { type Job, type Processor, Worker } from "bullmq"; import type { Address } from "thirdweb"; -import { recycleNonce } from "../../shared/db/wallets/wallet-nonce"; -import { isNonceAlreadyUsedError } from "../../shared/utils/error"; +import { + deleteNoncesForBackendWallets, + recycleNonce, +} from "../../shared/db/wallets/wallet-nonce"; +import { + isInsufficientFundsError, + isNonceAlreadyUsedError, +} from "../../shared/utils/error"; import { logger } from "../../shared/utils/logger"; import { redis } from "../../shared/utils/redis/redis"; import { sendCancellationTransaction } from "../../shared/utils/transaction/cancel-transaction"; @@ -47,10 +53,18 @@ const handler: Processor = async (job: Job) => { }); success.push(nonce); } catch (error: unknown) { - // Release the nonce if it has not expired. - if (isNonceAlreadyUsedError(error)) { + if (isInsufficientFundsError(error)) { + // Wallet is out of funds. Reset the nonce state. + // After funded, the next transaction will resync the nonce. + job.log( + `Wallet ${chainId}:${walletAddress} out of funds. Resetting nonce.`, + ); + await deleteNoncesForBackendWallets([{ chainId, walletAddress }]); + } else if (isNonceAlreadyUsedError(error)) { + // Nonce is used. Don't recycle it. ignore.push(nonce); } else { + // Nonce is not used onchain. Recycle it. job.log(`Recycling nonce: ${nonce}`); await recycleNonce(chainId, walletAddress, nonce); fail.push(nonce); diff --git a/tests/e2e/tests/routes/signMessage.test.ts b/tests/e2e/tests/routes/sign-message.test.ts similarity index 100% rename from tests/e2e/tests/routes/signMessage.test.ts rename to tests/e2e/tests/routes/sign-message.test.ts diff --git a/tests/e2e/tests/routes/signaturePrepare.test.ts b/tests/e2e/tests/routes/signature-prepare.test.ts similarity index 100% rename from tests/e2e/tests/routes/signaturePrepare.test.ts rename to tests/e2e/tests/routes/signature-prepare.test.ts diff --git a/tests/e2e/tests/utils/getBlockTime.test.ts b/tests/e2e/tests/utils/get-block-time.test.ts similarity index 100% rename from tests/e2e/tests/utils/getBlockTime.test.ts rename to tests/e2e/tests/utils/get-block-time.test.ts diff --git a/tests/unit/sendTransactionWorker.test.ts b/tests/unit/send-transaction-worker.test.ts similarity index 100% rename from tests/unit/sendTransactionWorker.test.ts rename to tests/unit/send-transaction-worker.test.ts