Files
minibits_wallet/__tests__
minibits-cash 6b5b1da356 Stage 6a: onchain melt service layer (NUT-30)
One melt lifecycle, two rails. TransferOperationApi now handles both bolt11
(NUT-05) and onchain (NUT-30) melts; what differs between them is resolved once
in resolveTransferMethod rather than branched on at each site that needs a fee or
an expiry. There is deliberately one copy of the proof reservation, the
preemptive swap, and the execute-error recovery matrix.

cashu-ts' prepareMelt is already method-agnostic (it derives the NUT-08 blank
count from inputs - quote.amount, not from fee_reserve), and fee_index rides
along as extraPayload on completeMelt. So the prepare -> persist meltPreview ->
complete split that melt-change recovery depends on survives intact.

Substance, beyond the plumbing:

- Onchain change can arrive at PENDING. The mint knows its miner fee the moment
  it builds the transaction, so it may return the unclaimed reserve with the
  spec-mandated PENDING response. bolt11's PENDING branch drops change on the
  floor (correctly - there is none yet); doing that here would strand signed
  proofs that nothing would ever look for again. execute() now commits change if
  present, and _finalizePaid subtracts what was already returned so banked change
  is not reported as fee.

- Settlement is quote-driven, not proof-driven. The mint spending our inputs
  means it BROADCAST, not that the transaction confirmed. sync's _dispatchFinalize
  therefore routes TRANSFER_ONCHAIN to refresh() (which asks the mint and only
  completes on PAID) rather than finalize(), and sync now reports the status the
  dispatch actually reached instead of assuming COMPLETED - otherwise it would
  announce a Bitcoin payment as landed while it sat unconfirmed in the mempool.

- Onchain transfers are never expired. The melt quote's expiry bounds executing
  the quote, not confirming the payment, which can outlive it by many blocks.

- Mainnet only. The CDK fakewallet hands out regtest deposit addresses for topup
  quotes, so testers end up with one in their clipboard; pasting it back into Pay
  must not spend. Refused in the parser and again in prepare(), so a screen that
  forgets the check cannot move money.

No websocket and no poller for onchain: settlement is bounded by block times, so
the existing ~60s pending sweep is already finer-grained than what it waits for.
Melts go through SyncQueue for the same counter-serialisation reason mints do.

87 tsc errors (unchanged baseline), 310/310 tests, i18n clean.
2026-07-14 16:30:15 +02:00
..
2026-06-27 23:55:40 +02:00
2026-07-08 15:44:11 +02:00