Repository navigation
[experiment] feat(wallet-sdk): cashu receive swap slice (step 10, solo arm) - #1177
Closed
ditto-agent wants to merge 4 commits into
Closed
ditto-agent wants to merge 4 commits into
ditto-agent wants to merge 4 commits into
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds createSwap to the sdk.receive.cashu namespace: the host-facing same-mint token claim, session-fenced per the step-9 template. - CreateCashuReceiveSwapParams take the caller-held CashuAccount (contract convention set by the step-9 review, #1176); the result is the service's { swap, account } shape — the create RPC advances the account keyset counter, so the updated account rides back on the same call. - reversedTransactionId stays in-package: only the send-swap reversal (step 14) and the claim orchestration (step 12) pass it. - CashuReceiveSwapService.create gains optional { abortSignal }, threaded to the repository; in-package callers unchanged. - receive-api tests: no-session and session-end fences (pre/mid/post), contract passthrough with the result returned verbatim. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hu (step 10)
useCreateCashuReceiveSwap resolves the account from the accounts cache
and calls sdk.receive.cashu.createSwap; the hook's { token, accountId }
interface is unchanged, so receive-cashu-token.tsx is untouched. The
background processor, change handlers, and pending reads stay on
/temporary until step 18; the send-swap ctor dep (step 14), the claim
route graph (step 12), and the details-page read keep their imports.
temporary.ts sheds the dead swap-domain re-exports
(CashuSwapReceiveDbData, CashuSwapReceiveDbDataSchema,
CashuReceiveSwapSchema); the contract-proposal ReceiveApi sketch gains
the createSwap line.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The three create validation throws become DomainError: the path is public contract now, and only DomainError.message is user-displayable. The dark claim path's DomainError catch surfaces the specific reason instead of the generic branch; the web toast text is unchanged. - Contract: createSwap documents its throws (DomainError validations, UniqueConstraintError duplicate claim); the result type is renamed to CreatedCashuReceiveSwap — db/database.ts's private RPC-result family owns the Create*Result names; the result JSDoc stops naming the cashu-receive-swap.updated event, which has no emit sites until step 18. - receive-api comments reworded per the repo comment policy. - Tests: the no-session fence now runs against live default builders so its no-repository-work assertion bites; the default swap-service assembly is covered over an injected repository (input/fee/output amount computation and signal threading); makeApi injects seams only when a test supplies them; unreachable inline stubs removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step 10 of the no-cache extraction (production design): wraps the cashu receive swap domain in the
sdk.receive.cashucontract namespace and flips the web same-mint token claim off@agicash/wallet-sdk/temporary.Plan: docs/superpowers/plans/2026-08-18-wallet-sdk-cashu-receive-swap-slice.md
What changed
SDK (
packages/wallet-sdk)domain/sdk/receive.ts:ReceiveApi.cashugainscreateSwap(params): Promise<CreatedCashuReceiveSwap>withCreateCashuReceiveSwapParams;@throwsdocuments theDomainErrorvalidations and theUniqueConstraintErrorduplicate claim. Thespark/cashuTokenplaceholders (steps 11/12) stay untouched.domain/receive/receive-api.ts: swap repository/service builders (mirroring the quote ones, withcreateSwapRepository/createSwapServicetest seams) and the session-fencedcreateSwapimplementation —requireUserId()→ signal capture → service build → pre-check →create(..., { abortSignal })→ post-check.domain/receive/cashu-receive-swap-service.ts:creategains an optionaloptions?: { abortSignal? }second param, threaded to the repository (which already accepted it); the three pre-write validation throws becomeDomainError— the path is public contract now, and onlyDomainError.messageis user-displayable. In-package callers (claim-cashu-token-service.ts,cashu-send-swap-service.ts) compile unchanged; the dark claim path'sDomainErrorcatch now surfaces the specific reason.domain/receive/receive-api.test.ts: 6 newcashu.createSwaptests — no-session fence against live default builders, contract passthrough (params + result verbatim), default swap-service assembly over an injected repository (input/fee/output amounts + signal threading), session-end during service construction (service never called), session-end during the write, and signal-threading identity.makeApiinjects seams only when a test supplies them.temporary.ts: sheds three dead swap-domain re-exports (CashuSwapReceiveDbData,CashuSwapReceiveDbDataSchema,CashuReceiveSwapSchema) — zero repo-wide importers.Web (
apps/web-wallet)features/receive/cashu-receive-swap-hooks.ts:useCreateCashuReceiveSwapresolves the account from the accounts cache and callssdk.receive.cashu.createSwap({ token, account }). The hook's{ token, accountId }interface is unchanged, soreceive-cashu-token.tsxis untouched.Docs
docs/superpowers/specs/2026-07-02-wallet-sdk-contract-proposal.md: theReceiveApisketch gains thecreateSwapline, so spec and shipped contract do not drift.Design decisions
receive.cashu.createSwap— the entity is a cashu-rail receive; rails nest where flows diverge, and the send-side sketch already placescreateSwapundersend.cashu.account: CashuAccount(contract convention set by the step-9 review, feat(wallet-sdk): cashu receive quote slice (step 9) #1176) — the SDK does no per-call account fetch; the web resolves the account from its own cache (a cache read, zero added network). Verified in the smoke: no account read precedes the create RPC.{ swap, account }(CreatedCashuReceiveSwap) — thecreate_cashu_receive_swapRPC advances the account keyset counter, so the updated account rides back on the same call. The web keeps using onlyswap.transactionId(parity). NamedCreated…becausedb/database.ts's private RPC-result family owns theCreate*Resultnames.reversedTransactionIdstays in-package — only the send-swap reversal (step 14) and the claim orchestration (step 12) pass it; same move as step 9 pinningreceiveType: 'LIGHTNING'.getByTransactionId), deliberately left repo-level for the quote domain too in step 9./temporaryuntil step 18; no host/processing repo+service split before step 18 (production-design bullet).Verification
bun run fix:all— exit 0bun run typecheck— all 9 workspace packages exit 0bun run test— green: wallet-sdk 179 pass (19 in receive-api suite, 5 new), web-wallet 38 pass, libs greenErrors on the public path →DomainError; duplicate-claim + validation throws documented on the contract; public result type renamed off the db layer's privateCreate*Resultfamily; JSDoc stopped naming the not-yet-emittedcashu-receive-swap.updatedevent; two comments reworded per the repo comment policy; the no-session fence test made meaningful (live default builders); the default swap-service assembly gained real coverage; dead seam stubs removed. Deferred with rationale: (1) an abort during the DB write surfaces the repository's genericErrorinstead ofSessionEndedError— inherited from the step-9 fence idiom, same increateQuote; a uniform fix belongs to a dedicated fence/error pass, not one namespace; (2)NoSessionErroris not a root export — the contract proposal's root-error list deliberately omits it; a contract-level decision; (3) the session-fence skeleton now has a 4th copy in this file — extraction is a cross-cutting cleanup the production design should schedule, not a per-slice move.https://testnut.cashu.space, opened/receive/cashu/token#…, claimed to the same-mint "Testnut BTC" account. Network trace shows the flipped path end-to-end:rpc/create_cashu_receive_swap(200, no preceding account fetch) → mintPOST /v1/swap(200, web processor via/temporary) →rpc/complete_cashu_receive_swap(200). Transaction page shows Received ₿20, Completed (21 − 1 sat mint fee); home balance 21 → 41 sats; zero console errors.🤖 Generated with Claude Code