NUT-04/05: custom payment methods#382
Conversation
robwoodgate
left a comment
There was a problem hiding this comment.
I think this is a fantastic step forward for wallets to be able to better support unknown methods - I've added some suggested tweaks to the base structs to make life even better.
|
Sorry @asmogo - I questioned an earlier draft that included So maybe we DO need to include |
|
Related #377 which adds the |
There was a problem hiding this comment.
Have implemented custom method support in cashu-ts on top of this + #377 + #387 (cashubtc/cashu-ts#698) and it works nicely. Wallets can quote, mint and melt methods they don't fully understand using just the base structs.
A few tweaks below that came out of implementing. Note the suggestions assume #377 merges first.
Related to my base struct suggestions above: expiry really wants to be in the common mint quote response - every method has one, and a wallet showing a quote for an unknown method needs to know when to stop polling. With #377 in, it's the only field from the suggested base response still left as method-specific.
(method in the quote structs is now covered by #387, which also resolves the WS routing question from #378.)
Can also confirm the optional fee_reserve in the melt base works in practice - onchain can't make it required (it uses fee_options), and bolt11/12 tighten it to required in their own NUTs.
Implements the base-shape harmonization proposed on cashubtc/nuts#382: - MintQuoteBaseRequest gains optional amount and description; method NUTs tighten as needed (bolt11 requires amount). - MintQuoteBaseResponse gains expiry (normalized to null when unset), subsuming the per-method expiry fields. - MeltQuoteBaseRequest gains optional amount (onchain requires it). - MeltQuoteBaseResponse gains request and optional fee_reserve; bolt methods keep fee_reserve required. - Melt base validation now requires the payment request for every method, and normalizes fee_reserve when present. - New MintQuoteGenericResponse/MeltQuoteGenericResponse passthrough types are the defaults on the generic quote methods, so custom-method fields are reachable without casting. BREAKING CHANGE: melt quote responses without a request field now throw; generic quote methods default to the Generic response types.
Implements the base-shape harmonization proposed on cashubtc/nuts#382: - MintQuoteBaseRequest gains optional amount and description; method NUTs tighten as needed (bolt11 requires amount). - MintQuoteBaseResponse gains expiry (normalized to null when unset), subsuming the per-method expiry fields. - MeltQuoteBaseRequest gains optional amount (onchain requires it). - MeltQuoteBaseResponse gains request and optional fee_reserve; bolt methods keep fee_reserve required. - Melt base validation now requires the payment request for every method, and normalizes fee_reserve when present. - New MintQuoteGenericResponse/MeltQuoteGenericResponse passthrough types are the defaults on the generic quote methods, so custom-method fields are reachable without casting. BREAKING CHANGE: melt quote responses without a request field now throw; generic quote methods default to the Generic response types.
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
|
Added asmogo#1 with my suggestions ready to merge into this PR if acceptable. |
Widen common quote structs for custom payment methods
Implements the base-shape harmonization proposed on cashubtc/nuts#382: - MintQuoteBaseRequest gains optional amount and description; method NUTs tighten as needed (bolt11 requires amount). - MintQuoteBaseResponse gains expiry (normalized to null when unset), subsuming the per-method expiry fields. - MeltQuoteBaseRequest gains optional amount (onchain requires it). - MeltQuoteBaseResponse gains request and optional fee_reserve; bolt methods keep fee_reserve required. - Melt base validation now requires the payment request for every method, and normalizes fee_reserve when present. - New MintQuoteGenericResponse/MeltQuoteGenericResponse passthrough types are the defaults on the generic quote methods, so custom-method fields are reachable without casting. BREAKING CHANGE: melt quote responses without a request field now throw; generic quote methods default to the Generic response types.
|
@callebtc - I've fixed the merge conflicts in this one. |
…cts (#698) Matures the NUT-04/05 base payloads so wallets can handle generic and custom payment methods, implementing: - **cashubtc/nuts#377** — mint quote accounting: `amount_paid`, `amount_issued` and `updated_at` become base mint quote response fields for every payment method; `state` is deprecated (but always populated for bolt11). - **cashubtc/nuts#382** — custom payment methods: base request/response structs widened per the shapes suggested in review, generic quote responses get base normalization + validation, and new `MintQuoteGenericResponse` / `MeltQuoteGenericResponse` passthrough types are the defaults on the generic quote methods so custom-method fields are reachable without casting. - **cashubtc/nuts#387** — every quote response carries a `method` field: populated from the request endpoint when the mint omits it (no mint ships it yet), throwing when a reported method disagrees with the endpoint. Once mints implement it, NUT-17 generic `mint_quote`/`melt_quote` notifications become routable by method. Follows up on #672 (nuts#374 `method_name`), which already merged. ## Design notes - **Required-but-derived accounting.** For mints that predate quote accounting, `amount_paid`/`amount_issued` are derived from the legacy single-use `state` + `amount` (UNPAID → 0/0, PAID → amount/0, ISSUED → amount/amount); conversely a missing bolt11 `state` is derived from the accounting fields. Underivable responses throw, matching the existing melt-side strictness. `updated_at` is `number | null` (null when the mint does not report it — it cannot be derived). - **Base shapes per the nuts#382 review suggestions.** Mint quote request base gains optional `amount`/`description`; `expiry` moves into the mint quote response base (normalized to `null` when unset); melt quote request base gains optional `amount`; melt quote response base gains required `request` and optional `fee_reserve` (bolt methods keep it required, onchain keeps `fee_options`). - **Mintable amount enforced for all methods.** `validateMintQuoteAvailableAmount` loses its bolt12/onchain gate: mintable = `amount_paid − amount_issued` is checked whenever the quote object carries accounting fields. Stored quotes without them (pre-accounting) skip the check, as before. - Method-id validation (`^[a-z0-9_-]+$`) and the generic `/v1/{mint,melt}/quote/{method}` plumbing already existed and are unchanged. ## Breaking changes - `MintQuoteBaseResponse` requires `amount_paid`, `amount_issued`, `updated_at`, `expiry`; `MeltQuoteBaseResponse` requires `request`. Type-breaking for code constructing quote literals; runtime consumers are unaffected because normalization guarantees the fields. - Non-conformant quote responses that previously passed through unvalidated (mint quotes missing `quote`/`request`/`unit` or underivable accounting; melt quotes missing `request`) now throw `Invalid response from mint`.
Implementations
support custom payment methods not defined in a dedicated NUT.