feat(wallet-sdk): spark receive quote slice (step 11) - #1179
Merged
Conversation
|
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. |
jbojcic1
reviewed
Aug 20, 2026
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>
ditto-agent
force-pushed
the
sdk/spark-receive-quote-slice
branch
from
August 20, 2026 14:31
19f7093 to
6fad6e6
Compare
jbojcic1
approved these changes
Aug 20, 2026
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 11 of the wallet-SDK extraction (spec, plan): implements
sdk.receive.sparkand flips the web spark-receive-quote host hooks off@agicash/wallet-sdk/temporary.What changed
domain/sdk/receive.ts):GetSparkReceiveLightningQuoteParams/CreateSparkReceiveQuoteParamstake the caller-supplied fullSparkAccount(step-9 precedent, r3784641517).amountstays broadMoneywith a pinned runtime BTC guard in the api (core + cashu sibling are broad;Moneyis not a discriminated union, soMoney<'BTC'>would force caller edits or casts).receive-api.ts): the throwingsparkgetter 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),receiveTypepinned'LIGHTNING', and newcreateSparkRepository/createSparkServicetest seams.spark-receive-quote-service.ts): newgetLightningQuotedelegate (mirrors the server twin);createReceiveQuotegainsoptions?: { abortSignal? }, threaded to bothrepository.createbranches. The two unchanged in-package callers (receive-cashu-token-quote-service.ts,transfer-service.ts) compile as before;lightning-address-service.tsuses the server twin and is untouched.sdk.ts,domain/sdk/index.ts):init()now front-loads the Breez WASM alongside session restore, fulfilling theSdk.initcontract whose migration note expired with this slice (the note is deleted).lib/spark/wasm.tsand the web hosts (entry.client.tsx,_protected.tsx) are untouched — all three share one memoized module promise, so web behavior does not change.spark-receive-quote-hooks.ts): only the two host hooks changed —useCreateSparkReceiveQuote→sdk.receive.spark.getLightningQuote+createQuote;useTrackSparkReceiveQuote→sdk.receive.spark.getQuote. Processor, change handlers, pending reads, and caches stay on/temporaryuntil step 18.getLightningQuotefromtemporary.ts.getLightningQuotedelegate with request-side passthrough assertions, USD rejection,init()WASM rejection + restore participation).Gates
bun run fix:allexit 0 · typecheck all packages exit 0 · SDKbun test196 pass · webbun test38 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 Breezstore_preimage_share_v2+ exactly onecreate_spark_receive_quoteRPC and zero account reads during the mutation (foreground parity — the lastaccountsfetch 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)
Open for maintainer —
init()enforcement (the gpt Important): theSdk.initcontract also says Spark calls without a completedinit()throw a typedSdkError. This slice fulfills the front-loading half only; no guard was added. Rationale: enforcement is cross-cutting (the accounts namespace constructs Spark wallets too, andspark.getQuoteis a plain DB read where a WASM guard would be wrong), so the guard design — which ops require init, where the state lives, one sharedrequireInitializedseam — deserves its own decision rather than a partial bolt-on here. If wanted, the shape gpt proposed: single-flight init state onAgicashSdk+ a typed not-initializedSdkError+ 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 passesoptions, and the api pinsLIGHTNING);deleteinstead ofundefined-assignment in the WASM test (biomenoDelete).Deviations from the plan
Two plan-text bugs surfaced on integration (both in pinned test specs, fixed): the pinned
delete globalThis.WebAssemblyviolates biomenoDelete(now an assignment), and the pinnedpaymentHashassertion missed the realinvoice.paymentHashnesting.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