Skip to content

feat(web): wallet picker for the implemented adapters (Freighter, LOBSTR) - #627

Merged
collinsezedike merged 1 commit into
drydocs:mainfrom
Mamavee001:feat/wallet-picker-ui
Aug 31, 2026
Merged

feat(web): wallet picker for the implemented adapters (Freighter, LOBSTR)#627
collinsezedike merged 1 commit into
drydocs:mainfrom
Mamavee001:feat/wallet-picker-ui

Conversation

@Mamavee001

Copy link
Copy Markdown
Contributor

closes #611

Summary

wallet.ts hardcoded a single wallet: WalletAdapter instance to FreighterWallet, so LobstrWallet (merged in #537) was fully implemented and tested but never actually reachable from the app. This adds a wallet picker so users can choose between every implemented adapter and reconnect through the same one automatically next time.

Changes

apps/web/src/lib/wallet.ts

  • New WALLETS: WalletMeta[] registry — { id, name, installUrl, adapter } for each implemented wallet (Freighter, LOBSTR). Adding xBull once feat(wallet): add XBullWallet adapter #598 merges is one new entry here, nothing else.
  • getSelectedWalletId() / setSelectedWalletId(), backed by a dedicated localStorage key (meridian-selected-wallet), default to Freighter. Kept in wallet.ts itself rather than the Zustand wallet store, since store/wallet.ts already imports from wallet.ts and putting it the other way round would create a circular import.
  • The exported wallet: WalletAdapter singleton is now a dispatcher that resolves to getWalletAdapter(getSelectedWalletId()) on every call, instead of being pinned to FreighterWallet. This means useSignAndSubmit's sign() and the store's revalidate()/isAuthorized() check automatically follow whichever wallet the user actually connected through — zero changes needed to those files or their tests.

apps/web/src/hooks/useWalletConnect.ts

  • handleConnect(walletId?) now accepts an explicit wallet, defaulting to the persisted selection when omitted (this is what keeps the plain "Connect Wallet" click unaffected — see acceptance criteria below).
  • The selection is only persisted (setSelectedWalletId) on a successful connect, so a failed or cancelled attempt never silently switches which wallet later sign/reconnect calls dispatch to.
  • Returns attemptedWalletId so the no-extension fallback UI knows which wallet's install link to show.

apps/web/src/components/onboarding/WalletConnect.tsx

  • Adds a small caret button beside the existing "Connect Wallet" button that opens a dropdown listing every implemented wallet, each with an "Installed" badge (checked live via isInstalled() on open, so it reflects the extension state even without a reload).
  • The no-extension fallback link is now wallet-aware (Install {name} → that wallet's own installUrl) instead of hardcoded to freighter.app.

apps/web/src/components/dashboard/VaultPanel.tsx

  • Its own connect button/no-extension fallback (a separate entry point inside the deposit/withdraw panel) made wallet-aware too, for consistency and correctness — it also fixes a latent bug where the button's onClick={handleConnect} would have passed the raw click event as handleConnect's new walletId parameter.

Acceptance criteria

  • User can choose between all implemented wallet adapters from the connect UI, not just Freighter
  • Chosen wallet persists across sessions (localStorage, only on a successful connect)
  • WalletConnect.tsx's no-extension fallback is wallet-aware, not hardcoded to freighter.app
  • Existing Freighter connect flow is unaffected for users who don't interact with the picker — the plain button's click handler and behavior are unchanged; verified the existing Playwright e2e selectors (getByRole("button", { name: "Connect Wallet" }) scoped to <main>) still resolve to the same element with the same text, and the "Install Freighter" e2e assertion still passes because the interpolated string (Install {{name}} → "Install Freighter") renders identically for the default wallet.

Test plan

  • apps/web unit tests: 108 passing (11 new/updated across lib/wallet.test.ts, useWalletConnect.test.ts, and a new WalletConnect.test.tsx).
  • eslint and tsc && vite build both clean.
  • Manually verified in a live dev server: opened the picker, saw both "Freighter" and "LOBSTR" listed, clicked each and got the correct wallet-specific "Install {name}" link (freighter.app / lobstr.co), and confirmed the selection is not persisted to localStorage after a failed connect attempt (no extension present in the sandbox).
  • Couldn't run the Playwright e2e suite directly in this environment (its webServer config invokes a bare pnpm binary not on PATH here, unrelated to this change), so verified by inspection that every getByRole("button", { name: "Connect Wallet" }) in the e2e specs is scoped to .locator("main"), which targets VaultPanel's own button — untouched in shape/behavior by this PR.

Caveats

  • xBull isn't in the registry yet since feat(wallet): add XBullWallet adapter #598 hasn't merged; adding it is a one-entry change to WALLETS.
  • LOBSTR's installUrl points at https://lobstr.co (its general site), matching how freighter.app was already used here — there's no official Chrome Web Store deep link in the LOBSTR package metadata to use instead.

@drips-wave

drips-wave Bot commented Aug 28, 2026

Copy link
Copy Markdown

@Mamavee001 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

@Mamavee001 is attempting to deploy a commit to the Collins' projects Team on Vercel.

A member of the Team first needs to authorize it.

@collinsezedike collinsezedike left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR breaks its own CI:

  • Commit Messages: the header is 73 characters, over the 72-char limit (✖ header must not be longer than 72 characters, current length is 73).
  • Lint & Typecheck: prettier --check . fails on apps/docs/architecture/frontend.md, apps/web/src/__tests__/components/WalletConnect.test.tsx, and apps/web/src/hooks/useWalletConnect.ts.

The design itself reads correctly: getSelectedWalletId/setSelectedWalletId staying in wallet.ts to avoid the circular import with store/wallet.ts is the right call, the selection is only persisted on a successful connect (verified this holds in useWalletConnect.ts), and the wallet singleton's dispatch-per-call means useSignAndSubmit and the store's revalidate() correctly follow whichever wallet is actually connected without needing changes themselves.

@collinsezedike

Copy link
Copy Markdown
Collaborator

@Mamavee001 checking in, this has been sitting on REQUEST_CHANGES for over two days with no update. Let me know if you're still working through the feedback or need any help.

@collinsezedike collinsezedike left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merging now.

@collinsezedike
collinsezedike merged commit 5af6ac0 into drydocs:main Aug 31, 2026
8 of 9 checks passed
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.

[Feature] Add wallet picker UI for the implemented wallet adapters

2 participants