# Cashu Wallet Enhancements — Implementation Plan

## Overview

Enhance the existing `www/cashu.html` page and zap flow, centered on **Cashu as the primary (and only) payment rail**. The core functionality is already implemented — this plan adds UI improvements to show zap capabilities in the feed and let users choose their zap rail.

### Design philosophy: Cashu-only

The project uses **Cashu for everything** — no NWC, CLINK, onchain, or Lightning channels to maintain:

| Payment type | How it works | Already implemented? |
|-------------|-------------|---------------------|
| **Nutzap (NIP-61)** | Send ecash proofs locked to recipient's p2pk via kind 9321 | ✅ Yes — `walletSendNutzap` |
| **Lightning zap (NIP-57)** | Melt Cashu ecash at a mint to pay a BOLT11 invoice | ✅ Yes — `walletPayInvoice` |
| **P2P ecash transfer** | Send cashuB token directly to another user | ✅ Yes — `walletSendToken` |
| **Lightning deposit** | Pay a BOLT11 invoice, mint issues ecash proofs | ✅ Yes — `walletCreateDeposit` |
| **Lightning withdrawal** | Melt ecash to pay an external BOLT11 invoice | ✅ Yes — `walletPayInvoice` |

---

## Phase 1: NIP-60 Compliance Fix (DONE ✅)

Already completed and deployed (v0.7.19–v0.7.24):
- Fixed kind 17375 content format from proprietary `{mints, nutzap}` object to NIP-60 tag-array `[["mint",url],["privkey",hex]]`
- Removed non-standard `pubkey` tag from 17375 public tags
- Added `parseWalletContent()` backwards-compat shim for reading both formats
- Added auto-migration for legacy 17375 events on startup
- Added "Republish Wallet (kind 17375)" button with lightweight 17375-only republish
- Added `ensureDirectNutzapP2pk()` call in `publishDirectProofs` so privkey is always included

---

## Phase 2: Zap Capability Indicators in the Feed

Show zap capability badges on each post's interaction bar so users can see at a glance whether the recipient can receive nutzaps, Lightning zaps, or both.

### Current state

The interaction bar in [`www/js/post-interactions.mjs`](www/js/post-interactions.mjs) already renders:
- ⚡ Zap button with count
- 🥜 Nutzap count display (separate element showing total nutzaps received)

### What to add

Small capability icons/badges next to the zap button showing what the recipient can receive:

```
┌──────────────────────────────────────────────────┐
│  ⚡ 5  🥜 2  🥜✅  ⚡✅     💬 3  🔄 1    2h ago │
│  └─ zap ─┘  └─ nutzap ─┘  └─ capability badges ─┘ │
└──────────────────────────────────────────────────┘
```

Or a more compact design — color the zap icon differently based on capabilities:

| Visual | Meaning |
|--------|---------|
| ⚡ (default color) | Recipient can receive Lightning zaps (has lud16) |
| ⚡ + 🥜 (nut badge) | Recipient can also receive nutzaps (has kind 10019 with shared mint) |
| ⚡ (dimmed) | Recipient cannot receive any zaps (no lud16, no 10019) |

### Implementation

- [ ] **2.1** Create a `resolveZapCapabilities(pubkey)` function that checks:
  - Does the recipient have a lud16 (Lightning address)? → can receive Lightning zaps
  - Does the recipient have a kind 10019 (nutzap mint list)? → can receive nutzaps
  - If yes to 10019, which mints? Do any overlap with the sender's wallet mints?

