fix(web3): don't double-send sponsored transactions on slow Turnkey confirmations#1713
Merged
Merged
Conversation
… signing The Turnkey sponsorship poll gave up after 30s and returned null, which the caller read as "sponsorship failed" and retried via direct signing. A slow sponsored send and its direct-signed retry then landed a block apart, and the second reverted. Wait up to 120s and return the tx hash as soon as Turnkey broadcasts one so the node reports that transaction's real on-chain outcome. On a submitted but unconfirmed outcome (timeout or repeated status-API failures) raise a typed SponsoredTxPendingError so the caller fails the step instead of re-sending. A reverted sponsored receipt raises SponsoredTxRevertError; its reason is recovered by a read-only eth_call replay of the already-mined tx (never a second broadcast), falling back to a generic message when it cannot be decoded.
Extract resolveSponsoredSendError so write-contract, transfer-token, approve-token, and transfer-funds decide fall-back-vs-fail in one place, and surface the tx hash in the reverted-node error. Every write op now treats a reverted or unconfirmed sponsored send as a terminal node result and never re-broadcasts a second transaction.
…ndler Add cases for returning the hash as soon as Turnkey broadcasts, raising SponsoredTxPendingError on timeout and repeated status-API failures, and the shared resolveSponsoredSendError fall-back decision.
d961f38 to
318ba80
Compare
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
A gas-sponsored (Turnkey) web3 write could be broadcast twice from the same wallet, one block apart, with the second transaction reverting. The sponsorship poll waited only 30s for Turnkey to confirm; on timeout it returned
null, the caller read that as "sponsorship failed" and re-sent the transaction via direct signing. The sponsored send and its direct-signed retry then raced each other on-chain.Root cause
pollForTxHashreturnednullon a 30s timeout without distinguishing "still pending" from "failed before broadcast".writeContractCore(and the other write steps) treatednullas a reason to fall back to direct signing, producing a second transaction while the first was still in flight. Separately, a reverted sponsored receipt threw a genericErrorthat also fell through to the direct-signing fallback.Fix
SponsoredTxPendingError; the step fails cleanly instead of re-broadcasting.SponsoredTxRevertError, which callers already report as a terminal node result rather than retrying.resolveSponsoredSendErrorand route all four web3 write steps (write-contract,transfer-token,approve-token,transfer-funds) through it, so the "never double-send" rule is identical everywhere and cannot drift.Direct signing is now used only for genuine pre-broadcast failures (unsupported chain, credits exhausted, Turnkey rejected before broadcast).
Tests
pnpm type-checkand lint clean; full unit suite passes. Added coverage for returning the hash as soon as Turnkey broadcasts, raising the pending error on timeout and on repeated status-API failures, and the shared fall-back decision.