Skip to content

Support multiple named wallets per app install in both demos - #63

Merged
jamaljsr merged 19 commits into
mainfrom
pr/multi-wallet-per-app-1ac84c
Jul 29, 2026
Merged

Support multiple named wallets per app install in both demos#63
jamaljsr merged 19 commits into
mainfrom
pr/multi-wallet-per-app-1ac84c

Conversation

@jamaljsr

Copy link
Copy Markdown
Member

Summary

The demos used to hard-bind one wallet per browser origin or device install: one dataDir, one passkey marker, and a network dropdown that silently rewired the same wallet between networks. This reworks both demo apps (web and React Native) around a wallet registry, so one install can hold many wallets side by side.

The user-facing shape, identical in both demos:

  • First run shows a create form: wallet name, a network picker (signet, testnet, regtest behind a dev gate), and for regtest an advanced endpoints section. The network is chosen once, at creation, and bound to the wallet permanently; there is no post-create network switcher.
  • Returning users land on a wallet list (name, network chip, unlock-kind icon, last used) and pick a wallet to reach its unlock screen. Switching wallets is stop-then-start on the single runtime; nothing runs concurrently.
  • Settings gains the wallet identity (name, network, created date) with switch and delete actions; the old "wipe everything" affordance survives as an explicit "Clear all data" on the list footer.
  • A wallet that predates the registry migrates automatically into it and asks for its network once, on first open, keeping its passkey or password identity.

This is demo-layer only: no packages/* source changes, no new SDK surface. The registry is localStorage on web and AsyncStorage on React Native, deliberately app-local, since wallet bookkeeping is a host-app concern the SDK does not prescribe.

Technical Notes

  • One runtime, sequential wallets. The daemon is a per-origin/per-process singleton (wasm Web Lock on web, gomobile package singleton on RN), so the registry model is sequential switching, not concurrency. True multi-profile concurrency remains the leader-tab follow-up tracked from Fail fast with wallet_locked when the wallet is open in another tab #56.
  • Per-wallet isolation by dataDir. Every new wallet gets its own /wallets/<id> (web OPFS) or <defaultRoot>/<id> (RN) directory. A migrated legacy wallet keeps the bare default root it already lives in, which is why deletion guards below exist.
  • Deletion semantics differ by platform, deliberately. Web removal is list-only (OPFS stores flat, hash-named files, so surgical per-wallet deletion is not safely expressible); RN removal really deletes the wallet's directory via expo-file-system. On both, a legacy root-dwelling entry skips the disk wipe so it can never take sibling wallets' data with it, and the confirm copy states the exception.
  • Network permanence over convenience. The picker binds at creation because the daemon's on-disk stores are per-network; rewiring a live dataDir across networks is exactly the foot-gun the old dropdown invited. Legacy entries record their network only after a successful unlock proves the guess, and a wrong guess loops back to the picker instead of poisoning the entry.
  • Restores stamp late. The registry marks a wallet's unlock kind only once the restored wallet is actually up, so a restore that dies before then re-renders the restore form with the preserved failure instead of the "data missing" screen.
  • Dev gates. Regtest is hidden behind ?regtest=1 on web and a long-press on the brand mark on RN, keeping the hosted-network demo path clean.
  • RN has no hermetic rig. The web changes are covered by the Playwright suite (extended from 5 to 8 scenarios: multi-wallet identity separation, legacy migration, removal/first-run restore). The RN half ships a manual verification checklist in the demo README; there is no mock target for a compiled-in native runtime.

Steps to Test

CI covers build, typecheck, and the unit suites. The end-to-end checks:

Web (hermetic, no regtest stack needed):

  1. WAVELENGTH_DIR="$(cd ../wavelength && pwd)" pnpm --filter web-wallet-demo run wasm:local
  2. pnpm --filter web-wallet-demo run build
  3. pnpm --filter web-wallet-demo run test - exercises create, two-wallet switching, legacy migration, removal, and the data-missing recovery path.

React Native (manual, device-driven):

  1. pnpm --filter @lightninglabs/wavelength-react-native run fetch-bindings
  2. cd apps/rn-wallet-demo && npx expo run:android
  3. Walk the "Multi-wallet manual verification" checklist at the end of the demo README.

Related Issues & Pull Requests

Depends on:

jamaljsr added 19 commits July 29, 2026 10:59
Port the web demo's App.tsx state machine (registry-backed wallet
selection, pre-start list/setup/choose-network routing, legacy
network re-guessing, mode-aware onboarding) into WalletApp.tsx. Real
per-wallet deletion (stop, wipe the entry's data directory, drop the
registry row) backs both the list's onRemove and the data-missing
screen's onRemove; a new onWipeAll clears the whole registry, the
shared data root and the legacy single-wallet markers for the list's
footer wipe button.

StoppedScreen and ErrorScreen gain a "back to wallets" affordance to
match; the old single-form ConnectScreen is dropped along with the
routing that used it.
WipeDataButton's copy understated the blast radius of the wallet-list
footer's clear-all wipe. Give it a scope prop so the footer gets plural
"Clear all data" copy while ErrorScreen keeps the singular per-wallet
wording that matches its actual wipeCurrentEntry call.

Append a manual verification checklist to the README covering the
multi-wallet flows this app has no hermetic smoke test for.
@jamaljsr jamaljsr self-assigned this Jul 29, 2026
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@jamaljsr
jamaljsr merged commit c51d689 into main Jul 29, 2026
3 checks passed
@jamaljsr
jamaljsr deleted the pr/multi-wallet-per-app-1ac84c branch July 29, 2026 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant