[Bug]: The UI wallet store discards permission results and never revalidates per-account state
Nobody has claimed this yet.
- Dominant language
- Solidity
- Stars
- 4
- Forks
- 2
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 24
Description
Component
Other
Priority
P2
What happened?
Three defects in packages/ui/src/store/useWalletStore.ts with one root cause: state is captured at connect and never revalidated, and grant results are discarded.
Grants are requested and ignored. ensureSignerReady:279 discards the ensureChainSubmit() boolean, and requestWritePermissions:87-94 swallows both a non-Allocated result and a throw. A signer is returned either way. The store's comment at :112-113 records the consequence: the host then rejects every write with PermissionDenied, "often as a silent hang".
The signer is a connect-time snapshot. injected (:179) is never replaced, and the subscription at :224-228 reacts only to status === "disconnected". The SDK's attemptReconnect builds a new provider and new accounts and returns to connected, which goes unnoticed, and ensureReady:252 refuses to refresh because injected is still non-null. Later writes sign with a dead signer.
Derived state is never refreshed. userPopState is written only at services/AppInitializer.ts:34-38, and components RegisterModal.vue:391-411 gates requirementMet on it, so after an account switch or reconnect a verified user's Register button is disabled with no explanation while the search panel still offers it. handleDisconnect:207-218 also leaves transactionStatus set, wedging the timeline mid-flight under a "Not signed in" header.
There is no recovery path: connectWallet() is reachable only from two docs demos, and components/WalletButton.vue:17-20 no action, so a page reload is the only way back.
Expected behavior
- A denied or failed grant fails the write with a message naming the missing permission, instead of submitting and hanging.
- The store keys on account and provider identity rather than connection status alone, and refreshes the signer and any per-account derived state when either changes.
- Disconnect resets transaction state.
- The header offers a way to reconnect without reloading.
Reproduction
- Decline the ChainSubmit prompt, then attempt any write. The timeline stays on "Signing" with no error; the rejection appears only in the console.
- With a session established, force a host reconnect, then attempt a write. It is signed with the pre-reconnect signer.
- Switch account in the host, then open the register modal for a name requiring PoP. Register is disabled even for a verified account.
Additional context
Related to #227: a failed personhood read also yields NoStatus through safeRead, so the disabled Register button has two possible causes. This issue covers the not-refetched half.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/ui/src/store/useWalletStore.ts, tracing requestWritePermissions, ensureSignerReady, ensureReady, the connection subscription, and handleDisconnect. Then inspect services/AppInitializer.ts, components/RegisterModal.vue, and components/WalletButton.vue for derived state and reconnect behavior. Done means failed grants surface a permission error, account or provider changes refresh state, disconnect clears transaction status, and the header can reconnect.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100