Skip to content

feat(wallet-sdk): spark receive quote slice (step 11) - #1179

Merged
jbojcic1 merged 1 commit into
masterfrom
sdk/spark-receive-quote-slice
Aug 20, 2026
Merged

feat(wallet-sdk): spark receive quote slice (step 11)#1179
jbojcic1 merged 1 commit into
masterfrom
sdk/spark-receive-quote-slice

Conversation

@ditto-agent

Copy link
Copy Markdown
Contributor

Step 11 of the wallet-SDK extraction (spec, plan): implements sdk.receive.spark and flips the web spark-receive-quote host hooks off @agicash/wallet-sdk/temporary.

What changed

  • Contract (domain/sdk/receive.ts): GetSparkReceiveLightningQuoteParams / CreateSparkReceiveQuoteParams take the caller-supplied full SparkAccount (step-9 precedent, r3784641517). amount stays broad Money with a pinned runtime BTC guard in the api (core + cashu sibling are broad; Money is not a discriminated union, so Money<'BTC'> would force caller edits or casts).
  • API (receive-api.ts): the throwing spark getter becomes the implemented sub-namespace (getLightningQuote/createQuote/getQuote), with the step-9/10 fence template (signal captured pre-await, re-checked post-await and post-call), receiveType pinned 'LIGHTNING', and new createSparkRepository/createSparkService test seams.
  • Service (spark-receive-quote-service.ts): new getLightningQuote delegate (mirrors the server twin); createReceiveQuote gains options?: { abortSignal? }, threaded to both repository.create branches. The two unchanged in-package callers (receive-cashu-token-quote-service.ts, transfer-service.ts) compile as before; lightning-address-service.ts uses the server twin and is untouched.
  • First-Spark WASM obligation (sdk.ts, domain/sdk/index.ts): init() now front-loads the Breez WASM alongside session restore, fulfilling the Sdk.init contract whose migration note expired with this slice (the note is deleted). lib/spark/wasm.ts and the web hosts (entry.client.tsx, _protected.tsx) are untouched — all three share one memoized module promise, so web behavior does not change.
  • Web flip (spark-receive-quote-hooks.ts): only the two host hooks changed — useCreateSparkReceiveQuotesdk.receive.spark.getLightningQuote + createQuote; useTrackSparkReceiveQuotesdk.receive.spark.getQuote. Processor, change handlers, pending reads, and caches stay on /temporary until step 18.
  • Canary: pruned exactly getLightningQuote from temporary.ts.
  • Tests: +15 SDK tests (fences, param passthrough, abort-signal identity through the real default service, real getLightningQuote delegate with request-side passthrough assertions, USD rejection, init() WASM rejection + restore participation).

Gates

bun run fix:all exit 0 · typecheck all packages exit 0 · SDK bun test 196 pass · web bun test 38 pass.

Smoke (local stack, fresh guest)

Logged-out boot and guest signup clean with init() now awaiting the WASM load. Receive ₿21 on the spark account: invoice QR rendered; network shows Breez store_preimage_share_v2 + exactly one create_spark_receive_quote RPC and zero account reads during the mutation (foreground parity — the last accounts fetch was at wallet boot). Invoice copy toast works. 0 console errors. Payment completion (web processor melt) needs a real Lightning payment and was not run — worth one live receive before/after merge.

Review round (2 cross-model marketplace reviews of the integrated diff)

  • claude-agent: READY, 0 Critical / 0 Important / 1 Minor / 4 Nits — all applied except two declined below.
  • gpt-5.6-sol: NOT READY, 0 Critical / 1 Important / 3 Minor / 2 Nits — Minors applied.

Open for maintainer — init() enforcement (the gpt Important): the Sdk.init contract also says Spark calls without a completed init() throw a typed SdkError. This slice fulfills the front-loading half only; no guard was added. Rationale: enforcement is cross-cutting (the accounts namespace constructs Spark wallets too, and spark.getQuote is a plain DB read where a WASM guard would be wrong), so the guard design — which ops require init, where the state lives, one shared requireInitialized seam — deserves its own decision rather than a partial bolt-on here. If wanted, the shape gpt proposed: single-flight init state on AgicashSdk + a typed not-initialized SdkError + a guard passed into the factories.

Declined findings (with reasons): a CASHU_TOKEN-branch options-threading test (that combination has no caller today — only the api passes options, and the api pins LIGHTNING); delete instead of undefined-assignment in the WASM test (biome noDelete).

Deviations from the plan

Two plan-text bugs surfaced on integration (both in pinned test specs, fixed): the pinned delete globalThis.WebAssembly violates biome noDelete (now an assignment), and the pinned paymentHash assertion missed the real invoice.paymentHash nesting.

Delegation

Built via 5 maxplayer contribution jobs, 1100 sats: plan 250 (deepseek-v4-flash) → plan-attack 150 (gpt-5.6-sol: 1 Critical [the WASM obligation] + 3 Important, all applied as doc edits pre-code) → whole-slice impl 350 (deepseek-v4-flash) → 2 cross-model reviews 200+200 (gpt-5.6-sol + claude-agent).

🤖 Generated with Claude Code

@vercel

vercel Bot commented Aug 19, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
agicash Ready Ready Preview Aug 20, 2026 2:32pm

Request Review

@supabase

supabase Bot commented Aug 19, 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 ↗︎.

Comment thread packages/wallet-sdk/supabase/.branches/_current_branch Outdated
Comment thread packages/wallet-sdk/supabase/.temp/cli-latest Outdated
Comment thread packages/wallet-sdk/domain/sdk/sdk.ts
ditto-agent added a commit that referenced this pull request Aug 20, 2026
Decided in the #1179 review: the host calls stay this slice; step 19's
/temporary deletion build-enforces the swap, and the spec now names it
so an earlier prefetch-removal slice can pick it up deliberately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wraps the spark receive quote domain in the sdk.receive.spark contract
namespace and flips web quote creation and tracking off
@agicash/wallet-sdk/temporary. First Spark slice.

- createReceiveApi implements spark (getLightningQuote/createQuote/
  getQuote) alongside cashu; cashuToken stays a throwing getter for
  step 12. Step-9/10 session-fence template (requireUserId +
  sessionSignal pre/post checks), caller-supplied SparkAccount params
  per the step-9 convention, receiveType pinned 'LIGHTNING'.
- The preview amount stays broad Money with a pinned BTC guard (Money
  is not a discriminated union, so Money<'BTC'> params would be a
  runtime trap); USD rejection is tested.
- SparkReceiveQuoteService gains a getLightningQuote delegate and
  optional { abortSignal }, threaded to both repository.create
  branches; in-package callers unchanged.
- init() now front-loads the Breez WASM per the Sdk.init contract
  (Promise.all with session restore); the expired migration note is
  gone from the contract JSDoc. The web hosts keep their direct
  ensureBreezWasm calls this slice (same memoized module promise, no
  double-load, no web behavior change); the swap to sdk.init() is
  pinned to step 19 in the production-design spec (#1179 review) and
  is build-enforced by the /temporary deletion.
- Web flip: useCreateSparkReceiveQuote and useTrackSparkReceiveQuote
  on sdk.receive.spark.*; the background processor and change handlers
  stay on /temporary until step 18.
- temporary.ts sheds the getLightningQuote re-export.
- receive-api/sdk test suites: +15 tests — spark fences (pre/mid/post),
  preview passthrough, BTC guard, abort-signal identity, init() WASM
  rejection with session-restore participation.
- init()-before-Spark-ops enforcement is documented open in the PR:
  cross-cutting (accounts constructs Spark wallets too), needs its own
  guard design.

Gates: fix:all 0, typecheck clean, SDK 196 tests, web 38 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jbojcic1
jbojcic1 merged commit ae4051e into master Aug 20, 2026
5 checks passed
@jbojcic1
jbojcic1 deleted the sdk/spark-receive-quote-slice branch August 20, 2026 14:32
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.

2 participants