- [ ] **2.2** Cache the results per pubkey (the kind 10019 and lud16 don't change often). Use a simple in-memory Map with TTL.

- [ ] **2.3** Update `createInteractionItem` for the zap type to show capability indicators:
  - Add a small 🥜 badge on the zap button if the recipient can receive nutzaps
  - Add a tooltip showing the shared mints
  - Dim the zap button if the recipient can't receive any zaps

- [ ] **2.4** Fetch kind 10019 events for post authors as the feed loads. The worker already fetches kind 10019 for nutzap sending — extend this to cache the results for feed display. Use the existing `walletFetchMintList` worker function.

- [ ] **2.5** Show shared mint info on hover/tooltip: "Can nutzap via mint.minibits.cash/Bitcoin" or "No shared mints — Lightning only" or "No zap capability"

### Where the code lives

| File | What to change |
|------|---------------|
| [`www/js/post-interactions.mjs`](www/js/post-interactions.mjs) | Add capability badges to zap button rendering |
| [`www/js/zaps.mjs`](www/js/zaps.mjs) | Add `resolveZapCapabilities()` function (or extend existing `resolveZapSpecForPubkey`) |
| [`www/ndk-worker.js`](www/ndk-worker.js) | May need a lightweight "fetch 10019 for display" message (or reuse `walletFetchMintList`) |

---

## Phase 3: Zap Rail Selector in the Zap Dialog

When the user taps the zap button, show which rails are available and let them choose.

```mermaid
flowchart TD
    A[User taps zap button] --> B[Show zap dialog with amount input]
    B --> C{What rails are available?}
    C -->|Nutzap + Lightning| D[Show rail toggle: 🥜 Nutzap | ⚡ Lightning]
    C -->|Lightning only| E[Show Lightning only]
    C -->|Nutzap only| F[Show Nutzap only]
    C -->|Neither| G[Show error: recipient cannot receive zaps]
    D --> H[User selects rail + amount]
    H --> I{Rail selected}
    I -->|Nutzap| J[Send NIP-61 nutzap via walletSendNutzap]
    I -->|Lightning| K[Create LN invoice, melt Cashu via walletPayInvoice]
    E --> K
    F --> J
```

- [ ] **3.1** Update `promptZapDetails` in `zaps.mjs` to accept and display rail options:
  - If nutzap is available (recipient has 10019 with shared mint) → show "🥜 Nutzap" option
  - If Lightning is available (recipient has lud16) → show "⚡ Lightning" option
  - Let the user pick which rail to use via a toggle/selector

- [ ] **3.2** Show balance info in the dialog:
  - "Cashu balance: 261 sats" 
  - If amount > balance → show warning "Insufficient balance"

- [ ] **3.3** Default rail selection:
  - If nutzap is available and amount < 1000 sats → default to nutzap (lower fees)
  - If nutzap is available and amount ≥ 1000 sats → default to Lightning (more reliable for larger amounts)
  - If nutzap is not available → default to Lightning

- [ ] **3.4** Pass the selected rail back to `handleZapClick` in `post-interactions.mjs` so it uses the right send function.

---

## Phase 4: cashu.html UI Polish

Minor UI improvements to the existing cashu.html page:

- [ ] **4.1** Consider renaming the page title from "CASHU" to "WALLET" since it's the project's unified wallet.

- [ ] **4.2** Update the sidenav entry in `index.html` from "CASHU" to "WALLET" (keeping the same `cashu.html` URL).

- [ ] **4.3** Add a "Zap via Lightning" info note in the Withdraw panel explaining that this melts ecash to pay a Lightning invoice — same mechanism used for Lightning zaps.

---

## NIP Compliance Reference

| Rail | NIP | Kind(s) | Status |
|------|-----|---------|--------|
| Cashu Wallet | NIP-60 | 17375, 7375, 7376, 375 | ✅ Fixed and working |
| Cashu Nutzap | NIP-61 | 10019, 9321 | ✅ Working |
| Lightning Zap | NIP-57 | 9734, 9735 | ✅ Working (via Cashu melt) |

---

## Key Files

### Files to modify

| File | Changes |
|------|---------|
| [`www/js/post-interactions.mjs`](www/js/post-interactions.mjs) | Add zap capability badges to interaction bar, pass selected rail to zap handler |
| [`www/js/zaps.mjs`](www/js/zaps.mjs) | Add `resolveZapCapabilities()`, update `promptZapDetails` with rail selector |
| [`www/cashu.html`](www/cashu.html) | Minor UI polish (title, info notes) |
| [`www/index.html`](www/index.html) | Sidenav label: "CASHU" → "WALLET" |

### Files reused as-is

| File | Why no changes needed |
|------|----------------------|
| [`www/js/cashu-wallet.mjs`](www/js/cashu-wallet.mjs) | Controller already wraps all worker functions |
| [`www/ndk-worker.js`](www/ndk-worker.js) | All wallet functions already exist (payInvoice, sendNutzap, fetchMintList) |
| [`www/js/init-ndk.mjs`](www/js/init-ndk.mjs) | All wallet exports already exist |

---

## Implementation Order

1. **Phase 1** (DONE) — NIP-60 compliance fix
2. **Phase 2** (zap capability indicators) — show what the recipient can receive in the feed
3. **Phase 3** (rail selector) — let users choose nutzap vs Lightning melt in the zap dialog
4. **Phase 4** (UI polish) — minor label/info changes

No new files needed. No new infrastructure. Just UI improvements on top of the existing working zap flow.
