# Cashu Wallet — Move NDKCashuWallet into SharedWorker

## Goal

Move all Cashu wallet operations into the SharedWorker so that:
1. Any page can query wallet balance, send/receive tokens, and get wallet state
2. No page creates its own private NDK instance
3. Wallet state is shared across all open tabs
4. The existing relay connections and signer are reused

## Current Architecture

```mermaid
graph TD
    subgraph Page - cashu.html
        A[cashu.html UI]
        B[cashu-wallet.mjs]
        C[Private NDK Instance]
        D[NDKNip07Signer]
        E[NDKCashuWallet]
    end
    
    subgraph SharedWorker - ndk-worker.js
        F[Worker NDK Instance]
        G[MessageBasedSigner]
        H[Dexie Cache]
    end
    
    A --> B
    B --> C
    C --> D
    C --> E
    E --> D
    
    A -.-> F
    F --> G
    F --> H
```

**Problem:** The page creates its own NDK at `cashu-wallet.mjs:56` with its own relay connections and signer. This duplicates connections, races with the worker, and can't be shared across tabs.

## Target Architecture

```mermaid
graph TD
    subgraph Any Page
        A[Page UI]
        B[init-ndk.mjs]
    end
    
    subgraph SharedWorker - ndk-worker.js
        C[Worker NDK Instance]
        D[MessageBasedSigner]
        E[Dexie Cache]
        F[NDKCashuWallet]
        G[Wallet Message Handlers]
    end
    
    A --> B
    B <-->|message protocol| G
    G --> F
    F --> C
    C --> D
    C --> E
```

**All wallet operations go through the SharedWorker message protocol.** Pages send messages like `walletGetBalance`, `walletSend`, `walletReceive`, etc. The worker manages the `NDKCashuWallet` instance.

## Why This Works

The worker already has:
- **NDK instance** with relay connections at [`ndk-worker.js:883`](www/ndk-worker.js:883)
- **MessageBasedSigner** at [`ndk-worker.js:770`](www/ndk-worker.js:770) with `sign()`, `encrypt()`, `decrypt()`, and NIP-44 support
- **NDKCashuWallet** class available via `NDKExports.NDKCashuWallet`
- **Dexie cache** for event storage
- **Message protocol** for request/response with pages

`NDKCashuWallet` needs:
- An NDK instance ✅ (worker has one)
- A signer for NIP-44 encryption ✅ (MessageBasedSigner handles this)
- Relay connections ✅ (worker manages these)
- Event publishing ✅ (worker can publish)

## New Worker Message Types

### Wallet Lifecycle

| Message Type | Direction | Params | Response |
|---|---|---|---|
| `walletInit` | page → worker | `{ pubkey }` | `{ hasWallet, status }` |
| `walletCreate` | page → worker | `{ mints, relays }` | `{ success, mints }` |
| `walletStart` | page → worker | `{ pubkey }` | `{ success, status }` |
| `walletShutdown` | page → worker | — | `{ success }` |

### Balance & State

| Message Type | Direction | Params | Response |
|---|---|---|---|
| `walletGetBalance` | page → worker | — | `{ balance, balancesByMint }` |
| `walletGetMints` | page → worker | — | `{ mints }` |
| `walletHasWallet` | page → worker | — | `{ hasWallet }` |

### Token Operations

| Message Type | Direction | Params | Response |
|---|---|---|---|
| `walletReceiveToken` | page → worker | `{ token, description }` | `{ balance, balancesByMint }` |
| `walletSendToken` | page → worker | `{ amount, memo, mint }` | `{ token, amount, mint, balance }` |

### Lightning Deposit

| Message Type | Direction | Params | Response |
|---|---|---|---|
| `walletCreateDeposit` | page → worker | `{ amount, mint }` | `{ invoice, quoteId, mint, amount }` |

Note: Deposit monitoring happens inside the worker. The worker broadcasts `walletDepositConfirmed` to all pages when payment arrives.

### Mint Management

| Message Type | Direction | Params | Response |
|---|---|---|---|
| `walletAddMint` | page → worker | `{ mintUrl }` | `{ mints }` |
| `walletRemoveMint` | page → worker | `{ mintUrl }` | `{ mints }` |

### Transaction History

| Message Type | Direction | Params | Response |
|---|---|---|---|
| `walletGetTransactions` | page → worker | — | `{ transactions }` |

### Worker → Page Broadcasts

| Message Type | Direction | Data |
|---|---|---|
| `walletBalanceUpdated` | worker → all pages | `{ balance, balancesByMint }` |
| `walletDepositConfirmed` | worker → all pages | `{ amount, mint }` |
| `walletTransactionReceived` | worker → all pages | `{ direction, amount, mint }` |
| `walletStatusChanged` | worker → all pages | `{ status }` |

## Implementation Steps

### Phase 1: Worker-Side Wallet Manager

**File: `www/ndk-worker.js`**

1. Add wallet state variables at the top of the worker:
   ```javascript
   let cashuWallet = null;
   let walletStatus = 'initial'; // initial, loading, ready, failed
   ```

2. Add `NDKCashuWallet` to the imports from `NDKExports`:
   ```javascript
   const NDKCashuWallet = NDKExports?.NDKCashuWallet;
   const NDKKind = NDKExports?.NDKKind;
   ```

