Skip to content

fix: Reset nonce if out of funds #807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions src/shared/utils/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
);
}

Expand All @@ -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);
};
22 changes: 18 additions & 4 deletions src/worker/tasks/cancel-recycled-nonces-worker.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -47,10 +53,18 @@ const handler: Processor<any, void, string> = async (job: Job<string>) => {
});
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);
Expand Down
Loading