Files
minibits_wallet/__tests__
minibits-cashandClaude Opus 5 a5ac3d7487 Stop deleting the melt recovery record when a melt may have succeeded
WalletStore.payLightningMelt deleted the melt recovery record for any error
whose message did not mention a timeout or a network failure. That heuristic
is wrong for the case cashu-ts 4.10 now names explicitly.

MeltChangeError is raised by completeMelt only AFTER the mint executed the
payment: the inputs are spent, the payment stands, and solely the NUT-08
change could not be reconstructed. Its message mentions neither timeout nor
network, so the old test fell through to the delete branch — one step before
TransferOperationApi._handleExecuteError re-checks the quote, finds it PAID,
and calls recoverMeltQuoteChange, which reads exactly that record. Recovery
then failed with "MeltPreview not found", was swallowed as "Change recovery
failed", and the transaction was marked RECOVERED with zero change. The
payment went through and the user silently forfeited the change.

This predates 4.10 — on 4.7.1 the same path was reachable whenever
createMeltChangeProofs threw, which was MORE likely then, since 4.10 loads
the change keyset's keys before building. 4.10 only made it a named type.

The fix is not to special-case one error but to give the record a coherent
owner. WalletStore cannot know whether the mint acted on a request that
threw, so it no longer guesses: both melt catches keep the record. Removal
moves to the code that learns the quote's terminal state:

  PAID     recoverMeltQuoteChange / _unblindMeltChange remove it (already did)
  PENDING  kept — the async monitor still needs it
  UNPAID   removed, in _handleExecuteError and the async revert path

The UNPAID half also closes a pre-existing leak: an async melt resolving
UNPAID left an orphaned row forever, because the only two cleanups sat on the
PAID path. Reproduced on device — two failed melts left two orphans while a
successful one cleaned up after itself. It is included here rather than split
out because fixing only the catch would have made that leak worse, and
meltRecovery.test.ts already documents the intended contract as "removed on
terminal success/failure".

Adds meltChangeError.test.ts pinning the cashu-ts contract this now depends
on: the type is exported, distinguishable by instanceof, and carries
outputData and quote. It also pins the bug itself — asserting that the old
message heuristic would NOT have kept the record — so the reasoning cannot
quietly rot.

Verified: tsc --noEmit unchanged against baseline (89 pre-existing, none new),
47 suites / 637 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:16:06 +02:00
..
2026-06-27 23:55:40 +02:00
2026-07-08 15:44:11 +02:00