Skip to content
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
31 changes: 21 additions & 10 deletions src/server/routes/backend-wallet/reset-nonces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ const requestBodySchema = Type.Object({
description:
"The backend wallet address to reset nonces for. Omit to reset all backend wallets.",
}),
syncOnchainNonces: Type.Boolean({
description:
"Resync nonces to match the onchain transaction count for your backend wallets. (Default: true)",
default: true,
}),
});

const responseSchema = Type.Object({
Expand Down Expand Up @@ -61,7 +66,11 @@ export const resetBackendWalletNoncesRoute = async (
},
},
handler: async (req, reply) => {
const { chainId, walletAddress: _walletAddress } = req.body;
const {
chainId,
walletAddress: _walletAddress,
syncOnchainNonces,
} = req.body;

// If chain+wallet are provided, only process that wallet.
// Otherwise process all used wallets that has nonce state.
Expand All @@ -70,19 +79,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 (syncOnchainNonces) {
// Resync nonces for these backend wallets.
await Promise.allSettled(
batch.map(({ chainId, walletAddress }) =>
syncLatestNonceFromOnchain(chainId, walletAddress),
),
);
}
}

reply.status(StatusCodes.OK).send({
Expand Down
4 changes: 3 additions & 1 deletion src/shared/db/wallets/wallet-nonce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)}`;
Expand Down Expand Up @@ -260,6 +261,7 @@ export async function deleteNoncesForBackendWallets(
lastUsedNonceKey(chainId, walletAddress),
recycledNoncesKey(chainId, walletAddress),
sentNoncesKey(chainId, walletAddress),
nonceHistoryKey(chainId, walletAddress),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also delete nonce history when we reset nonces.

]);
await redis.del(keys);
}
Expand Down
9 changes: 6 additions & 3 deletions src/worker/tasks/nonce-health-check-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just swapping the args to match the patterns of the others

return `nonce-history:${chainId}:${getAddress(walletAddress)}`;
}

Expand All @@ -128,15 +131,15 @@ async function getHistoricalNonceStates(
chainId: number,
periods: number,
): Promise<NonceState[]> {
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));
}

// 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()
Expand Down
Loading