From fb3e5b932ce632395ebea55a297ac6e035e2b613 Mon Sep 17 00:00:00 2001 From: Phillip Ho Date: Sat, 25 Jan 2025 11:26:18 +0800 Subject: [PATCH 1/3] chore: Allow deleting stored nonces without resyncing yet --- .../routes/backend-wallet/reset-nonces.ts | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/server/routes/backend-wallet/reset-nonces.ts b/src/server/routes/backend-wallet/reset-nonces.ts index f94be5b69..68a13c6f6 100644 --- a/src/server/routes/backend-wallet/reset-nonces.ts +++ b/src/server/routes/backend-wallet/reset-nonces.ts @@ -21,6 +21,10 @@ const requestBodySchema = Type.Object({ description: "The backend wallet address to reset nonces for. Omit to reset all backend wallets.", }), + resetOnchainNonce: Type.Boolean({ + description: "Resets the nonce to the onchain value. (Default: true)", + default: true, + }), }); const responseSchema = Type.Object({ @@ -61,7 +65,11 @@ export const resetBackendWalletNoncesRoute = async ( }, }, handler: async (req, reply) => { - const { chainId, walletAddress: _walletAddress } = req.body; + const { + chainId, + walletAddress: _walletAddress, + resetOnchainNonce, + } = req.body; // If chain+wallet are provided, only process that wallet. // Otherwise process all used wallets that has nonce state. @@ -70,19 +78,21 @@ export const resetBackendWalletNoncesRoute = async ( ? [{ chainId, walletAddress: getAddress(_walletAddress) }] : await getUsedBackendWallets(); - const RESYNC_BATCH_SIZE = 50; - for (let i = 0; i < backendWallets.length; i += RESYNC_BATCH_SIZE) { - const batch = backendWallets.slice(i, i + RESYNC_BATCH_SIZE); + const BATCH_SIZE = 50; + for (let i = 0; i < backendWallets.length; i += BATCH_SIZE) { + const batch = backendWallets.slice(i, i + BATCH_SIZE); // Delete nonce state for these backend wallets. await deleteNoncesForBackendWallets(backendWallets); - // Resync nonces for these backend wallets. - await Promise.allSettled( - batch.map(({ chainId, walletAddress }) => - syncLatestNonceFromOnchain(chainId, walletAddress), - ), - ); + if (resetOnchainNonce) { + // Resync nonces for these backend wallets. + await Promise.allSettled( + batch.map(({ chainId, walletAddress }) => + syncLatestNonceFromOnchain(chainId, walletAddress), + ), + ); + } } reply.status(StatusCodes.OK).send({ From 956230f013e94ebac57550e2f3d85c163364c28d Mon Sep 17 00:00:00 2001 From: Phillip Ho Date: Sat, 25 Jan 2025 11:28:07 +0800 Subject: [PATCH 2/3] rename --- src/server/routes/backend-wallet/reset-nonces.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/server/routes/backend-wallet/reset-nonces.ts b/src/server/routes/backend-wallet/reset-nonces.ts index 68a13c6f6..74f2d237a 100644 --- a/src/server/routes/backend-wallet/reset-nonces.ts +++ b/src/server/routes/backend-wallet/reset-nonces.ts @@ -21,8 +21,9 @@ const requestBodySchema = Type.Object({ description: "The backend wallet address to reset nonces for. Omit to reset all backend wallets.", }), - resetOnchainNonce: Type.Boolean({ - description: "Resets the nonce to the onchain value. (Default: true)", + syncOnchainNonces: Type.Boolean({ + description: + "Resync nonces to match the onchain transaction count for your backend wallets. (Default: true)", default: true, }), }); @@ -68,7 +69,7 @@ export const resetBackendWalletNoncesRoute = async ( const { chainId, walletAddress: _walletAddress, - resetOnchainNonce, + syncOnchainNonces, } = req.body; // If chain+wallet are provided, only process that wallet. @@ -85,7 +86,7 @@ export const resetBackendWalletNoncesRoute = async ( // Delete nonce state for these backend wallets. await deleteNoncesForBackendWallets(backendWallets); - if (resetOnchainNonce) { + if (syncOnchainNonces) { // Resync nonces for these backend wallets. await Promise.allSettled( batch.map(({ chainId, walletAddress }) => From 42240ff6b4b26867672cd578f8d5db4cfb7aaf86 Mon Sep 17 00:00:00 2001 From: Phillip Ho Date: Sat, 25 Jan 2025 11:39:36 +0800 Subject: [PATCH 3/3] also delete nonce-history --- src/shared/db/wallets/wallet-nonce.ts | 4 +++- src/worker/tasks/nonce-health-check-worker.ts | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/shared/db/wallets/wallet-nonce.ts b/src/shared/db/wallets/wallet-nonce.ts index c730533ce..c7c0ea148 100644 --- a/src/shared/db/wallets/wallet-nonce.ts +++ b/src/shared/db/wallets/wallet-nonce.ts @@ -10,6 +10,7 @@ import { normalizeAddress } from "../../utils/primitive-types"; import { redis } from "../../utils/redis/redis"; import { thirdwebClient } from "../../utils/sdk"; import { updateNonceMap } from "./nonce-map"; +import { nonceHistoryKey } from "../../../worker/tasks/nonce-health-check-worker"; /** * Get all used backend wallets. @@ -45,7 +46,7 @@ export const getUsedBackendWallets = async ( /** * The "last used nonce" stores the last nonce submitted onchain. - * Example: "25" + * Example: 25 -> nonce 25 is onchain, nonce 26 is unused or inflight. */ export const lastUsedNonceKey = (chainId: number, walletAddress: Address) => `nonce:${chainId}:${normalizeAddress(walletAddress)}`; @@ -260,6 +261,7 @@ export async function deleteNoncesForBackendWallets( lastUsedNonceKey(chainId, walletAddress), recycledNoncesKey(chainId, walletAddress), sentNoncesKey(chainId, walletAddress), + nonceHistoryKey(chainId, walletAddress), ]); await redis.del(keys); } diff --git a/src/worker/tasks/nonce-health-check-worker.ts b/src/worker/tasks/nonce-health-check-worker.ts index 839fb794b..b75adf6e7 100644 --- a/src/worker/tasks/nonce-health-check-worker.ts +++ b/src/worker/tasks/nonce-health-check-worker.ts @@ -116,7 +116,10 @@ async function getCurrentNonceState( }; } -function nonceHistoryKey(walletAddress: Address, chainId: number) { +/** + * Stores a list of onchain vs sent nonces to check if the nonce is stuck over time. + */ +export function nonceHistoryKey(chainId: number, walletAddress: Address) { return `nonce-history:${chainId}:${getAddress(walletAddress)}`; } @@ -128,7 +131,7 @@ async function getHistoricalNonceStates( chainId: number, periods: number, ): Promise { - const key = nonceHistoryKey(walletAddress, chainId); + const key = nonceHistoryKey(chainId, walletAddress); const historicalStates = await redis.lrange(key, 0, periods - 1); return historicalStates.map((state) => JSON.parse(state)); } @@ -136,7 +139,7 @@ async function getHistoricalNonceStates( // Update nonce history async function updateNonceHistory(walletAddress: Address, chainId: number) { const currentState = await getCurrentNonceState(walletAddress, chainId); - const key = nonceHistoryKey(walletAddress, chainId); + const key = nonceHistoryKey(chainId, walletAddress); await redis .multi()