MetaMask / MetaMask/metamask-mobile
Dismiss pending WalletConnect approvals when their session or pairing is deleted
- Dominant language
- TypeScript
- Stars
- 3k
- Forks
- 1.7k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 669
Description
### What is this about?
A mobile dapp can open MetaMask through WalletConnect to request a connection or transaction confirmation.
While the MetaMask approval is still pending, the user can return to the dapp and cancel the operation. The dapp then deletes the associated WalletConnect pairing or session.
MetaMask receives the deletion and removes the WalletConnect connection, but the approval created from that connection can remain open inside MetaMask.
This leaves an orphaned approval:
- The dapp has cancelled the operation.
- The WalletConnect pairing or session no longer exists.
- MetaMask still displays the connection or transaction confirmation.
- The user must manually open MetaMask and reject it.
- A transaction confirmation may remain actionable even though the requesting session has ended.
MetaMask should dismiss pending approvals associated with a deleted WalletConnect pairing or session. Approvals belonging to other sessions or origins must not be affected.
### Scenario
Scenario 1: Pending connection approval
GIVEN a dapp sends a WalletConnect session proposal
AND MetaMask displays the connection approval
WHEN the dapp cancels the connection and deletes the associated pairing
THEN MetaMask should reject and remove that connection approval
AND the approval sheet should no longer be present when MetaMask is opened
Scenario 2: Pending transaction confirmation
GIVEN a dapp has an active WalletConnect session
AND the dapp sends a transaction request
AND MetaMask displays the transaction confirmation
WHEN the dapp cancels the operation and deletes that WalletConnect session
THEN MetaMask should reject and remove confirmations associated with that session
AND the transaction must not be signed or broadcast
AND the confirmation screen should close if it is currently visible
Scenario 3: Isolation
GIVEN MetaMask has approvals from more than one origin or session
WHEN one WalletConnect pairing or session is deleted
THEN only approvals belonging to that pairing or session should be removed
AND unrelated approvals should remain unchanged
### Design
No new user interface is proposed.
MetaMask should use its existing approval rejection and confirmation dismissal behavior.
If MetaMask is currently displaying the affected approval, it should close that approval screen.
If MetaMask is in the background when the deletion is processed, the affected approval should be removed so it is not displayed when MetaMask is opened again.
### Technical Details
WalletConnect does not define a method for cancelling an individual request after it has been published. The closest available operations are deleting the unapproved pairing or deleting the established session.
For an established session, the dapp calls SignClient.disconnect(). This publishes wc_sessionDelete for that session.
MetaMask Mobile currently handles session_delete by:
1. Removing stored deeplink session information.
2. Removing the WalletConnect session listeners.
3. Deleting the session from its local session collection.
The handler does not reject pending ApprovalController requests or pending transactions created from that session.
Current handler:
https://github.com/MetaMask/metamask-mobile/blob/ea2cfff7fe75d0dd2ea7626cb492ec48b5d109fb/app/core/WalletConnect/WalletConnectV2.ts#L113-L130
A WalletConnect transaction is transferred into MetaMask's transaction flow through addTransaction(), after which the WalletConnect handler waits for trx.result:
https://github.com/MetaMask/metamask-mobile/blob/ea2cfff7fe75d0dd2ea7626cb492ec48b5d109fb/app/core/WalletConnect/WalletConnect2Session.ts#L867-L920
At that point, removing the WalletConnect session does not remove the transaction confirmation.
For a connection proposal, MetaMask creates an internal permissions approval through requestPermissions():
https://github.com/MetaMask/metamask-mobile/blob/ea2cfff7fe75d0dd2ea7626cb492ec48b5d109fb/app/core/WalletConnect/WalletConnectV2.ts#L660-L682
Deleting the associated pairing does not appear to reject that internal permissions approval.
MetaMask already contains utilities for rejecting pending approvals by origin:
https://github.com/MetaMask/metamask-mobile/blob/ea2cfff7fe75d0dd2ea7626cb492ec48b5d109fb/app/util/permissions/index.ts#L82-L120
Possible implementation:
1. Track the relationship between:
- WalletConnect pairing topic
- WalletConnect session topic
- WalletConnect request or proposal ID
- MetaMask origin/channel ID
- MetaMask approval ID
- MetaMask transaction ID, when applicable
2. When pairing deletion is received:
- Find the pending connection approval created from that pairing.
- Reject that exact approval through the normal rejection path.
- Remove the approval UI if it is visible.
3. When session_delete is received:
- Find pending approvals and transactions created from that session.
- Reject those exact approvals through their normal rejection paths.
- Ensure their transactions cannot be signed or broadcast.
- Remove their confirmation UI if visible.
- Then complete the existing session cleanup.
4. Make deletion handling idempotent:
- Repeated deletion events should be safe.
- Already-resolved requests should remain unchanged.
- Unrelated approvals must never be removed.
5. Handle races between approval and session deletion:
- If deletion wins, the transaction must not be broadcast.
- If the request was already completed before deletion, its completed result should remain unchanged.
The cleanup should use exact pairing/session ownership rather than clearing every pending approval for an origin.
### Threat Modeling Framework
What are we working on?
Cleaning up MetaMask approvals whose originating WalletConnect pairing or session has been deleted.
What does this aim to solve?
It prevents stale connection and transaction approvals from remaining inside MetaMask after the requesting dapp has cancelled the operation and ended the corresponding WalletConnect connection.
What can go wrong?
- Cleanup could remove an unrelated approval.
- A race could allow a transaction to be approved while its session is being deleted.
- A transaction could be broadcast after the dapp has cancelled the operation.
- Repeated deletion events could attempt to reject the same approval more than once.
- Closing the visible approval route could affect another confirmation displayed immediately afterward.
What are we going to do about it?
- Track exact ownership between WalletConnect topics, request IDs and MetaMask approval or transaction IDs.
- Reject only approvals owned by the deleted pairing or session.
- Make cleanup idempotent.
- Define deterministic behavior for approval/deletion races.
- Verify the approval being displayed still matches the deleted request before changing navigation.
- Add automated tests for unrelated concurrent approvals.
Did we do a good job?
The implementation is successful if deleted WalletConnect connections leave no associated pending approvals, cannot broadcast cancelled transactions, and do not affect approvals from other sessions or origins.
### Acceptance Criteria
- Deleting a WalletConnect pairing while its connection approval is pending removes that approval.
- Deleting a WalletConnect session while one of its confirmations is pending removes that confirmation.
- A removed transaction confirmation cannot sign or broadcast its transaction.
- If the affected approval is visible, MetaMask closes its approval screen.
- If MetaMask is backgrounded, the approval is not present when MetaMask is opened again.
- Only approvals belonging to the deleted pairing or session are removed.
- Approvals from other pairings, sessions and origins remain unchanged.
- Already-approved or already-rejected requests remain unchanged.
- Repeated pairing or session deletion events are handled safely.
- Approval/deletion race conditions have deterministic behavior.
- Existing WalletConnect connection, approval and rejection flows continue working.
- Automated tests cover connection approvals, transaction confirmations, background handling, repeated deletion and concurrent unrelated approvals.
### Stakeholder review needed before the work gets merged
- [ ] Engineering (needed in most cases)
- [ ] Design
- [ ] Product
- [ ] QA (automation tests are required to pass before merging PRs but not all changes are covered by automation tests - please review if QA is needed beyond automation tests)
- [ ] Security
- [ ] Legal
- [ ] Marketing
- [ ] Management (please specify)
- [ ] Other (please specify)
### References
Environment:
- Dapp platform: Flutter mobile application
- Wallet connection: WalletConnect v2 through Reown Sign
- Reown Sign version: 1.3.9
- MetaMask Mobile version: 8.11.0
- MetaMask Mobile build number: 6909
- Device and operating system: Samsung SM-S942B, Android 16 (API 36)
- Android security patch: 2026-08-05
- Installation source: Google Play Store
WalletConnect session deletion semantics:
https://github.com/WalletConnect/walletconnect-specs/blob/main/docs/specs/clients/sign/session-events.md#session_delete
Relevant MetaMask Mobile source:
https://github.com/MetaMask/metamask-mobile/blob/ea2cfff7fe75d0dd2ea7626cb492ec48b5d109fb/app/core/WalletConnect/WalletConnectV2.ts#L113-L130
https://github.com/MetaMask/metamask-mobile/blob/ea2cfff7fe75d0dd2ea7626cb492ec48b5d109fb/app/core/WalletConnect/WalletConnectV2.ts#L660-L682
https://github.com/MetaMask/metamask-mobile/blob/ea2cfff7fe75d0dd2ea7626cb492ec48b5d109fb/app/core/WalletConnect/WalletConnect2Session.ts#L867-L920
https://github.com/MetaMask/metamask-mobile/blob/ea2cfff7fe75d0dd2ea7626cb492ec48b5d109fb/app/util/permissions/index.ts#L82-L120
Contributor guide
Research direction
Start with the session_delete handler in app/core/WalletConnect/WalletConnectV2.ts, then trace addTransaction() in app/core/WalletConnect/WalletConnect2Session.ts and requestPermissions(). Review the rejection utilities in app/util/permissions/index.ts before deciding how ownership is tracked. Done means deleted pairings and sessions remove only their pending approvals and transactions, preserve unrelated requests, and handle races and repeated events safely with automated tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react-native, typescript
- Domain
- mobile, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100