feat(transactions): nonce-tracker integration and retry logic for sponsor-builder#312
Open
feat(transactions): nonce-tracker integration and retry logic for sponsor-builder#312
Conversation
…-enable-collateral subcommand Syncs with aibtc-mcp-server v1.46.0 which adds zest_enable_collateral tool (PR #423, closes #422). Documents the zest-enable-collateral CLI subcommand and adds it to the mcp-tools frontmatter for discoverability. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sponsor-builder
Phase 1a + 1b from docs/nonce-strategy-alignment-plan.md:
1a. New retry-strategy.ts with classifyRelayError / classifyBroadcastError helpers
- Classifies relay /sponsor error codes: NONCE_CONFLICT, BROADCAST_FAILED,
NONCE_DO_UNAVAILABLE, SPONSOR_FAILED, RATE_LIMITED, SENDER_NONCE_CONFLICT
- Classifies direct Hiro broadcast errors: ConflictingNonceInMempool, BadNonce
- RetryInfo: { retryable, delayMs, relaySideConflict, senderSideConflict, category }
- sleep() utility co-located with classification logic
1b. Modified sponsor-builder.ts with nonce-tracker integration
- Import acquireNonce/releaseNonce from nonce-tracker
- sponsoredContractCallWithRetry(): acquires sender nonce, builds tx with
explicit nonce, retries on relay-side conflicts (same tx hex) and
sender-side conflicts (re-acquire nonce, rebuild tx)
- transferStxSponsoredWithRetry(): same pattern for STX transfers
- submitToSponsorRelay() is now exported for external retry resubmission
- Existing sponsoredContractCall/transferStxSponsored unchanged (backward compat)
Also exports retry-strategy from transactions/index.ts.
Resolves ConflictingNonceInMempool errors in sequential Zest supply ops
when multiple dispatch cycles broadcast sponsored txs concurrently.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tfireubs-ui
approved these changes
Apr 8, 2026
Contributor
tfireubs-ui
left a comment
There was a problem hiding this comment.
Clean implementation. The retry-strategy error classification covers all known relay failure modes (NONCE_CONFLICT, BROADCAST_FAILED, SENDER_NONCE_CONFLICT, RATE_LIMITED, transient). Nonce lifecycle is correct: acquire → build → submit → release(success/rejected/broadcast). The sender-side conflict path correctly releases as 'rejected' and re-acquires before rebuild, preventing nonce leaks.
Two minor observations (not blocking):
- The
zest-enable-collateraladdition to defi/SKILL.md looks like it belongs in a separate PR (unrelated to nonce/retry) - Both
sponsoredContractCallWithRetryandtransferStxSponsoredWithRetryshare identical retry loop logic — could be extracted to a shared helper, but the duplication is minimal and keeping them separate is fine for readability.
LGTM.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
retry-strategy.ts(Phase 1a): shared error classification engine for all transaction paths. Classifies relay/sponsorcodes (NONCE_CONFLICT, BROADCAST_FAILED, NONCE_DO_UNAVAILABLE, SPONSOR_FAILED, RATE_LIMITED, SENDER_NONCE_CONFLICT) and direct Hiro broadcast errors (ConflictingNonceInMempool, BadNonce). Includessleep()utility.sponsor-builder.ts(Phase 1b): addssponsoredContractCallWithRetry()andtransferStxSponsoredWithRetry()that acquire sender nonces from nonce-tracker before building transactions. Retries relay-side conflicts with same serialized tx hex; re-acquires nonce and rebuilds on sender-side conflicts.submitToSponsorRelay()is now exported.transactions/index.ts: exportsretry-strategy.js.Existing
sponsoredContractCallandtransferStxSponsoredare unchanged — backward compatible.Motivation
Sequential Zest supply ops in back-to-back dispatch cycles were generating
ConflictingNonceInMempoolerrors becausemakeContractCallauto-fetches sender nonce from Hiro on each call, but Hiro's mempool view lags behind transactions we've already broadcast. The nonce-tracker serialises nonce assignment across concurrent processes using a file lock, preventing collisions.Test plan
bun run typecheckpasses (verified locally — clean)sponsoredContractCallWithRetryhandles relay NONCE_CONFLICT (resubmits same hex)sponsoredContractCallWithRetryhandles ConflictingNonceInMempool (re-acquires nonce, rebuilds tx)sponsoredContractCallWithRetrysucceeds on first attempt with correct noncesponsoredContractCallcallers unaffected🤖 Generated with Claude Code