Skip to content

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

Open
Mamavee001 wants to merge 1 commit into
drydocs:mainfrom
Mamavee001:feat/wallet-picker-ui
Open

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

Conversation

@Mamavee001

Copy link
Copy Markdown

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 (#488) #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 (#488) #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.

…STR)

wallet.ts hardcoded a single FreighterWallet instance, so LobstrWallet
(drydocs#537) was fully implemented and tested but unreachable from the UI, and
XBullWallet (drydocs#598) would land in the same state.

- wallet.ts: replaces the single `wallet` export's implicit Freighter-only
  assumption with a WALLETS registry (id, name, installUrl, adapter) and
  getSelectedWalletId/setSelectedWalletId, persisted via a dedicated
  localStorage key (kept out of the Zustand store to avoid a circular
  import, since store/wallet.ts already imports from wallet.ts). `wallet`
  itself becomes a dispatcher that resolves to whichever wallet is
  currently selected, so useSignAndSubmit's sign() and the store's
  revalidate() automatically follow the user's choice with no changes.
- useWalletConnect.ts: handleConnect now takes an optional walletId,
  defaulting to the persisted selection. The choice is only persisted on
  a successful connect, so a failed/cancelled attempt never silently
  switches which wallet later calls go through. Returns attemptedWalletId
  so the no-extension fallback can link to the right wallet.
- WalletConnect.tsx: adds a picker dropdown (a caret beside the existing
  button) listing every implemented wallet with an "Installed" badge; the
  plain "Connect Wallet" click is unchanged, still connecting through the
  default/selected wallet with one click. The no-extension fallback is now
  wallet-aware instead of hardcoded to freighter.app.
- VaultPanel.tsx's own connect button/fallback made wallet-aware too, for
  consistency with the header.

Tests added/updated across lib/wallet, useWalletConnect, and a new
WalletConnect component test; existing suites (including e2e-relevant
selectors) still pass.
@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.

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

1 participant