Skip to content

feat(web): let a wallet be unlinked from the dashboard - #333

Merged
blockchain-maxis merged 3 commits into
blockchain-maxis:mainfrom
ibochivincent-lang:feat/unlink-wallet
Sep 2, 2026
Merged

feat(web): let a wallet be unlinked from the dashboard#333
blockchain-maxis merged 3 commits into
blockchain-maxis:mainfrom
ibochivincent-lang:feat/unlink-wallet

Conversation

@ibochivincent-lang

Copy link
Copy Markdown
Contributor

closes #286

Summary

Wallets could be listed (getAccountWallets) but never removed — apps/web/lib/server/trpc.ts had account.me and account.update and nothing for wallets. A developer who rotated a deploy key or linked the wrong identity had no way to detach it; the rotated key kept contributing to the profile permanently.

Changes

  • apps/web/lib/server/account.ts: added unlinkWallet(address, pubkey, store?). It looks up the caller's own profile from their session wallet, then the target wallet, and:
    • refuses if the target doesn't exist, or belongs to a different profile than the caller's — both cases raise the same "Wallet not found" message, so a caller can't distinguish "doesn't exist" from "belongs to someone else";
    • refuses the primary wallet — that binding is the handle's on-chain claim itself, changeable only via release/transfer on the Identity Registry, never from the dashboard;
    • otherwise deletes the wallet row.
    • Takes an optional WalletStore (mirroring the indexer workers' injectable-store pattern) so the cross-profile case can be unit tested without a real database.
  • apps/web/lib/server/trpc.ts: added account.unlinkWallet, a protectedProcedure mutation (session + same-origin guarded, same as account.update), reusing the existing walletInput validator for the pubkey.
  • apps/web/app/(dashboard)/app/wallets/unlink-wallet-button.tsx (new): a client component with a two-step Unlink → Confirm/Cancel control (matching the dashboard's existing styling rather than a native confirm() dialog), calling the new mutation and refreshing the page on success.
  • apps/web/app/(dashboard)/app/wallets/page.tsx: renders the button next to the Explorer link for every non-primary wallet; the primary wallet gets no button, since unlinking it is refused server-side anyway.
  • Tests: account.test.ts gained 6 new cases for unlinkWallet (no database, no caller profile, missing wallet, cross-profile refusal, primary refusal, successful delete); trpc.test.ts gained cases for no-session, malformed input, no-database, and cross-origin FORBIDDEN.

Verification

  • pnpm typecheck / pnpm lint / pnpm test — all pass across the workspace (one pre-existing, unrelated lint warning in apps/indexer/src/stellar.ts).
  • apps/web/lib/server/account.test.ts — 13/13 passing, including the cross-profile refusal.
  • apps/web/lib/server/trpc.test.ts — 217/217 passing (web suite total), including the new account.unlinkWallet session/origin/input/database cases.
  • The wallets page sits behind sign-in-with-Stellar; I did not drive the full wallet-signature flow in a browser for this change, so the UI wiring is verified by the tests above rather than a manual click-through.

…oard

Wallets could be listed but never removed, so a rotated deploy key or a
wrongly-linked wallet kept contributing to a profile permanently. Add
account.unlinkWallet, gated by the same session + same-origin guard as
every other mutation, which refuses the primary handle wallet (that's a
registry operation) and refuses a wallet bound to a different profile
with the same "not found" message a nonexistent pubkey gets. The
dashboard gets a two-step Unlink/Confirm control per non-primary wallet.
@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@ibochivincent-lang 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 30, 2026

Copy link
Copy Markdown

@ibochivincent-lang is attempting to deploy a commit to the blockchainmaxis-8449's projects Team on Vercel.

A member of the Team first needs to authorize it.

@netlify

netlify Bot commented Aug 30, 2026

Copy link
Copy Markdown

Deploy Preview for stellar-signet ready!

Name Link
🔨 Latest commit b7a9f94
🔍 Latest deploy log https://app.netlify.com/projects/stellar-signet/deploys/6a97f90e3e726d0008b17215
😎 Deploy Preview https://deploy-preview-333--stellar-signet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@blockchain-maxis
blockchain-maxis merged commit 218209a into blockchain-maxis:main Sep 2, 2026
9 of 10 checks passed
blockchain-maxis added a commit to escaprt/signet that referenced this pull request Sep 2, 2026
Two things needed fixing beyond the mechanical merge.

account.test.ts conflicted on its import block (main gained blockchain-maxis#333's
unlinkWallet/WalletStore); combined both. My first pass at that also swallowed
the closing `});` of the last linkDeployWallet test where the two sides met,
which typecheck caught as 'lib/server/account.test.ts(335,1): error TS1005'.
Restored — that test (the create-time race resolving as a typed conflict) is
now actually running rather than being silently absorbed into the next block.

blockchain-maxis#329 made LinkedWallet.source a typed WalletSource after this PR was written,
so the writer's plain `string` no longer assigned:

  lib/server/account.ts(183,5): error TS2322: Type 'string' is not assignable
  to type '"cli" | "curated" | "onchain"'

Threaded WalletSource through linkDeployWallet's signature and LinkWalletStore's
create/update data, and routed the row read back through the existing
toWalletSource guard — same treatment getAccountWallets already gives the
column, since it is an untyped String in the schema.

277/277 web tests pass (7 of them linkDeployWallet's); typecheck, lint and
build clean.
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.

No way to unlink a wallet from the dashboard

2 participants