fix(APP-958): prevent app crash on wallet disconnect during transaction dialogs#1215
fix(APP-958): prevent app crash on wallet disconnect during transaction dialogs#1215harryburger wants to merge 11 commits into
Conversation
|
🚀 Preview Deployment: View Here |
There was a problem hiding this comment.
Pull request overview
Prevents the app from crashing when a connected wallet disconnects while certain transaction-related dialogs are open, by introducing an explicit requiresWallet dialog definition flag and using it to unmount/close affected dialogs before they can re-render without an address.
Changes:
- Add
requiresWallet?: booleanto dialog definitions and use it inDialogRootto hide/unmount and close wallet-dependent dialogs on disconnect (while not closing during reconnects). - Mark several existing dialogs across modules/plugins as
requiresWallet: true. - Add
DialogRoottests covering connected/disconnected/reconnecting behavior and the “disconnect while open” crash scenario; add a changeset entry.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/shared/components/dialogRoot/dialogRoot.tsx | Adds wallet-awareness to dialog rendering and closes wallet-dependent dialogs on disconnect to prevent render-time assertion crashes. |
| src/shared/components/dialogRoot/dialogRoot.test.tsx | Adds unit tests to verify wallet-dependent dialog behavior across disconnect/reconnect states and prevent regressions. |
| src/shared/components/dialogProvider/dialogProvider.api.ts | Extends dialog definition API with a requiresWallet flag. |
| src/plugins/tokenPlugin/constants/tokenPluginDialogsDefinitions.ts | Marks token plugin dialogs that require a connected wallet. |
| src/plugins/sppPlugin/constants/sppPluginDialogsDefinitions.ts | Marks SPP dialog(s) that require a connected wallet. |
| src/plugins/lockToVotePlugin/constants/lockToVotePluginDialogsDefinitions.ts | Marks lock/unlock dialog as requiring a connected wallet. |
| src/plugins/gaugeVoterPlugin/constants/gaugeVoterPluginDialogsDefinitions.ts | Marks lock/unlock dialog as requiring a connected wallet. |
| src/modules/settings/constants/settingsDialogDefinitions.ts | Marks plugin uninstallation preparation dialog as requiring a connected wallet. |
| src/modules/governance/constants/governanceDialogsDefinitions.ts | Marks publish/execute/vote dialogs as requiring a connected wallet. |
| src/modules/createDao/constants/createDaoDialogsDefinitions.ts | Marks publish/prepare dialogs as requiring a connected wallet. |
| src/modules/capitalFlow/constants/capitalFlowDialogsDefinitions.ts | Marks policy preparation/transaction dispatch dialogs as requiring a connected wallet. |
| .changeset/dialog-wallet-disconnect-crash.md | Adds a patch changeset describing the crash fix and the new requiresWallet behavior. |
| .agents/shared/metrics/hits.jsonl | Appends new guardrail-metrics log entries. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
E2E results (preview) Smoke
|
5e37589 to
0ef07de
Compare
…ring-transaction-dialog
thekidnamedkd
left a comment
There was a problem hiding this comment.
Really nice fix overall — the unmount-before-throw ordering is race-free (we re-render DialogRoot before the dialog child on the same wagmi store update), the integration test reproducing the actual invariant crash is great, and I checked the flag coverage: all 5 dialogs with render-time address invariants are flagged, and the unflagged address != null matches elsewhere are benign. One thing we should fix before merge (the wagmi state-model comment inline), the rest is non-blocking polish.
Non-blocking extras:
- Test gaps we could close cheaply: reconnect-restore (address returns → dialog re-renders, close never called), multi-dialog stack (flagged top closes, unflagged below becomes visible), and open-while-already-disconnected.
- Follow-up ticket material rather than this PR: DialogRoot still renders outside the page-level error boundary, so the flag only protects dialogs someone remembered to flag — an error boundary around the dialog layer would kill the whole crash class. Might also be a good rule-skill ("dialog asserts wallet ⇒ set requiresWallet").
There was a problem hiding this comment.
Hey @harryburger, it looks good!
What I mentioned today, I was thinking about wrapping ActiveDialogComponent in an ErrorBoundary which would catch all errors, log em, and close the dialog. After thinking more about it, I think it's better to start with your more focused approach and we can consider this more general approach if needed later.
One thing only regarding your approach. Since now one must know about requiresWallet flag when creating a new dialog, let's add something like this to AGENTS.md: "When creating a new dialog which uses TransactionDialog internally pass requiresWallet flag to dialog definition.
…f-wallet-disconnects-during-transaction-dialog # Conflicts: # .agents/shared/metrics/hits.jsonl
isReconnecting is only true for wagmi's 'reconnecting' status, which is the mount-time reconnectOnMount path. A mid-session connector switch goes through 'connecting', where the address can be undefined while isReconnecting is false, so wallet-requiring dialogs were closed instead of being waited out. Expose isConnecting from useWalletAccount and close only when neither transient state is active. Also reword the requiresWallet JSDoc and the changeset to describe the behavioural contract rather than an assertion.
…ring-transaction-dialog
…f-wallet-disconnects-during-transaction-dialog # Conflicts: # .agents/shared/metrics/hits.jsonl
…ction-dialog' of github.com:aragon/app into app-958-app-crashes-if-wallet-disconnects-during-transaction-dialog
Description
Fixes the application crashing when the wallet disconnects while a transaction dialog is open (APP-958).
Wallet-requiring dialogs (vote, execute, publish proposal, token approve, …) assert a connected address during render. Disconnecting the wallet from the wallet itself while one of them was open made the assertion throw during render, and since
DialogRootrenders outside the page-level error boundary, the throw unmounted the entire application.Changes:
requiresWalletflag toIDialogComponentDefinitionsand set it on the 15 dialogs carrying the connected-wallet assertionDialogRootunmounts flagged dialogs as soon as the wallet address is gone — before they can re-render and throw — and removes them from the dialog stack once the disconnect settles. Connector switches are unaffected: while reconnecting, dialogs are only hidden and restored once the address returnsType of Change
Developer Checklist:
Review Checklist: