feat: adds a standalone transaction-clearer bot for recovering from nonce backlogs. #4915
feat: adds a standalone transaction-clearer bot for recovering from nonce backlogs. #4915
Conversation
Signed-off-by: Pablo Maldonado <pablo@umaproject.org>
| feeBumpNumerator: Number(env.FEE_BUMP_NUMERATOR) || 12, | ||
| feeBumpDenominator: Number(env.FEE_BUMP_DENOMINATOR) || 10, | ||
| replacementAttempts: Number(env.REPLACEMENT_ATTEMPTS) || 3, |
There was a problem hiding this comment.
nit: these are named differently in bot-oo
| export const initMonitoringParams = async (env: NodeJS.ProcessEnv): Promise<MonitoringParams> => { | ||
| const base = await initBaseMonitoringParams(env); | ||
|
|
||
| const nonceBacklogConfig: NonceBacklogConfig = { |
There was a problem hiding this comment.
bot-oo has some additional parsing checks that would be worth also using here and could reuse parsePositiveInt logic
| return { latestNonce, pendingNonce }; | ||
| } | ||
|
|
||
| export async function clearStuckTransactions( |
There was a problem hiding this comment.
might be more maintainable in the future if we have a single tx clearing module here and bot-oo would just reuse it
- Revert bot-oo/index.ts to master (nonce handling moved to transaction-clearer) - Replace feeBumpNumerator/Denominator with simpler feeBumpPercent (default 20%) - Add parsePositiveInt that throws on invalid values instead of silently using defaults Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move clearStuckTransactions and related helpers to bot-utils/transactionClearing.ts so both transaction-clearer and bot-oo can reuse the same logic. Signed-off-by: Pablo Maldonado <pablo@umaproject.org>
828aa01 to
8744689
Compare
Signed-off-by: Pablo Maldonado <pablo@umaproject.org>
| } | ||
| } | ||
|
|
||
| if (!cleared) { |
There was a problem hiding this comment.
Is it worth continuing with clearing next nonces if this one failed all attempts? Or we shall better reevaluate current state of pending-latest as some earlier tx might have been mined or another bot instance submits new tx.
There was a problem hiding this comment.
good point. Now re-evaluates nonce state after failed attempts before continuing so it handles cases where the stuck tx got mined elsewhere or new txs were submitted.
| const { latestNonce: finalLatestNonce, pendingNonce: finalPendingNonce } = await getNonces(provider, botAddress); | ||
| const finalBacklog = finalPendingNonce - finalLatestNonce; | ||
|
|
||
| if (finalBacklog < nonceBacklogConfig.nonceBacklogThreshold) { |
There was a problem hiding this comment.
shouldn't the threshold be used just to decide if we need to start tx clearing? but once we are clearing, shouldn't we clear all pending ones?
There was a problem hiding this comment.
You're right! Changed to check finalBacklog === 0. Threshold now only gates when to start clearing.
…acklog - After failing to clear a nonce, re-fetch the nonce state before continuing to handle cases where the tx was cleared by another source - Change final success check from threshold comparison to zero backlog, since threshold should only gate when to start clearing
| } | ||
|
|
||
| if (!cleared) { | ||
| logger.error({ |
There was a problem hiding this comment.
this could end up paging indefinitely if clearing transactions end up underpriced and we are not updating baseFeeData.
There was a problem hiding this comment.
Fixed! Now refreshing baseFeeData for each nonce iteration to handle rising gas prices.
| const refreshed = await getNonces(provider, botAddress); | ||
| currentLatestNonce = refreshed.latestNonce; | ||
| currentPendingNonce = refreshed.pendingNonce; |
There was a problem hiding this comment.
shouldn't this exit the loop after nonceBacklogConfig.replacementAttempts if there are other reasons for clearing tx to fail (e.g. underpriced or bot is just out of funds) if latestNonce did not change?
There was a problem hiding this comment.
Good catch! Added a check - if latestNonce hasn't advanced after failed clearing attempts, we now exit the loop to prevent infinite iteration. Will retry on next bot cycle with fresh gas prices.
- Refresh baseFeeData for each nonce to handle rising gas prices - Exit loop if latestNonce doesn't advance after failed clearing attempts (e.g., underpriced txs, bot out of funds) Addresses review feedback from Reinis-FRP. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Problem
When a transaction gets stuck (e.g., gas price too low during a spike), it blocks all subsequent transactions from the same wallet. The pending nonce can't advance until the stuck transaction is either mined or replaced.
Solution
transaction-clearer: Standalone bot that clears stuck transactions by submitting 0-value self-transactions at the stuck nonce with escalating gas fees.
Changes
transaction-clearerbotbot-utils/transactionClearing.ts(reusable by other bots if needed)Configuration (env vars)
NONCE_BACKLOG_THRESHOLD- minimum backlog to trigger clearing (default: 1)NONCE_REPLACEMENT_BUMP_PERCENT- fee bump per attempt (default: 20%)NONCE_REPLACEMENT_ATTEMPTS- max attempts per stuck nonce (default: 3)Fixes UMA-3012