Skip to content

feat(wallet): cross-platform NWC connection backup via NIP-78 + parity polish#563

Open
dmnyc wants to merge 4 commits into
barrydeen:mainfrom
dmnyc:feat/wallet-nwc-backup
Open

feat(wallet): cross-platform NWC connection backup via NIP-78 + parity polish#563
dmnyc wants to merge 4 commits into
barrydeen:mainfrom
dmnyc:feat/wallet-nwc-backup

Conversation

@dmnyc

@dmnyc dmnyc commented May 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Cross-platform NWC connection backup via NIP-78 kind 30078 — publishes the active NWC URI as a NIP-44 v2 self-encrypted event on every successful connect, and surfaces a one-tap "Restore previous wallet" affordance on the NWC setup screen when an existing backup is found on relays. Implements the contract documented in #560 (`NWC_BACKUP_PARITY.md`) so an NWC connection published from iOS restores on Android and vice versa with no re-paste.

Also bundles wallet-dashboard / settings / mode-picker parity polish that was lost during the conflict resolution between #561 (NWC redesign) and the earlier wallet-setup work on `feat/one-tap-zap` (#556), and ports a few iOS UX fixes that round out the redesign.

Contract (locked decisions from #560)

  • `kind` = 30078, `d`-tag = `nwc-wallet-backup` (flat, no namespacing — cross-platform interop)
  • NIP-44 v2 self-to-self encryption, plaintext = raw NWC URI
  • Publish best-effort on every successful connect; no NIP-09 on disconnect
  • Restore search runs when the NWC setup screen opens; suppressed if the backup matches the active connection

Dependencies

Stacks on top of #561 (NWC connect screen redesign). Assumes #562 (BalanceUnit tri-state cycle) merges first; the `WalletBalanceDisplayMode.kt` source file is included here so this PR builds standalone but is identical to #562's copy and will merge cleanly.

#560 is the docs companion for this feature and can close in favor of this PR.

Test plan

  • Open NWC setup → "Searching for backup…" appears, then either disappears (no backup) or transitions into the orange "Backup found" card with date
  • Tap "Restore connection" → connects via the restored URI without re-paste
  • Connect from iOS device A → open Android NWC setup → "Backup found" surfaces within ~10s, restores on one tap
  • Reverse: connect on Android → iOS sees the offer
  • Wallet settings: relay URL + lightning address rows align in the same column
  • Wallet settings → sats / ₿ / ⚡ toggle changes the dashboard balance render
  • Dashboard balance tap-cycle (sats → fiat → hidden) keeps the lightning-address pill + Send/Receive row at the same Y
  • Mode picker: Spark row glows orange with "Recommended." subtitle
  • Spark setup: "Use my default wallet" is the orange primary; other options collapse under "More options"

🤖 Generated with Claude Code

dmnyc added 2 commits May 22, 2026 19:41
Publish the active NWC URI as a NIP-44 v2 self-encrypted kind-30078
event on every successful connect, and surface a one-tap "Restore
previous wallet" affordance on the NWC setup screen when an existing
backup is found on relays.

Implements the cross-platform contract documented in
NWC_BACKUP_PARITY.md (PR barrydeen#560) so an NWC connection published from iOS
restores on Android — and vice versa — with no re-paste.

Contract honored:
- kind 30078, d-tag "nwc-wallet-backup" (flat, no namespacing)
- NIP-44 v2 self-to-self encryption, plaintext = raw NWC URI
- Publish best-effort on every connect; no NIP-09 on disconnect
- Restore search runs when the NWC setup screen opens; suppressed
  if the backup matches the active connection
Layers iOS-parity polish on top of the bare cross-platform NWC
backup feature so the connect screen, restore affordance, and the
broader wallet dashboard all match the iOS 1:1 visual contract.

NWC setup screen polish
- Replace the single-line "Restore previous wallet" pill with the
  iOS-style "Backup found" card: cloud icon + title row, body copy
  showing the backup date ("A connection backed up on <date> is
  available. Restore it to reconnect without pasting a string."),
  and a full-width orange "Restore connection" button.
- Add a "Searching for backup…" indicator that renders while the
  relay query is in flight, between the subtitle and paste/scan
  card. Disappears as soon as the state moves to Found / NotFound.
- Add a cloud-shield footer line under the paste/scan card:
  "Connecting saves an encrypted backup to your Nostr relays so you
  can restore this connection on other devices."
- Surface the backup's `created_at` via `NwcRestoreState.Found` so
  the card can format the date with `DateFormat.MEDIUM`.
- Drop the verbose per-relay status-line stream on the NWC setup
  screen — the Connect button's spinner + "Connecting…" label
  covers the happy path and any real failure surfaces via
  `WalletState.Error`. Looked like noise after a one-tap restore.

Wallet-mode picker
- Spark row renders as a full-bleed orange card with white text +
  icon and a layered shadow glow (24dp outer halo + 10dp tighter
  inner, both tinted with the brand accent). Subtitle reads
  "Self-custody, embedded. Recommended." NWC row keeps its dark
  surface-variant peer treatment.

Spark setup screen
- "Use my default wallet" gets the same orange + layered-glow
  treatment so the nsec-derived recovery path reads as the obvious
  first choice.
- Create / Restore-from-seed / Restore-from-relays collapse under a
  "More options" disclosure with a chevron that rotates 0→180°
  via `animateFloatAsState`. `AnimatedVisibility` wraps the inner
  Column for the expand/collapse transition. Defaults to open when
  the default-wallet primary isn't visible.

Wallet Settings dropdown
- `WalletInfoRow` uses a fixed-width label column (`widthIn(min =
  110.dp)` + trailing padding) and right-aligns values with
  `maxLines = 1 + TextOverflow.Ellipsis + TextAlign.End`. Long relay
  URLs / lightning addresses truncate cleanly; labels align across
  rows like the iOS settings panel.
- Backup-to-relay button is offered for default Spark wallets too —
  matches iOS, gives users belt-and-braces durability beyond the
  nsec.
- Recoverable wallets (default Spark + NWC) get an iOS-red soft
  card row with SwapHoriz + "Switch to a different wallet", a
  section header ("Disconnect Wallet"), and an explanatory caption.
  Non-default Spark keeps the filled-red Delete CTA because its
  seed can't be re-derived.
- Delete confirmation page unifies on `#FF3B30` across all three
  flows (switch / disconnect / delete) instead of mixing Material
  primary orange and `#D32F2F`.

Dashboard
- Lightning-address pill renders for any wallet mode that carries
  a `lud16` (Spark via Breez, NWC via parsed URI). Removes the
  redundant "Nostr Wallet Connect" footer below the balance — the
  in-page top row already brands the connection.
- Recent-transactions list scales with `LocalConfiguration.
  screenHeightDp` (5 / 4 / 3 / 2 rows by tier) so smaller phones
  don't crowd out the balance + Send/Receive controls.
- `BalanceUnit` toggle (sats / ₿ / ⚡) is wired through the
  tri-state cycle's SATS branch so the Wallet Settings unit picker
  actually changes what the dashboard shows. Symbols rendered in
  `onSurfaceVariant` so the number stays the prominent reading.
- Balance container reserves a fixed 96dp min height with vertical-
  center arrangement so single-line variants (BITCOIN, LIGHTNING,
  FIAT) don't shift the lightning-address pill or Send / Receive
  row off their Y as the user cycles modes. Hidden state drops
  "tap to reveal" — the asterisks make the action obvious.

Dependencies
- Stacks on top of barrydeen#561 (NWC connect redesign) and assumes barrydeen#562
  (BalanceUnit tri-state cycle) merges first. The `WalletBalance
  DisplayMode.kt` source file is included here so this PR builds
  standalone; the file is identical to barrydeen#562's and will merge cleanly.
The default Spark wallet derives from the user's nsec, so the nsec
already serves as the cross-device backup — the "Backup to Nostr
Relays" affordance is redundant and noisy alongside the equivalent
nsec-based recovery path. Recovery Phrase view is unchanged.

Hides the relay backup button + status section for default wallets;
non-default wallets keep both, since they have no nsec-derived
fallback.
@dmnyc dmnyc force-pushed the feat/wallet-nwc-backup branch from 0ec005f to 5de27af Compare May 26, 2026 11:51
# Conflicts:
#	app/src/main/kotlin/com/wisp/app/ui/screen/WalletScreen.kt
@dmnyc dmnyc force-pushed the feat/wallet-nwc-backup branch from 5de27af to 12a5b75 Compare May 26, 2026 11:57
@dmnyc dmnyc closed this May 26, 2026
@dmnyc dmnyc deleted the feat/wallet-nwc-backup branch May 26, 2026 13:04
@dmnyc dmnyc restored the feat/wallet-nwc-backup branch May 26, 2026 13:10
@dmnyc dmnyc reopened this May 26, 2026
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