Skip to content

Commit b468f5d

Browse files
authored
fix: /reset-nonces endpoint allows deleting nonce state without resyncing (#836)
* chore: Allow deleting stored nonces without resyncing yet * rename * also delete nonce-history
1 parent 38ecff2 commit b468f5d

File tree

3 files changed

+30
-14
lines changed

3 files changed

+30
-14
lines changed

src/server/routes/backend-wallet/reset-nonces.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ const requestBodySchema = Type.Object({
2121
description:
2222
"The backend wallet address to reset nonces for. Omit to reset all backend wallets.",
2323
}),
24+
syncOnchainNonces: Type.Boolean({
25+
description:
26+
"Resync nonces to match the onchain transaction count for your backend wallets. (Default: true)",
27+
default: true,
28+
}),
2429
});
2530

2631
const responseSchema = Type.Object({
@@ -61,7 +66,11 @@ export const resetBackendWalletNoncesRoute = async (
6166
},
6267
},
6368
handler: async (req, reply) => {
64-
const { chainId, walletAddress: _walletAddress } = req.body;
69+
const {
70+
chainId,
71+
walletAddress: _walletAddress,
72+
syncOnchainNonces,
73+
} = req.body;
6574

6675
// If chain+wallet are provided, only process that wallet.
6776
// Otherwise process all used wallets that has nonce state.
@@ -70,19 +79,21 @@ export const resetBackendWalletNoncesRoute = async (
7079
? [{ chainId, walletAddress: getAddress(_walletAddress) }]
7180
: await getUsedBackendWallets();
7281

73-
const RESYNC_BATCH_SIZE = 50;
74-
for (let i = 0; i < backendWallets.length; i += RESYNC_BATCH_SIZE) {
75-
const batch = backendWallets.slice(i, i + RESYNC_BATCH_SIZE);
82+
const BATCH_SIZE = 50;
83+
for (let i = 0; i < backendWallets.length; i += BATCH_SIZE) {
84+
const batch = backendWallets.slice(i, i + BATCH_SIZE);
7685

7786
// Delete nonce state for these backend wallets.
7887
await deleteNoncesForBackendWallets(backendWallets);
7988

80-
// Resync nonces for these backend wallets.
81-
await Promise.allSettled(
82-
batch.map(({ chainId, walletAddress }) =>
83-
syncLatestNonceFromOnchain(chainId, walletAddress),
84-
),
85-
);
89+
if (syncOnchainNonces) {
90+
// Resync nonces for these backend wallets.
91+
await Promise.allSettled(
92+
batch.map(({ chainId, walletAddress }) =>
93+
syncLatestNonceFromOnchain(chainId, walletAddress),
94+
),
95+
);
96+
}
8697
}
8798

8899
reply.status(StatusCodes.OK).send({

src/shared/db/wallets/wallet-nonce.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { normalizeAddress } from "../../utils/primitive-types";
1010
import { redis } from "../../utils/redis/redis";
1111
import { thirdwebClient } from "../../utils/sdk";
1212
import { updateNonceMap } from "./nonce-map";
13+
import { nonceHistoryKey } from "../../../worker/tasks/nonce-health-check-worker";
1314

1415
/**
1516
* Get all used backend wallets.
@@ -45,7 +46,7 @@ export const getUsedBackendWallets = async (
4546

4647
/**
4748
* The "last used nonce" stores the last nonce submitted onchain.
48-
* Example: "25"
49+
* Example: 25 -> nonce 25 is onchain, nonce 26 is unused or inflight.
4950
*/
5051
export const lastUsedNonceKey = (chainId: number, walletAddress: Address) =>
5152
`nonce:${chainId}:${normalizeAddress(walletAddress)}`;
@@ -260,6 +261,7 @@ export async function deleteNoncesForBackendWallets(
260261
lastUsedNonceKey(chainId, walletAddress),
261262
recycledNoncesKey(chainId, walletAddress),
262263
sentNoncesKey(chainId, walletAddress),
264+
nonceHistoryKey(chainId, walletAddress),
263265
]);
264266
await redis.del(keys);
265267
}

src/worker/tasks/nonce-health-check-worker.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,10 @@ async function getCurrentNonceState(
116116
};
117117
}
118118

119-
function nonceHistoryKey(walletAddress: Address, chainId: number) {
119+
/**
120+
* Stores a list of onchain vs sent nonces to check if the nonce is stuck over time.
121+
*/
122+
export function nonceHistoryKey(chainId: number, walletAddress: Address) {
120123
return `nonce-history:${chainId}:${getAddress(walletAddress)}`;
121124
}
122125

@@ -128,15 +131,15 @@ async function getHistoricalNonceStates(
128131
chainId: number,
129132
periods: number,
130133
): Promise<NonceState[]> {
131-
const key = nonceHistoryKey(walletAddress, chainId);
134+
const key = nonceHistoryKey(chainId, walletAddress);
132135
const historicalStates = await redis.lrange(key, 0, periods - 1);
133136
return historicalStates.map((state) => JSON.parse(state));
134137
}
135138

136139
// Update nonce history
137140
async function updateNonceHistory(walletAddress: Address, chainId: number) {
138141
const currentState = await getCurrentNonceState(walletAddress, chainId);
139-
const key = nonceHistoryKey(walletAddress, chainId);
142+
const key = nonceHistoryKey(chainId, walletAddress);
140143

141144
await redis
142145
.multi()

0 commit comments

Comments
 (0)