diff --git a/docs/ZAP_GATED_PREMIUM_FEATURE.md b/docs/ZAP_GATED_PREMIUM_FEATURE.md index fb735b8..61774c3 100644 --- a/docs/ZAP_GATED_PREMIUM_FEATURE.md +++ b/docs/ZAP_GATED_PREMIUM_FEATURE.md @@ -43,7 +43,7 @@ There is no creator-only fallback path. The phases build on each other. Each has an explicit ship gate so work can be paused between phases for review. -### Phase 0 — Crypto bundle *(~2–3h)* +### Phase 0 — Crypto bundle *(~3–5h)* The existing codebase signs/encrypts only via `window.nostr.*` (the user's NIP-07 extension). NWC requires signing kind-23194 events with @@ -69,12 +69,22 @@ keys → produces the test-vector ciphertext. **Protocol summary (NIP-47)** The wallet exposes a connection URI: ``` -nostr+walletconnect://?relay=&secret= +nostr+walletconnect://?relay=&secret=[&lud16=] ``` -- `wallet-service-pubkey` — the wallet's NIP-47 service pubkey. -- `relay` — a dedicated relay both ends listen on. +- `wallet-service-pubkey` — the wallet's NIP-47 service pubkey (hex). +- `relay` — a dedicated relay both ends listen on. May appear MORE than + once; we publish/subscribe on all of them for redundancy. - `secret` — a 32-byte hex privkey that the client uses to sign - requests (this is NOT the user's identity key). + requests (this is NOT the user's identity key). May be URL-encoded. +- `lud16` — optional Lightning address some wallets (incl. CoinOS) + include for convenience; we cache it but don't depend on it. + +**Capability discovery (kind-13194)** +The wallet publishes a kind-13194 "info" event listing supported +methods (e.g. `pay_invoice get_balance get_info lookup_invoice`). +On connect we subscribe to that event and verify `pay_invoice` is in +the list — otherwise we surface "This wallet doesn't support +payments via NWC" before the user is allowed to proceed. **Request lifecycle** 1. Build a kind-23194 event: @@ -89,11 +99,18 @@ nostr+walletconnect://?relay=&secret= or `{ "error": { "code": "...", "message": "..." } }`. **Deliverables** -- `parseNwcUri(uri)` → `{ servicePubkey, relay, secret }` or null. -- `nwcConnect(uri)` — store creds, open WS, send a `get_info` ping to - verify the wallet responds. -- `nwcPayInvoice(bolt11, { timeoutMs, hintMsats })` → resolves to - `{ preimage }` or rejects with `{ code, message }`. +- `parseNwcUri(uri)` → `{ servicePubkey, relays[], secret, lud16? }` or + null. Handles multiple `relay` params and URL-encoded secrets. +- `nwcConnect(uri)` — store creds, open WS to each relay, fetch the + kind-13194 capability list, send a `get_info` request to retrieve + the wallet's alias + pubkey. +- `nwcPayInvoice(bolt11, { timeoutMs })` → resolves to + `{ preimage }` or rejects with `{ code, message }`. Common codes + worth special-casing: `INSUFFICIENT_BALANCE`, + `QUOTA_EXCEEDED` / `BUDGET_EXCEEDED` (wallet's per-connection cap + hit), `PAYMENT_FAILED`, `NOT_FOUND` (invoice mistyped), `INTERNAL`. +- `nwcLookupInvoice(paymentHash)` → returns settlement state. Used + during retry of a half-failed unlock so we never double-pay. - Credentials live in `localStorage` under `casewrap-nwc-connection` (single-record; new paste replaces old). Trade-off acknowledged in the threat-model section. @@ -116,24 +133,37 @@ A new "Wallet" section in the Community settings, two-state: ``` The CoinOS button opens a short modal: -> **1.** Visit [coinos.io](https://coinos.io) and sign up. -> **2.** Open *Settings → Wallet Connect*. -> **3.** Copy the connection string (`nostr+walletconnect://…`). -> **4.** Paste below. +> **1.** Sign up at [coinos.io](https://coinos.io) (no install). +> **2.** Open the [Wallet Connect](https://coinos.io/settings/connect) +> page directly, or browse to *Settings → Connect → Nostr Wallet +> Connect*. +> **3.** Set a budget that comfortably covers the unlocks you expect +> — at least a few thousand sats. *(Hint inline.)* +> **4.** Copy the connection string (`nostr+walletconnect://…`). +> **5.** Paste below. > *[input]* *[Connect]* The "Paste connection string" disclosure expands to the same input with no per-wallet instructions — works for Alby Hub, Mutiny, Phoenix, Cashu.me, Boltz, etc. +**Mobile note:** on a phone, the user copies the NWC URI from their +wallet app (Alby Go, Mutiny, Cashu.me's "Connect" share sheet, etc.) +and pastes into this same field. We don't try to detect / deep-link. + *Connected:* ``` ┌────────────────────────────────────────────┐ -│ ⚡ Connected to │ +│ ⚡ Connected to │ │ Last test: