fix: harden popup lifecycle against hangs and duplicates#39
Merged
BitHighlander merged 2 commits intodevelopfrom Apr 21, 2026
Merged
fix: harden popup lifecycle against hangs and duplicates#39BitHighlander merged 2 commits intodevelopfrom
BitHighlander merged 2 commits intodevelopfrom
Conversation
Addresses three symptoms: popup hanging open after completion, multiple
popup windows opening per request, and the approval promise never
resolving when the user X's the popup.
methods.ts
- Replace in-memory isPopupOpen flag with chrome.windows.getAll lookup
that survives service worker restarts. Focuses an existing popup
(matched by URL) instead of opening a second one.
- Register chrome.windows.onRemoved exactly once at module load and
fan out to per-request subscribers, instead of adding a new listener
on every openPopup() call (previously leaked forever).
- requireApproval now resolves {success:false} when the popup closes
without an eth_sign_response, so chain handlers no longer hang and
the dapp's RPC call terminates.
chain handlers
- Every transaction_complete / signature_complete / transaction_error
message now carries eventId so the popup can match it to the right
in-flight request. ethereum threads the id through signMessage /
signTypedData; solana's buildEvent mutates requestInfo.id so the
match works end-to-end.
Transaction.tsx
- Ignores completion/error messages whose eventId doesn't match the
current event. Previously any signature_complete would close the
popup, slamming the window on unrelated queued requests.
ethereumHandler.ts
- Removes dead duplicate openPopup / requireUnlock (never called,
would have bypassed dedup if it had been).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
The outer catch in handleWalletRequest sends transaction_error to the popup but was the one remaining site missing the eventId scope tag. Combined with Transaction.tsx's backward-compat rule that accepts unscoped messages, a thrown chain handler would surface its error on the wrong event if two events were queued — exactly the cross-contamination this PR is fixing everywhere else. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Fixes three recurring popup bugs:
Root causes
Hang: Chain handlers broadcast `signature_complete` / `transaction_complete` without any event identifier. The popup's `Transaction.tsx` closes the window on ANY such message — including messages destined for a different queued request — while the corresponding `requireApproval()` promise keeps waiting for the original response.
Duplicates: `methods.ts` tracks "popup open" with an in-memory flag. In MV3 the service worker is torn down frequently, so the flag resets to `false` while the popup window is still visible; the next request opens a second window. A parallel, unused `openPopup` in `ethereumHandler.ts` had no dedup at all.
X → hang: `requireApproval()` only resolves when an `eth_sign_response` message arrives. Closing the popup sent no such message, so the promise never settled; the background handler stayed awaiting and the dapp's RPC call blocked until its own timeout.
Changes
`chrome-extension/src/background/methods.ts`
All chain handlers (`ethereum`, `solana`, `bitcoin`, `bitcoincash`, `dogecoin`, `litecoin`, `dash`, `thorchain`, `cosmos`, `osmosis`, `maya`, `ripple`)
`pages/popup/src/components/Transaction.tsx`
`chrome-extension/src/background/chains/ethereumHandler.ts`
Test plan
🤖 Generated with Claude Code