3. Implement wallet handler functions:
   - `handleWalletInit(pubkey, port)` — fetch kind 17375, create NDKCashuWallet from event
   - `handleWalletCreate(mints, relays, port)` — NDKCashuWallet.create()
   - `handleWalletStart(pubkey, port)` — wallet.start()
   - `handleWalletGetBalance(requestId, port)` — return balance
   - `handleWalletGetMints(requestId, port)` — return mints
   - `handleWalletReceiveToken(requestId, token, description, port)`
   - `handleWalletSendToken(requestId, amount, memo, mint, port)`
   - `handleWalletCreateDeposit(requestId, amount, mint, port)`
   - `handleWalletAddMint(requestId, mintUrl, port)`
   - `handleWalletRemoveMint(requestId, mintUrl, port)`
   - `handleWalletGetTransactions(requestId, port)`
   - `handleWalletShutdown(port)`

4. Add message type cases to the `onconnect` switch statement

5. Set up wallet event listeners to broadcast balance updates to all pages

### Phase 2: Page-Side Wallet API in init-ndk.mjs

**File: `www/js/init-ndk.mjs`**

Add exported functions that send messages to the worker and return promises:

```javascript
export async function walletInit() { ... }
export async function walletCreate(mints, relays) { ... }
export async function walletStart() { ... }
export async function walletGetBalance() { ... }
export async function walletGetMints() { ... }
export async function walletHasWallet() { ... }
export async function walletReceiveToken(token, description) { ... }
export async function walletSendToken(amount, memo, mint) { ... }
export async function walletCreateDeposit(amount, mint) { ... }
export async function walletAddMint(mintUrl) { ... }
export async function walletRemoveMint(mintUrl) { ... }
export async function walletGetTransactions() { ... }
export async function walletShutdown() { ... }
```

Each function follows the existing pattern: create a requestId, post message to worker, return a promise that resolves when the worker responds.

Add event listeners for wallet broadcasts:
```javascript
export function onWalletBalanceUpdated(callback) { ... }
export function onWalletDepositConfirmed(callback) { ... }
export function onWalletTransactionReceived(callback) { ... }
```

### Phase 3: Rewrite cashu-wallet.mjs as Thin Wrapper

**File: `www/js/cashu-wallet.mjs`**

Replace the entire module with a thin wrapper that imports from `init-ndk.mjs`:

```javascript
import { walletInit, walletCreate, walletStart, walletGetBalance, ... } from './init-ndk.mjs';

export async function createCashuWalletController({ pubkey }) {
  const initResult = await walletInit();
  
  return {
    hasWallet: () => initResult.hasWallet,
    getBalance: walletGetBalance,
    getBalanceByMint: async () => (await walletGetBalance()).balancesByMint,
    getMints: walletGetMints,
    createWallet: walletCreate,
    startWallet: walletStart,
    addMint: walletAddMint,
    removeMint: walletRemoveMint,
    receiveToken: walletReceiveToken,
    sendToken: walletSendToken,
    createDeposit: walletCreateDeposit,
    getTransactionHistory: walletGetTransactions,
    shutdown: walletShutdown,
    // Event subscriptions
    onWalletEvent: (name, handler) => { ... },
    subscribeTransactions: (callback) => { ... }
  };
}
```

Or alternatively, **remove `cashu-wallet.mjs` entirely** and have `cashu.html` import wallet functions directly from `init-ndk.mjs`.

### Phase 4: Update cashu.html

**File: `www/cashu.html`**

- Import wallet functions from `init-ndk.mjs` instead of `cashu-wallet.mjs`
- Remove the `createCashuWalletController` call
- Use the new wallet API functions directly
- Listen for `walletBalanceUpdated` broadcasts to update the UI
- Remove the private NDK instance entirely

### Phase 5: Enable Wallet on Other Pages

Once the wallet lives in the SharedWorker, any page can:
- Show balance in the header/footer
- Accept cashu tokens via paste
- Display wallet status

This is a future step but the architecture enables it.

## Files to Modify

| File | Changes |
|---|---|
| `www/ndk-worker.js` | Add NDKCashuWallet import, wallet state, handler functions, message cases, broadcast listeners |
| `www/js/init-ndk.mjs` | Add wallet API functions, wallet broadcast event handlers |
| `www/js/cashu-wallet.mjs` | Rewrite as thin wrapper or remove entirely |
| `www/cashu.html` | Update imports, use new wallet API, remove private NDK references |

## Key Risks & Mitigations

1. **NIP-44 encryption in worker** — The `MessageBasedSigner` already handles `encrypt()` and `decrypt()` by proxying to the page's `window.nostr`. NDKCashuWallet uses NIP-44 for wallet event content. This should work transparently since the signer interface is the same.

2. **Deposit monitoring** — `NDKCashuDeposit` polls the mint for payment status. This runs fine in the worker. The worker broadcasts `walletDepositConfirmed` when payment arrives. If the user closes the tab, the worker keeps monitoring (SharedWorker stays alive as long as any tab is open).

3. **Wallet event subscriptions** — `wallet.subscribeTransactions()` creates NDK subscriptions. These run in the worker and broadcast to all pages.

4. **Error handling** — Worker errors need to be serialized and sent back to pages. Use `{ success: false, error: message }` pattern.

5. **Multiple tabs** — Only one wallet instance exists in the worker. All tabs share it. Balance updates broadcast to all tabs simultaneously.

## Migration Order

The safest order is:
1. First commit and push current working state
2. Add worker-side wallet handlers (can coexist with old code)
3. Add init-ndk.mjs wallet API functions
4. Switch cashu.html to use new API
5. Remove old cashu-wallet.mjs private NDK code
6. Test thoroughly
