feat: move pending transaction polling to background#2231
feat: move pending transaction polling to background#2231
Conversation
6522f01 to
1ee6d5f
Compare
bfdec1e to
a56fc01
Compare
a56fc01 to
f060f03
Compare
1ee6d5f to
79dc11f
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 79dc11f216
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const { pendingTransactions } = usePendingTransactionsStore.getState(); | ||
| const intervalMs = getMinPollingIntervalForPendingTxs(pendingTransactions); | ||
|
|
||
| nextPollTimeout = setTimeout(() => { | ||
| nextPollTimeout = null; | ||
| runPollCycle(); | ||
| }, intervalMs); |
There was a problem hiding this comment.
Recompute timer when pending transactions change
This schedules the next poll from a single snapshot of pendingTransactions and does not reschedule when new pending txs are added before the timeout fires. In practice, if a long-delay timer (e.g. 30s from an older mainnet tx) is already set and a fresh tx is submitted right after, that new tx will not be checked until the old timer expires, which delays mined-status propagation and dapp status updates.
Useful? React with 👍 / 👎.
| .filter((tx) => { | ||
| if (!skipTimedOutTxs) return true; | ||
| const pendingDurationMs = tx.lastSubmittedTimestamp | ||
| ? now - tx.lastSubmittedTimestamp | ||
| : 0; |
There was a problem hiding this comment.
Gate each tx poll by its own tier interval
The poll loop only excludes timed-out txs; it does not skip txs whose per-chain tier interval has not elapsed yet. That means when one fast-chain tx drives the global timer to 1–2s, all other pending txs (including slow chains configured for 15–30s) are still fetched every cycle, creating avoidable background/network load and defeating the intended tiered polling behavior.
Useful? React with 👍 / 👎.
| () => | ||
| pendingTransactions.filter( | ||
| (tx): tx is MinedTransaction => tx.status !== 'pending', | ||
| ), |
There was a problem hiding this comment.
usePendingTransactionWatcher.ts:56-62 — minedTransactions is a new array reference on every
render (.filter() always returns a new array). It's in the useEffect dep array at line 168.
When the effect calls removePendingTransactionsForAddress, the store updates,
pendingTransactions changes, minedTransactions gets a new reference, and the effect
re-fires.The old code avoided this because watchPendingTransactions was a useCallback invoked by
usePoll, not an effect triggered by store state.Suggestion: Stabilize minedTransactions with a deep comparison (e.g., serialize the
hash+chainId list) or use a ref to track which txs have already been processed:const minedTxKey = useMemo( () => minedTransactions.map((tx) => `${tx.hash}:${tx.chainId}`).join(','), [minedTransactions], ); // use minedTxKey in the dep array instead of minedTransactions
| } | ||
|
|
||
| const { pendingTransactions } = usePendingTransactionsStore.getState(); | ||
| const intervalMs = getMinPollingIntervalForPendingTxs(pendingTransactions); |
There was a problem hiding this comment.
This returns 5s even when pendingTransactions is empty, so we start a polling loop with nothing to poll
| }); | ||
| }; | ||
|
|
||
| userAssetsFetchQuery({ |
There was a problem hiding this comment.
Are we warming the cache with this?
DanielSinclair
left a comment
There was a problem hiding this comment.
Noted a few issues. These might be addressed in the upstream versions of this implementation
|
@janek26 Was this a requirement or a parallel fix that is no longer needed? |
|
I split this into tinier PRs which are now merged. Closed |

Move pending transaction polling to background
Pending transactions were only polled while the popup was open. If the user closed the popup after sending a tx (e.g. after approving an EIP-5792 batch from a dApp), we never checked the receipt. The tx stayed pending, batch status never updated, and dApps couldn’t see completion via wallet_getCallsStatus.
Receipt polling now runs in the background service worker instead of the popup, so pending txs are updated to confirmed/failed even when the popup is closed, batch status is updated when mined, and dApps can reliably detect completion.
Design choices & review focus
PR-Codex overview
This PR introduces functionality to monitor pending transactions in the background service worker, enhancing the transaction polling mechanism and updating the UI accordingly. It adds new handlers and modifies existing components to improve the transaction handling process.
Detailed summary
useWatchPendingTransactionshook.handleWatchPendingTransactionsinsrc/entries/background/handlers/handleWatchPendingTransactions.ts.handleWatchPendingTransactionsintosrc/entries/background/index.ts.PendingTransactionWatcherinsrc/entries/popup/hooks/usePendingTransactionWatcher.tsto utilize the new polling mechanism.src/core/state/networks/timing.ts.