Skip to content

refactor(core): centralize mint quote claimability - #398

Open
Egge21M wants to merge 1 commit into
masterfrom
issue-387-centralize-mint-quote-claimability
Open

refactor(core): centralize mint quote claimability#398
Egge21M wants to merge 1 commit into
masterfrom
issue-387-centralize-mint-quote-claimability

Conversation

@Egge21M

@Egge21M Egge21M commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Problem

Canonical Mint Quote Accounting was already uniform, but readiness, scheduling, recovery, and pending selection still rebuilt payment-method behavior from BOLT11 compatibility state, method, and reusable. Those scattered rules could disagree, especially when remote balance accounting lagged local issuance or a quote observation jumped directly to complete.

Summary

  • add one pure Mint Quote Claimability assessment with private atomic BOLT11 and balance BOLT12/on-chain policies
  • route operation orchestration, handlers, watchers, processor scheduling, Manager recovery, lifecycle checks, and repository pending selection through canonical accounting
  • account for finalized local issuance and in-flight reservations during balance claims and recovery
  • keep expiry out of claimability while preserving observation-before-operation advancement
  • add focused policy, caller, and adapter contract coverage and update the domain glossary

Impact

BOLT11 compatibility state remains available for import and projection but no longer drives runtime claimability. BOLT12 and on-chain quotes retain repeated partial claims, including when remote issuance counters lag local finalized operations.

Verification

  • bun run --filter='@cashu/coco-core' test:unit — 1,186 passed
  • bun run --filter='@cashu/coco-core' build
  • bun run --filter='@cashu/coco-core' typecheck
  • SQLite repository contracts — 57 passed
  • IndexedDB Chromium contracts — 51 passed
  • Expo SQLite contracts — 59 passed
  • SQL storage tests — 26 passed
  • adapter/core typechecks, Prettier, and git diff --check

The live SQLite integration suite requires MINT_URL; IndexedDB contracts were run in Chromium.

Changeset

Adds .changeset/canonical-mint-quote-claimability.md for the affected published packages.

Resolves #387.

@changeset-bot

changeset-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2675637

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-sqlite Patch
@cashu/coco-sqlite-bun Patch
@cashu/coco-expo-sqlite Patch
@cashu/coco-indexeddb Patch
@cashu/coco-react Patch
@cashu/coco-sql-storage 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

@Egge21M
Egge21M marked this pull request as ready for review July 29, 2026 15:04
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.21488% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.81%. Comparing base (1601cf7) to head (2675637).

Files with missing lines Patch % Lines
...ages/core/infra/handlers/mint/MintBolt11Handler.ts 59.61% 21 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #398      +/-   ##
==========================================
+ Coverage   92.63%   92.81%   +0.17%     
==========================================
  Files         111      112       +1     
  Lines       17633    17751     +118     
==========================================
+ Hits        16334    16475     +141     
+ Misses       1299     1276      -23     
Flag Coverage Δ
core-unit 93.24% <94.21%> (+0.17%) ⬆️

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: 2675637aff

ℹ️ 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".

);
}

if (canonicalQuote && (result.category !== 'terminal' || result.quoteSnapshot !== undefined)) {

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 terminal failures during accounting reclassification

When a BOLT12 or on-chain pending operation is missing its NUT-20 pubkey, checkPending returns a terminal missing_quote_pubkey failure together with a quote snapshot. This condition nevertheless reassesses the persisted quote, replaces that terminal result with ready or waiting, and clears terminalFailure; a funded quote can therefore enter execution before the missing-key invariant is handled, and callers receive the wrong check result. Skip accounting reclassification for non-accounting terminal failures so the operation fails immediately.

AGENTS.md reference: AGENTS.md:L162-L164

Useful? React with 👍 / 👎.

Comment on lines 787 to 790
for (const quote of quotes) {
if (!quote.reusable) {
continue;
}
if (getMintQuoteAvailableAmount(quote).isZero()) {
continue;
}

claimed.push(
...(await this.claimMintQuote(quote.mintUrl, quote.method, quote.quoteId, options)),
);

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 Gate startup quote claims on mint trust

Because this loop now forwards BOLT11 quotes as well as reusable quotes, processor startup can redeem an existing paid BOLT11 operation after its mint has been untrusted. claimMintQuote executes pending siblings without checking isTrustedMint, whereas the watcher and recovery paths explicitly skip untrusted mints, so restarting the manager can contact and mutate wallet state for a mint the user disabled. Check trust before automatically claiming these quotes.

AGENTS.md reference: AGENTS.md:L162-L163

Useful? React with 👍 / 👎.

}
}
async ({ mintUrl, method, quoteId }) => {
this.scheduleQuoteClaim(mintUrl, method, quoteId);

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 Queue a follow-up claim when an update arrives in flight

Routing every BOLT11 quote update through scheduleQuoteClaim makes paid transitions subject to its in-flight deduplication. If an UNPAID assessment is still running when a concurrent PAID notification is persisted, the second call is discarded because the quote key is already in claimingQuotes; the first task can then return waiting, and unchanged later snapshots do not emit another quote-update event, leaving the paid operation pending until restart or manual recovery. Record a dirty/pending rerun instead of dropping an update received during an active claim.

Useful? React with 👍 / 👎.

@@ -334,12 +360,6 @@ export class MintBolt12Handler implements MintMethodHandler<'bolt12'> {

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 Revalidate accounting on the execute-time quote

BOLT12 and on-chain execution fetch a fresh remote quote after the service-level claimability assessment, but removing this check from assertQuoteMatchesRequest means that fresh response is no longer rejected when amount_issued exceeds amount_paid. If the mint returns contradictory accounting between readiness and execution, the handlers now pass that invalid quote into mintProofsBolt12 or mintProofsOnchain and sign/send outputs instead of stopping before mutation. Assess the execute-time quote or retain the accounting invariant check here.

AGENTS.md reference: AGENTS.md:L163-L163

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

Centralize Mint Quote Claimability behind canonical accounting

1 participant