Skip to content

fix(core): record melt quotes before settlement#275

Merged
Egge21M merged 8 commits into
masterfrom
issue-269-melt-observation-before-settlement
Jun 29, 2026
Merged

fix(core): record melt quotes before settlement#275
Egge21M merged 8 commits into
masterfrom
issue-269-melt-observation-before-settlement

Conversation

@Egge21M

@Egge21M Egge21M commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Parent PRD

Slice

Summary

  • Refreshes and persists the canonical melt quote before pending melt operation decisions.
  • Passes the persisted canonical quote into pending and finalize handler contexts.
  • Keeps the existing melt operation saga responsible for finalize, stay-pending, and rollback decisions.
  • Uses persisted settlement fields for finalization when complete, with remote-fetch fallback for incomplete canonical rows.
  • Ensures the pending rollback safety check in this path uses the persisted canonical quote before mutating operation/proof state.
  • Adds focused ordering, persisted-settlement, and fallback tests plus a patch changeset.

Validation

  • bun run --filter='@cashu/coco-core' test -- test/unit/MeltOperationService.test.ts test/unit/MeltBolt11Handler.test.ts test/unit/MeltBolt12Handler.test.ts
  • bun run --filter='@cashu/coco-core' typecheck
  • bun run --filter='@cashu/coco-core' build
  • bun run --filter='@cashu/coco-core' test:unit

Notes

Full @cashu/coco-core test was attempted by the implementation worker; unit tests passed, but live integration checks need the repo's mint/auth environment. This PR intentionally does not add the melt quote watcher, settlement processor, or manager lifecycle wiring.

@changeset-bot

changeset-bot Bot commented Jun 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7445e8a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
@cashu/coco-core Patch
@cashu/coco-adapter-tests Patch
@cashu/coco-expo-sqlite Patch
@cashu/coco-indexeddb Patch
@cashu/coco-react Patch
@cashu/coco-sql-storage Patch
@cashu/coco-sqlite-bun Patch
@cashu/coco-sqlite Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.99%. Comparing base (7cf807a) to head (7445e8a).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #275      +/-   ##
==========================================
+ Coverage   86.90%   86.99%   +0.08%     
==========================================
  Files         130      130              
  Lines       17314    17427     +113     
==========================================
+ Hits        15047    15160     +113     
  Misses       2267     2267              
Flag Coverage Δ
core-integration 51.39% <ø> (ø)
core-unit 91.33% <100.00%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bdde50a8d4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +593 to +594
const res =
this.getPersistedSettlementResponse(ctx.canonicalQuote) ?? (await this.checkMeltQuote(ctx));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist fallback settlement before finalizing

When the cached quote is already PAID but lacks change, this fallback fetches the full settlement response and uses it only for local finalization. In the new checkPendingOperation flow, the preceding quote refresh has already stored a terminal PAID quote; QuoteLifecycle then ignores later observations for existing PAID quotes, so mints that report PAID before returning change/outpoint/preimage leave the canonical quote permanently missing those settlement fields even though this remote response had them. Record this fallback response (or avoid terminal persistence until settlement fields are present) before completing finalization.

Useful? React with 👍 / 👎.

@Egge21M Egge21M force-pushed the issue-269-melt-observation-before-settlement branch from bdde50a to 34c16d3 Compare June 29, 2026 05:07
@Egge21M

Egge21M commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 26a9ddf0ac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return 'finalize';
} else if (decision === 'rollback') {
await this.rollback(op.id, 'Rollback requested by handler');
await this.rollback(op.id, 'Rollback requested by handler', { canonicalQuote: quote });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Re-check quote under the rollback lock

When checkPendingOperation() decides to roll back, this passes the pre-lock quote snapshot into rollback(), so rollback's own safety check reuses that stale canonicalQuote instead of querying the mint after acquiring the operation lock. If a pending melt is observed as UNPAID here but the remote quote advances to PAID before rollback wins the local lock, this path can still release inflight proofs and mark the operation rolled back; previously the rollback safety check re-queried the quote at that point. Let rollback() refresh/verify the quote under its lock rather than trusting the earlier decision snapshot.

Useful? React with 👍 / 👎.

@Egge21M

Egge21M commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ec4aa294c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return null;
}

const change = quote.change ?? [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fetch full settlement before assuming zero change

When a cached PAID quote does not have its settlement change serialized (for example, legacy rows or an earlier paid observation with change omitted), this new cached-settlement path converts the missing field to []. Finalization then marks the melt inputs spent, skips unblindAndSaveChangeProofs, and computes the whole excess as fees instead of fetching the full quote as the old path did, so any actual mint change for that pending melt is lost locally.

Useful? React with 👍 / 👎.

@Egge21M

Egge21M commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6f1216dc6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/core/quotes/QuoteLifecycle.ts Outdated
Comment on lines +731 to +733
const persisted = await this.persistCanonicalMeltQuote({
...existing,
change: canonicalQuote.change,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve settlement metadata when enriching paid quotes

When an existing terminal PAID quote was stored before settlement metadata was available and a later refresh returns PAID with change, this enrichment persists only the new change signatures while keeping the old payment_preimage/outpoint. Because BaseQuoteMeltHandler now treats any PAID quote with an array change as complete and skips the remote settlement fetch, BOLT/onchain pending operations can finalize without the preimage or outpoint even though the refreshed quote contained it; copy the method-specific settlement field when it is missing locally.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7445e8afea

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +114 to +117
if (canonicalQuote.state === 'PAID' && !Array.isArray(canonicalQuote.change)) {
return this.quoteLifecycle.refreshMeltQuote(op.mintUrl, op.method, op.quoteId);
}
return canonicalQuote;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh paid quotes that lack settlement metadata

When the cached PAID quote already has a serialized change array but still lacks the method-specific settlement field (payment_preimage for BOLT or outpoint for onchain), this branch treats it as complete and returns it without a remote refresh. The new cached-PAID fast path means mergePaidMeltQuoteSettlement never gets a chance to copy that metadata later, so the pending operation can be finalized permanently without the preimage/outpoint even though a full quote check would now provide it.

Useful? React with 👍 / 👎.

@Egge21M Egge21M merged commit 6d78daf into master Jun 29, 2026
12 checks passed
@Egge21M Egge21M deleted the issue-269-melt-observation-before-settlement branch June 29, 2026 14:18
@github-project-automation github-project-automation Bot moved this from Backlog to Done in coco Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Record melt Quote Observations before pending operation settlement

1 participant