Skip to content

wallet-sdk PR5b: cashu send + receive ops#1124

Closed
orveth wants to merge 1 commit into
sdk/pr5a-wallet-handlesfrom
sdk/pr5b-cashu-ops
Closed

wallet-sdk PR5b: cashu send + receive ops#1124
orveth wants to merge 1 commit into
sdk/pr5a-wallet-handlesfrom
sdk/pr5b-cashu-ops

Conversation

@orveth

@orveth orveth commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Sub-slice 3b of @agicash/wallet-sdk, stacked on PR5a (#1123). Lifts the cashu protocol service primitives framework-free and wires CashuSendOps / CashuReceiveOps into Sdk.create, replacing PR2's cashu stub. The wallets are live as of PR5a (ExtendedCashuWallet handle + LiveAccountHandleResolver); this slice drives the mint over them.

Implemented

Service primitives (re-housed from app/features/{send,receive}/*, dropping the useX() React factories; near-verbatim logic; the DB reservation / CONCURRENCY_ERROR guards + wallet.restore recovery — the stale-proof protection — preserved):

  • cashu-send-quote-service (lightning send: getLightningQuote/createSendQuote/initiateSend/markSendQuoteAsPending/completeSendQuote/failSendQuote/expireSendQuote)
  • cashu-send-swap-service (token send + reverse, decision 8 — a new cashu receive-swap tagged reversedTransactionId)
  • cashu-receive-quote-service (+ cashu-receive-quote-core: NUT-20 quote-locking, framework-free in master)
  • cashu-receive-swap-service (same-mint token claim)
  • the four SDK-owned repos over the SDK Supabase client + Encryption (encrypt-on-write / decrypt-on-read of the jsonb + proof ciphertext; account-returning RPCs use an injected resolver-backed account mapper)

Public ops (domains/cashu.ts): createLightningQuote (with the LNURL ln-address→bolt11 fold internal to quote creation, per the contract — getInvoiceFromLud16), createTokenQuote, failQuote, reverse, get; receive createLightningQuote (+ BUY_CASHAPP purpose), get.

Supporting:

  • Encryption gains the encrypt half (encrypt/encryptBatch/decrypt) the write path needs (PR5a shipped decrypt-only, explicitly deferring this); + OpenSecret getEncryptionPublicKeyHex and cashu-locking xPub / private-key methods.
  • lib-cashu-quotes / lib-cashu-crypto / lib-lnurl single-source re-exports (the established pattern); the package tsconfig.json maps ~/*apps/web-wallet/app/* so the re-housed zod schemas resolve their own imports one level deeper than PR5a's leaf-only re-exports.
  • SdkError.code is now optional (defaults to the subclass name) so the lifted services construct new DomainError(msg) verbatim; UniqueConstraintError added (the double-claim DB guard).

Deferred (and why)

  • executeQuote — documented NotImplementedError stub. Per the build plan (§1 + §4), the unified executeQuote orchestrator is the single biggest net-new construct: a framework-free state machine that absorbs master's six React-resident useProcess*Tasks hooks (each a TanStack mutation + retry/MintOperationError branch + a live mint-WS *SubscriptionManager) and drives UNPAID→PENDING→PAID off DB state, plus the classify()-body relocation. The plan keeps that state-machine core as its own reviewable unit; PR5b ships the idempotent primitives it sequences (initiateSend/markSendQuoteAsPending/completeSendQuote/failSendQuote + the swap/claim services). → the orchestrator sub-slice (PR5d).
  • receiveToken — stub at the public surface. The same-mint receive-swap primitive (CashuReceiveSwapService.create, with completeSwap's double-claim wallet.restore recovery) is shipped + tested. What the public method additionally needs — destination-account resolution, same-vs-cross routing, the cross-account melt-then-mint quote path, and the result-surface reconciliation (a same-mint claim yields a swap, but the declared result is a quote) — is orchestrated over account/user services + exchange-rate fetches not yet in the SDK, and rides with PR5d + transfers. Stubbed with no side effects rather than half-driving a claim.
  • listPending is not public (decision) — kept as service-internal getUnresolved/getPending helpers for the future orchestrator.

Tests

Service idempotency / concurrency guards (no-op + state-guard branches), reverse (reversedTransactionId tagging), the LNURL fold (bolt11 passthrough vs ln-address resolution vs LNURL error → DomainError vs amount-required), repo CONCURRENCY_ERRORConcurrencyError and 23505UniqueConstraintError mapping, plus the executeQuote/receiveToken deferred-stub contract. Wallet / mint / repos / LNURL are mocked.

CI gate (local)

tsc --noEmit ✓ · biome format (check) ✓ · biome lint ✓ · bun --filter='*' run test ✓ — wallet-sdk 203 pass / 0 fail, web-wallet 133 pass / 0 fail.

🤖 Generated with Claude Code

Sub-slice 3b of @agicash/wallet-sdk, stacked on PR5a (#1123). Lifts the cashu
service primitives framework-free + wires CashuSendOps/CashuReceiveOps into
Sdk.create, replacing PR2's cashu stub.

Services (re-housed from app/features/{send,receive}/*, dropping the useX()
React factories; near-verbatim logic; DB reservation / CONCURRENCY_ERROR guards
+ wallet.restore recovery preserved):
- cashu-send-quote-service / cashu-send-swap-service (incl. reverse, decision 8)
- cashu-receive-quote-service / cashu-receive-swap-service
- cashu-receive-quote-core (NUT-20 locking, framework-free in master)
- the four SDK-owned repos over the SDK Supabase client + Encryption

Supporting:
- Encryption gains the encrypt half (encrypt/encryptBatch/decrypt) the write
  path needs (PR5a shipped decrypt-only); + OpenSecret encryption-public-key
  and cashu-locking xPub/private-key methods.
- lib-cashu-quotes / lib-cashu-crypto / lib-lnurl single-source re-exports;
  tsconfig maps ~/* so the re-housed zod schemas resolve their own imports.
- SdkError.code is now optional (defaults to the subclass name) so the lifted
  services construct DomainError(msg) verbatim; UniqueConstraintError added.

executeQuote is a documented NotImplementedError stub here — the orchestrator
state machine (the six React useProcess*Tasks hooks → framework-free, mint-WS
managers, classify-body relocation) is the heaviest net-new construct and is
deferred to the orchestrator sub-slice (PR5d) per the build plan; PR5b ships the
idempotent primitives it sequences. receiveToken is likewise a stub at the
public surface (the claim FLOW + cross-account melt-then-mint ride with PR5d +
transfers); the same-mint receive-swap primitive it builds on is shipped+tested.

Tests: service idempotency/concurrency guards, reverse (reversedTransactionId
tagging), the LNURL ln-address fold, repo CONCURRENCY_ERROR/UniqueConstraint
mapping. Full CI gate green locally (tsc, biome format+lint, bun --filter='*'
test: wallet-sdk 203 / 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 11:10pm
agicash-root-dir-test Ready Ready Preview, Comment Jun 3, 2026 11:10pm

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/pr5b-cashu-ops 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