Skip to content

fix(web3): don't double-send sponsored transactions on slow Turnkey confirmations#1713

Merged
joelorzet merged 3 commits into
stagingfrom
fix/turnkey-sponsored-double-send
Jul 6, 2026
Merged

fix(web3): don't double-send sponsored transactions on slow Turnkey confirmations#1713
joelorzet merged 3 commits into
stagingfrom
fix/turnkey-sponsored-double-send

Conversation

@joelorzet

Copy link
Copy Markdown

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

pollForTxHash returned null on a 30s timeout without distinguishing "still pending" from "failed before broadcast". writeContractCore (and the other write steps) treated null as 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 generic Error that also fell through to the direct-signing fallback.

Fix

  • Raise the confirm wait to 120s and return the tx hash as soon as Turnkey assigns one. The poll stops the instant a hash appears (or a terminal revert), so fast confirmations are unaffected; the timeout is only the maximum.
  • Once a send is accepted by Turnkey, never fall back to direct signing. A submitted-but-unconfirmed outcome (timeout or repeated status-API failures) raises the new SponsoredTxPendingError; the step fails cleanly instead of re-broadcasting.
  • A reverted sponsored receipt now raises the typed SponsoredTxRevertError, which callers already report as a terminal node result rather than retrying.
  • Extract resolveSponsoredSendError and 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-check and 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.

joelorzet added 3 commits July 6, 2026 14:47
… 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.
@joelorzet joelorzet force-pushed the fix/turnkey-sponsored-double-send branch from d961f38 to 318ba80 Compare July 6, 2026 17:49
@joelorzet joelorzet merged commit 8f4ec8a into staging Jul 6, 2026
46 checks passed
@joelorzet joelorzet deleted the fix/turnkey-sponsored-double-send branch July 6, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant