Skip to content

Align NUT-04 mint quote accounting with amount_paid / amount_issued#377

Open
Egge21M wants to merge 1 commit into
cashubtc:mainfrom
Egge21M:generic-paid-issued
Open

Align NUT-04 mint quote accounting with amount_paid / amount_issued#377
Egge21M wants to merge 1 commit into
cashubtc:mainfrom
Egge21M:generic-paid-issued

Conversation

@Egge21M

@Egge21M Egge21M commented May 24, 2026

Copy link
Copy Markdown
Contributor

This updates NUT-04 to make amount_paid and amount_issued the
canonical way to track mint quote accounting, matching the reusable
quote model already used by NUT-25 and NUT-30.

The deprecated state field is kept as an optional compatibility
field for single-use quotes, with derivation rules documented from
amount_paid and amount_issued. This also adds updated_at to mint
quote responses so clients can avoid applying stale quote updates.

Changes:

@robwoodgate

This comment was marked as outdated.

@robwoodgate

Copy link
Copy Markdown
Collaborator

The updated_at param is a good idea, both for monotonic state tracking as noted, as well as being potentially useful for tracking stalled or failed payments.

@a1denvalu3

Copy link
Copy Markdown
Contributor

Agreed.

@robwoodgate robwoodgate left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ACK

@Egge21M

Egge21M commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Chatted with some folks offline. They raised the point that the combination of amount_issued and amount_paid is already enough to allow monotonic state updates (e.g. new_amount_issued + new_amount_paid > old_amount_issued + old_amount_paid). However this is less explicit and mints should not have any issue adding updated_at.

Should I kick it, or keep it?

@robwoodgate

Copy link
Copy Markdown
Collaborator

Chatted with some folks offline. They raised the point that the combination of amount_issued and amount_paid is already enough to allow monotonic state updates (e.g. new_amount_issued + new_amount_paid > old_amount_issued + old_amount_paid). However this is less explicit and mints should not have any issue adding updated_at.

Should I kick it, or keep it?

I think it has utility - whilst the combination does tell you if the state has changed, it doesn't tell you WHEN.

So unless payload size is a concern, it might be more useful than not, esp for consumer apps.

@Egge21M Egge21M force-pushed the generic-paid-issued branch from d251850 to 4f4f697 Compare June 9, 2026 09:48
@thesimplekid thesimplekid self-requested a review June 9, 2026 14:48
Comment thread 04.md

For reusable quotes, no finite `state` can fully represent whether the quote may receive future payments. Wallets **MUST** use `amount_paid` and `amount_issued` to determine the currently mintable amount.

Mints **MUST** update `updated_at` whenever `amount_paid` or `amount_issued` changes. Mints **SHOULD** ensure that `updated_at` monotonically increases for each quote, even if multiple updates occur within the same timestamp resolution. Wallets that receive multiple responses for the same quote **MUST NOT** replace locally stored quote data with a response whose `updated_at` is lower than the latest processed value for that quote. Wallets **MUST NOT** decrease locally stored `amount_paid` or `amount_issued` values based on stale responses.

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TBH I am not sure how to handle this properly. This document is now written in a way that makes updated_at mandatory (e.g. Mints **MUST** include). Including this addition would mean covering off-spec mint behavior. Maybe the new fields should be added as optional an with SHOULD language instead?

@robwoodgate robwoodgate Jun 13, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

On reflection you are right. Handling out of spec older mints is not the protocol's concern.

Ignore my suggestion - leave it as it was.

robwoodgate added a commit to robwoodgate/cashu-ts that referenced this pull request Jun 12, 2026
Implements cashubtc/nuts#377: amount_paid, amount_issued and updated_at
become base mint quote response fields for every payment method. For
mints that predate quote accounting, the amounts are derived from the
legacy single-use state; conversely, a missing bolt11 state is derived
from the accounting fields. updated_at is null when not reported.

Generic mint quote responses now get base normalization (quote, request,
unit, accounting) like melt quotes already did, and the wallet enforces
the mintable amount (paid minus issued) for all methods, not just
bolt12/onchain.

BREAKING CHANGE: MintQuoteBaseResponse requires amount_paid,
amount_issued and updated_at; non-conformant mint quote responses that
previously passed through unvalidated now throw.
robwoodgate added a commit to robwoodgate/cashu-ts that referenced this pull request Jun 12, 2026
Implements cashubtc/nuts#377: amount_paid, amount_issued and updated_at
become base mint quote response fields for every payment method. For
mints that predate quote accounting, the amounts are derived from the
legacy single-use state; conversely, a missing bolt11 state is derived
from the accounting fields. updated_at is null when not reported.

Generic mint quote responses now get base normalization (quote, request,
unit, accounting) like melt quotes already did, and the wallet enforces
the mintable amount (paid minus issued) for all methods, not just
bolt12/onchain.

BREAKING CHANGE: MintQuoteBaseResponse requires amount_paid,
amount_issued and updated_at; non-conformant mint quote responses that
previously passed through unvalidated now throw.
robwoodgate added a commit to robwoodgate/nuts that referenced this pull request Jun 15, 2026
Consolidates review feedback on the generic payment method PR. Scoped to
the delta NOT covered by cashubtc#377 (accounting fields) or cashubtc#387 (method), so
those can merge independently:

- common mint quote request: optional amount, description, pubkey
- common melt quote request: optional amount
- common mint quote response: expiry, optional pubkey
- common melt quote response: request, optional fee_reserve
- custom mint quotes MUST carry the accounting fields
- custom melt quotes MUST use the standard UNPAID/PENDING/PAID states
Comment thread 04.md
"amount_paid": <int>,
"amount_issued": <int>,
"updated_at": <int>,
"state": <str_enum[STATE]>, // Deprecated, optional

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

State should not be in nut04 that is in nut 23 as it is bolt11 specefic.

Comment thread 04.md
- `amount_paid` is the total amount that has been paid to the mint for this quote and is eligible for minting, denominated in `unit`
- `amount_issued` is the total amount of ecash that has been issued for this quote, denominated in `unit`
- `updated_at` is a Unix timestamp integer indicating when the quote was last updated
- `state` is a deprecated compatibility field

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- `state` is a deprecated compatibility field

Comment thread 04.md
Comment on lines +76 to +85

`amount_paid` and `amount_issued` are the canonical way to track the state of a mint quote. The deprecated `state` field **SHOULD** be included for single-use quotes for compatibility with older clients. Wallets **SHOULD** use `amount_paid` and `amount_issued` instead of `state` whenever these fields are present.

For single-use quotes, the deprecated finite `state` can be derived from `amount_paid` and `amount_issued`:

- `"UNPAID"` if `amount_paid == 0` and `amount_issued == 0`
- `"PAID"` if `amount_paid > amount_issued`
- `"ISSUED"` if `amount_paid == amount_issued` and `amount_issued > 0`

For reusable quotes, no finite `state` can fully represent whether the quote may receive future payments. Wallets **MUST** use `amount_paid` and `amount_issued` to determine the currently mintable amount.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nut23 specefic

Comment thread 20.md
@robwoodgate

Copy link
Copy Markdown
Collaborator

thesimplekid added a commit to thesimplekid/cdk that referenced this pull request Jun 17, 2026
Implement the mint quote accounting changes from cashubtc/nuts#377
by exposing amount_paid, amount_issued, and updated_at on mint quote responses.

Keep the deprecated state field only for BOLT11 compatibility
while deriving local quote state from the canonical counters for all mint methods.
Persist updated_at in wallet quote storage so stale responses cannot move amount_paid or amount_issued backwards.
@robwoodgate

Copy link
Copy Markdown
Collaborator

Cashu-TS implements in:

Sorry, now moved to:

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.

5 participants