Skip to content

wallet-sdk PR5a: wallet handles + proof decryption#1123

Closed
orveth wants to merge 1 commit into
sdk/pr4-accounts-scanfrom
sdk/pr5a-wallet-handles
Closed

wallet-sdk PR5a: wallet handles + proof decryption#1123
orveth wants to merge 1 commit into
sdk/pr4-accounts-scanfrom
sdk/pr5a-wallet-handles

Conversation

@orveth

@orveth orveth commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Sub-slice 3a of the heavy Slice 3 (cashu + spark), stacked on PR4 (#1122). It resolves PR4's deferred AccountHandleResolver seam with the REAL resolver — the live account wallet handles + cashu proof decryption that PR4 deferred. The accounts repository/domain is unchanged; Sdk.create simply swaps DeferredAccountHandleResolverLiveAccountHandleResolver.

Base this on sdk/pr4-accounts-scan. Review after PR4.

What's now LIVE

Cashu (fully live — the main win of this PR)

  • getInitializedCashuWallet (internal/cashu-wallet.ts) builds an ExtendedCashuWallet (cashu-ts Wallet subclass + seed), fetches mint info / keysets / keys, and loads keys from cache. Master's QueryClient 1 h staleTime memo → a framework-free per-mint-URL MintMetadataCache (1 h TTL, rejected fetches not cached); master's 10 s timeout → a Promise.race rejecting with a cashu-ts NetworkError. Offline / timeout → keyless wallet + isOnline: false, exactly as master.
  • Proof decryption (internal/encryption.ts): re-housed ECIES batch-decrypt + master's type-preserving (de)serialisation (Date / undefined / non-finite / Money), framework-free. The data-encryption key is derived from the OpenSecret enclave at m/10111099'/0'. Master's account-repository.decryptCashuProofs is ported into the resolver: batch-decrypt the interleaved amount/secret ciphertext, re-parse amount→number / secret→string, and re-validate dleq/witness via cashu-ts ProofSchema. Cashu accounts now get real proofs, isOnline, and a real getBalance.

Spark (live, but lazy-loaded)

  • getInitializedSparkWallet (internal/spark-wallet.ts): connects a Breez BreezSdk, reads balance from getInfo().balanceSats, memoised per (mnemonic, network); on any failure → a throwing stub + isOnline: false + null balance (master's behaviour). The single initLogging guard is preserved.
  • @agicash/breez-sdk-spark needed installing — it was a dep of apps/web-wallet but absent from the lockfile / node_modules. Added it (pinned to master's 0.13.5-1). It is a native/WASM package (loads WASM + auto-enables Node storage at module-eval), so its runtime is loaded via a dynamic import() inside the init function; only the types are imported statically. Result: the WASM is never loaded in unit tests (which inject a mock runtime) or by a cashu-only Sdk.create. When no breezApiKey is configured, spark resolves to a labelled stub + offline — the SDK runs cashu-only without Breez.

Placeholders resolved

types/dependencies.ts: ExtendedCashuWallet, BreezSdk, CashuProtocolProof, ProofDleq, ProofWitness now point at their real sources (app/lib/cashu/utils, @agicash/breez-sdk-spark, @cashu/cashu-ts Proof). SparkNetwork was already the real literal union.

Still deferred (later sub-slices / slices)

  • The cashu/spark send/receive services + the executeQuote orchestrator + mint-WS subscription managers (3b–3d / the rest of Slice 3).
  • The proof encrypt path (write side) — only decrypt is built here.
  • Sdk.destroy() now drops the live-handle memos; closing the actual Breez disconnect() / mint-WS subs lands when those connections open (Slice 5).

Wiring

New SDK-internal re-export seams (single-source, matching PR4's lib-* pattern): lib-cashu-wallet.ts, lib-ecies.ts, lib-account-cryptography.ts. OpenSecretClient gains getEncryptionPrivateKeyHex / getCashuWalletSeed / getSparkWalletMnemonic (derivation paths verbatim from master). Sdk.create constructs the MintMetadataCache + SparkWalletCache (held on the connection bundle, dropped in destroy()) and the LiveAccountHandleResolver.

Tests & mocking

+22 tests, mocking every networked/native bit: the mint fetch (injected Mint factory), the Breez runtime (injected mock — never imports WASM), and ECIES (mock.module of the decrypt primitive). Coverage: MintMetadataCache memo/TTL/reject-not-cached, online/offline cashu init, spark connect/stub/memo/retry, the live resolver (decrypted proofs incl. dleq parse, online/offline, no-key spark stub), and getBalance over the real decrypted proofs (sum = 42 sats).

CI gate (green locally)

biome format --write + biome format (check) + biome lint + tsc --noEmit for the package, and root bun --filter='*' run testwallet-sdk 160 pass / web-wallet 133 pass, 0 fail (both packages). bun install --frozen-lockfile verified (fresh CI install resolves the new deps).

🤖 Generated with Claude Code

Resolve PR4's deferred AccountHandleResolver with the REAL Slice-3 resolver
(sub-slice 3a of cashu+spark): live account wallet handles + cashu proof
decryption. The account repository/domain is unchanged — Sdk.create swaps the
DeferredAccountHandleResolver for the new LiveAccountHandleResolver.

Cashu (fully live):
- getInitializedCashuWallet (internal/cashu-wallet.ts): builds an
  ExtendedCashuWallet, fetches mint info/keysets/keys via a framework-free
  per-mint 1h-TTL memo (MintMetadataCache, replacing master's QueryClient
  staleTime) + master's 10s NetworkError timeout, loads keys from cache; offline
  → keyless wallet + isOnline:false.
- Proof decryption (internal/encryption.ts): re-housed ECIES decrypt + master's
  type-preserving (de)serialisation; key derived from OpenSecret at m/10111099'/0';
  decryptCashuProofs ported into the resolver (batch decrypt amount/secret,
  re-parse dleq/witness via cashu-ts ProofSchema).

Spark (live, lazy):
- @agicash/breez-sdk-spark added (was absent from the lockfile). It is a
  native/WASM package, so its runtime is loaded via dynamic import() inside
  getInitializedSparkWallet (internal/spark-wallet.ts); only the TYPES are
  imported statically. Connect+getInfo balance, per-(mnemonic,network) memo,
  offline→throwing stub. No breezApiKey → labelled stub + offline (cashu-only).

Placeholders resolved (types/dependencies.ts): ExtendedCashuWallet, BreezSdk,
CashuProtocolProof, ProofDleq/ProofWitness now point at their real sources.

OpenSecret (internal/open-secret.ts): added getEncryptionPrivateKeyHex /
getCashuWalletSeed / getSparkWalletMnemonic (derivation paths match master).

Tests (mock the networked/native bits — mint fetch, Breez runtime, ECIES):
+22 tests covering MintMetadataCache memo/TTL, online/offline cashu init,
spark connect/stub/memo, the live resolver, and getBalance over real decrypted
proofs. Breez WASM is never loaded in unit tests or by a cashu-only Sdk.create.

Full CI gate green locally: biome format + lint, tsc --noEmit, and both
packages' tests (wallet-sdk 160 / web-wallet 133, 0 fail).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agicash Error Error Jun 3, 2026 10:32pm
agicash-root-dir-test Ready Ready Preview, Comment Jun 3, 2026 10:32pm

Request Review

@supabase

supabase Bot commented Jun 3, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project hrebgkfhjpkbxpztqqke because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@orveth

orveth commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by the reactive design-B stack (SDK #1129-#1138 + lib-extract #1140-#1143). This no-cache stack was the reference substrate the reactive PRs were built from (~90% reused, reads adapted to Query); it was never reviewed/merged, so nothing is lost. Closing as superseded — branch retained for reference.

@orveth orveth closed this Jun 5, 2026
@pmilic021 pmilic021 deleted the sdk/pr5a-wallet-handles branch June 22, 2026 09:43
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