Conversation
🦋 Changeset detectedLatest commit: 0bc57ba The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@Kropiunig is attempting to deploy a commit to the Wevm Team on Vercel. A member of the Team first needs to authorize it. |
Contributor
Author
|
Hi @jxom — pinging for visibility. This PR adds dropped-transaction detection to |
bdf8494 to
69e0d6e
Compare
commit: |
Contributor
Author
…f polling forever When a transaction is evicted from the mempool (e.g. during a gas spike or when replaced by a same-nonce tx with higher fees), both getTransaction and getTransactionReceipt fail on every poll cycle. The current code silently returns in this case, creating an infinite loop of wasted RPC calls until the 180s timeout fires. This adds a `notFoundCount` tracker that increments each time the transaction cannot be found. After `retryCount` consecutive blocks without locating it, the promise rejects with a new `TransactionDroppedError` that explains what happened and how to recover. The detection only activates when `checkReplacement` is true (the default), preserving backward compatibility for callers that disable replacement checking. Closes wevm#3875
f78f8fb to
0bc57ba
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.
Problem
waitForTransactionReceiptpolls indefinitely when a transaction has been dropped from the mempool. This happens in a common real-world scenario during gas spikes:getTransaction(hash)starts returningTransactionNotFoundError(the node no longer knows about the pending tx)getTransactionReceipt(hash)also fails (tx was never mined)!transactioncheck silentlyreturns, continuing the poll loopwithRetrycycle (default 6 retries with exponential backoff ≈ 13s), producing ~6 wasted RPC calls per cycle, until the 180s timeout fires with a genericWaitForTransactionReceiptTimeoutErrorIf
timeoutis set to0/undefined, the promise never settles.This is #3875. The issue also affects the "speed-up" flow: when a user replaces a dropped tx with a same-nonce tx that gets mined under a different hash, the original
waitForTransactionReceiptcan't detect the replacement becausetransactionwas never populated (the evicted tx was gone before the firstgetTransactioncall succeeded).Solution
Track consecutive blocks where neither
getTransactionnorgetTransactionReceiptcan locate the transaction. AfterretryCountsuch blocks (default 6, matching the per-block retry semantics), reject with a newTransactionDroppedErrorinstead of polling forever.Design decisions
Scoped to
checkReplacement: true(the default): Drop detection only activates when replacement checking is enabled, because that's the code path that callsgetTransaction. WhencheckReplacement: false, the function still falls through to the timeout — this preserves backward compatibility for callers that explicitly opted out of replacement checking.Reuses
retryCountas the block threshold: Rather than introducing a new parameter, the existingretryCount(default 6) doubles as the number of consecutive blocks to tolerate before considering the tx dropped. This keeps the API surface unchanged while providing reasonable defaults (~72s on mainnet at 12s blocks).No false positives for slow RPCs: If a slow RPC eventually returns the transaction (e.g., after 2-3 blocks of lag),
transactiongets populated andnotFoundCountbecomes irrelevant — the normal replacement detection path takes over.Changes
src/errors/transaction.tsTransactionDroppedErrorwith actionable meta messagessrc/actions/public/waitForTransactionReceipt.tsnotFoundCounttracker + drop detection in the!transactioncatch branchsrc/index.tsTransactionDroppedErrorandTransactionDroppedErrorTypesrc/actions/public/waitForTransactionReceipt.test.tscheckReplacement: falsebypass4 files changed, ~150 lines added.
Tests
dropped transactions > rejects with TransactionDroppedError when transaction is evicted from mempoolMocks both
getTransactionandgetTransactionReceiptto consistently throw, simulating a fully evicted tx. Asserts the promise rejects withTransactionDroppedErrorwithin the retry threshold.dropped transactions > does not reject prematurely when transaction appears after initial failuresMocks
getTransactionto fail twice then fall through to the real implementation. Sends and mines a real transaction. Asserts the receipt resolves successfully despite the initial not-found responses (slow RPC simulation).dropped transactions > checkReplacement: false does not trigger drop detectionWith replacement checking disabled, the function should fall through to the timeout rather than the drop detection path. Asserts
WaitForTransactionReceiptTimeoutErroris thrown, notTransactionDroppedError.Future work
The remaining gap from #3875 is nonce-based replacement detection for evicted transactions: when the original tx was evicted before
getTransactioncould read it, we don't know the sender address or nonce, so we can't search recent blocks for a same-nonce replacement. This could be addressed by adding optionalfrom/nonceparameters toWaitForTransactionReceiptParametersin a follow-up PR.