feat: onchain payment method #365

Merged
thesimplekid merged 16 commits from onchain into main 2026-05-20 16:20:43 +00:00
thesimplekid commented 2026-04-23 19:12:55 +00:00 (Migrated from github.com)
- [ ] cdk https://github.com/cashubtc/cdk/pull/1870' - [ ] cashu-ts: https://github.com/cashubtc/cashu-ts/pull/633
robwoodgate (Migrated from github.com) approved these changes 2026-04-24 18:25:59 +00:00
robwoodgate (Migrated from github.com) left a comment

One nit suggestion, one clarification question - otherwise LGTM!

One nit suggestion, one clarification question - otherwise LGTM!
robwoodgate (Migrated from github.com) commented 2026-04-24 18:20:20 +00:00

What happens if the wallet sends more proofs? Are they melted and sent on to the bitcoin address?

I assume that's problematic as it affects the fee. But we should be clear to prevent inconsistent mint action.

What happens if the wallet sends more proofs? Are they melted and sent on to the bitcoin address? I assume that's problematic as it affects the fee. But we should be clear to prevent inconsistent mint action.
robwoodgate (Migrated from github.com) commented 2026-04-24 18:23:55 +00:00

Small nit... it would be super helpful if we kept the base NUT keys in order and first in the list. Then it's more obvious what has been added in the NUT.

    "quote": <str>,
    "amount": <int>,
    "unit": <str_enum[UNIT]>,
    "state": <str_enum[STATE]>,
    "expiry": <int>,
    "request": <str>,
    "fee": <int>,
    "estimated_blocks": <int>,
    "outpoint": <str|null>
  }
Small nit... it would be super helpful if we kept the base NUT keys in order and first in the list. Then it's more obvious what has been added in the NUT. ```suggestion "quote": <str>, "amount": <int>, "unit": <str_enum[UNIT]>, "state": <str_enum[STATE]>, "expiry": <int>, "request": <str>, "fee": <int>, "estimated_blocks": <int>, "outpoint": <str|null> } ```
robwoodgate commented 2026-04-25 10:26:02 +00:00 (Migrated from github.com)

Discussed briefly offline:

The multi-quote is quite a significant change to NUT-05. It maybe also adds resource tracking overhead (4-5 quotes per request) and requires new wallet UI to handle quote selection (vs existing methods).

The tradeoff/benefit might be worth it, but perhaps we should consider setting a desired target confirmation speed in the request (eg target_blocks / priority etc) and just returning the closest quote so NUT-05 stays "one request, one quote"

Discussed briefly offline: The multi-quote is quite a significant change to NUT-05. It maybe also adds resource tracking overhead (4-5 quotes per request) and requires new wallet UI to handle quote selection (vs existing methods). The tradeoff/benefit might be worth it, but perhaps we should consider setting a desired target confirmation speed in the request (eg `target_blocks` / `priority` etc) and just returning the closest quote so NUT-05 stays "one request, one quote"
TheMhv (Migrated from github.com) reviewed 2026-04-25 13:09:53 +00:00
@ -0,0 +42,4 @@
- `expiry` is the Unix timestamp until which the mint quote is valid
- `pubkey` is the public key from the request
- `amount_paid` is the total confirmed amount paid to the request in UTXOs that are eligible for minting
- `amount_issued` is the amount of ecash that has been issued for the given mint quote
TheMhv (Migrated from github.com) commented 2026-04-25 13:09:53 +00:00

I think that is good to return the minimum confirmations from mint's to wallet. The wallet can check it outside before try to check the quote state.

```json
{
  "quote": <str>,
  "request": <str>,
  "unit": <str_enum[UNIT]>,
  "expiry": <int|null>,
  "pubkey": <str>,
  "min_confirmations": <int>,
  "amount_paid": <int>,
  "amount_issued": <int>,
}
```

Where:

- `quote` is the quote ID
- `request` is the Bitcoin address to send funds to
- `expiry` is the Unix timestamp until which the mint quote is valid
- `pubkey` is the public key from the request
- `min_confirmations` is the minimum number of bitcoin transaction confirmations specified for the mint's
- `amount_paid` is the total confirmed amount paid to the request
- `amount_issued` is the amount of ecash that has been issued for the given mint quote
I think that is good to return the minimum confirmations from mint's to wallet. The wallet can check it outside before try to check the quote state. ````suggestion ```json { "quote": <str>, "request": <str>, "unit": <str_enum[UNIT]>, "expiry": <int|null>, "pubkey": <str>, "min_confirmations": <int>, "amount_paid": <int>, "amount_issued": <int>, } ``` Where: - `quote` is the quote ID - `request` is the Bitcoin address to send funds to - `expiry` is the Unix timestamp until which the mint quote is valid - `pubkey` is the public key from the request - `min_confirmations` is the minimum number of bitcoin transaction confirmations specified for the mint's - `amount_paid` is the total confirmed amount paid to the request - `amount_issued` is the amount of ecash that has been issued for the given mint quote ````
TheMhv (Migrated from github.com) reviewed 2026-04-25 13:24:59 +00:00
TheMhv (Migrated from github.com) commented 2026-04-25 13:24:59 +00:00

We need to put some security disclosure because the example has a real Bitcoin address, that someone can send funds to it.

Maybe we can change this address to an testnet address, because this specified address is associated with a big scam.

We need to put some security disclosure because the example has a real Bitcoin address, that someone can send funds to it. Maybe we can change this address to an testnet address, because this specified address is associated with a big scam.
TheMhv (Migrated from github.com) reviewed 2026-04-25 13:40:47 +00:00
@ -0,0 +42,4 @@
- `expiry` is the Unix timestamp until which the mint quote is valid
- `pubkey` is the public key from the request
- `amount_paid` is the total confirmed amount paid to the request in UTXOs that are eligible for minting
- `amount_issued` is the amount of ecash that has been issued for the given mint quote
TheMhv (Migrated from github.com) commented 2026-04-25 13:40:47 +00:00

OBS: That's already implemented on MintMethodSetting, but i think that's good to return on PostMintQuoteOnchainResponse too.

https://github.com/cashubtc/nuts/pull/365/changes#diff-f42c146fe5cf1807b0e4ef524f727dcfa12d8e2221480cf58577b5410d478c53R165

OBS: That's already implemented on `MintMethodSetting`, but i think that's good to return on `PostMintQuoteOnchainResponse` too. https://github.com/cashubtc/nuts/pull/365/changes#diff-f42c146fe5cf1807b0e4ef524f727dcfa12d8e2221480cf58577b5410d478c53R165
TheMhv (Migrated from github.com) reviewed 2026-04-25 13:57:39 +00:00
TheMhv (Migrated from github.com) commented 2026-04-25 13:57:39 +00:00

Since we has a minimum number of confirmations, the state will update to paid only if reach it.

- `"PENDING"` means that the transaction is being processed by the mint but not reach the number of confirmations.
- `"PAID"` means that the transaction has been mined and confirmed.
Since we has a minimum number of confirmations, the state will update to paid only if reach it. ```suggestion - `"PENDING"` means that the transaction is being processed by the mint but not reach the number of confirmations. - `"PAID"` means that the transaction has been mined and confirmed. ```
thesimplekid (Migrated from github.com) reviewed 2026-04-25 20:43:25 +00:00
thesimplekid (Migrated from github.com) commented 2026-04-25 20:43:25 +00:00

Good call we should make it a partial address.

Good call we should make it a partial address.
gudnuf (Migrated from github.com) reviewed 2026-04-27 15:55:36 +00:00
gudnuf (Migrated from github.com) commented 2026-04-27 15:33:04 +00:00

is this necessary? its already stated in NUT-20

is this necessary? its already stated in NUT-20
gudnuf (Migrated from github.com) commented 2026-04-27 15:47:44 +00:00

Also, why not support nut-08? Its nice to not have to do a swap first, but I guess that's abusing change.

nit, but that last sentence is a bit weird to me because the fact that nut08 is not supported is why the mint must not return change

Where `fee` is the absolute onchain transaction fee of the selected melt quote, and `estimated_blocks` is the estimated number of blocks until confirmation. The mint expects the wallet to include `Proofs` of _at least_ `total_amount = amount + fee + input_fee` where `input_fee` is calculated from the keyset's `input_fee_ppk` as described in [NUT-02][02]. [NUT-08][08] does not apply to the `onchain` melt method, so the mint **MUST NOT** return `change` outputs for overpaid fees.
Also, why not support nut-08? Its nice to not have to do a swap first, but I guess that's abusing change. nit, but that last sentence is a bit weird to me because the fact that nut08 is not supported is why the mint must not return change ```suggestion Where `fee` is the absolute onchain transaction fee of the selected melt quote, and `estimated_blocks` is the estimated number of blocks until confirmation. The mint expects the wallet to include `Proofs` of _at least_ `total_amount = amount + fee + input_fee` where `input_fee` is calculated from the keyset's `input_fee_ppk` as described in [NUT-02][02]. [NUT-08][08] does not apply to the `onchain` melt method, so the mint **MUST NOT** return `change` outputs for overpaid fees. ```
gudnuf commented 2026-04-27 16:47:44 +00:00 (Migrated from github.com)

It maybe also adds resource tracking overhead (4-5 quotes per request)

@robwoodgate can you elaborate more on this? My thinking is that after the wallet picks the quote they only have to track that one and forget about the rest

requires new wallet UI to handle quote selection (vs existing methods).

Providing multiple options is pretty standard for onchain UIs. If a wallet doesn't want to offer quote selection to the user, then it can just select the fastest one and leave the UI unchanged.

> It maybe also adds resource tracking overhead (4-5 quotes per request) @robwoodgate can you elaborate more on this? My thinking is that after the wallet picks the quote they only have to track that one and forget about the rest > requires new wallet UI to handle quote selection (vs existing methods). Providing multiple options is pretty standard for onchain UIs. If a wallet doesn't want to offer quote selection to the user, then it can just select the fastest one and leave the UI unchanged.
thesimplekid commented 2026-04-27 16:51:01 +00:00 (Migrated from github.com)

Also, why not support nut-08? Its nice to not have to do a swap first, but I guess that's abusing change.

This will lead to proofs being stuck pending for 10+ min waiting for confirmation. I think this is bad ux so swapping should be forced. It's also much simpler for the mint to not have to deal with change.

> Also, why not support nut-08? Its nice to not have to do a swap first, but I guess that's abusing change. This will lead to proofs being stuck pending for 10+ min waiting for confirmation. I think this is bad ux so swapping should be forced. It's also much simpler for the mint to not have to deal with change.
robwoodgate commented 2026-04-27 18:37:38 +00:00 (Migrated from github.com)

It maybe also adds resource tracking overhead (4-5 quotes per request)

@robwoodgate can you elaborate more on this? My thinking is that after the wallet picks the quote they only have to track that one and forget about the rest

A mint has to track/hold all quotes it creates. It just seems wasteful on resources to have multiple quotes per request. Could also lead to a DoS vector.

requires new wallet UI to handle quote selection (vs existing methods).

Providing multiple options is pretty standard for onchain UIs. If a wallet doesn't want to offer quote selection to the user, then it can just select the fastest one and leave the UI unchanged.

It's different to the way all other methods are handled. In CTS, for example, I had to write custom handlers vs leaning on the generics. I just don't know as we need it when it's just as simple to send a param with desired confirmation speed up front.

> > It maybe also adds resource tracking overhead (4-5 quotes per request) > > @robwoodgate can you elaborate more on this? My thinking is that after the wallet picks the quote they only have to track that one and forget about the rest > A mint has to track/hold all quotes it creates. It just seems wasteful on resources to have multiple quotes per request. Could also lead to a DoS vector. > > requires new wallet UI to handle quote selection (vs existing methods). > > Providing multiple options is pretty standard for onchain UIs. If a wallet doesn't want to offer quote selection to the user, then it can just select the fastest one and leave the UI unchanged. It's different to the way all other methods are handled. In CTS, for example, I had to write custom handlers vs leaning on the generics. I just don't know as we need it when it's just as simple to send a param with desired confirmation speed up front.
minibits-cash (Migrated from github.com) reviewed 2026-04-29 14:52:05 +00:00
minibits-cash (Migrated from github.com) commented 2026-04-29 14:50:30 +00:00

I'd propose for the wallet to ask for a melt quote targeting desired time (estimated blocks) to be confirmed in the PostMentQuoteOnchainRequest and the mint adjusting and returning a single mint quote based on current fee environment - as an alternative to the mint responding with multiple melt quotes, which would be very specific (and yet options limiting) for this payment method.

Architecturally, transaction timing is pure user / wallet concern and I do not see the reason to increase complexity on the mint side if it can be resolved by 1 additional melt request param that exactly fits user expectation.

I'd propose for the wallet to ask for a melt quote targeting desired time (estimated blocks) to be confirmed in the `PostMentQuoteOnchainRequest ` and the mint adjusting and returning a single mint quote based on current fee environment - as an alternative to the mint responding with multiple melt quotes, which would be very specific (and yet options limiting) for this payment method. Architecturally, transaction timing is pure user / wallet concern and I do not see the reason to increase complexity on the mint side if it can be resolved by 1 additional melt request param that exactly fits user expectation.
thesimplekid (Migrated from github.com) reviewed 2026-04-29 15:51:07 +00:00
thesimplekid (Migrated from github.com) commented 2026-04-29 15:51:07 +00:00

The mint having to handle any number of blocks is more complex. Most exchanges offer a fast (next block), medium and slow option with the later two being cheaper as your tx gets batched with more txs.

One option is as Rob suggested where the wallet includes a requested conf in the request and the mint gives a quote back for the closest bucket. We would need to define how closest is chosen (round up or down).

The mint could advertise its "buckets" in the mint info. Though I generally dislike over using mint info.

The mint having to handle any number of blocks is more complex. Most exchanges offer a fast (next block), medium and slow option with the later two being cheaper as your tx gets batched with more txs. One option is as Rob suggested where the wallet includes a requested conf in the request and the mint gives a quote back for the closest bucket. We would need to define how closest is chosen (round up or down). The mint could advertise its "buckets" in the mint info. Though I generally dislike over using mint info.
thesimplekid commented 2026-04-29 15:55:25 +00:00 (Migrated from github.com)

A mint has to track/hold all quotes it creates. It just seems wasteful on resources to have multiple quotes per request. Could also lead to a DoS vector.

I don't think this is too much of a concern it's really only a couple extra db entry's and there is nothing really to monitor like there is for mint quotes.

It's different to the way all other methods are handled. In CTS, for example, I had to write custom handlers vs leaning on the generics. I just don't know as we need it when it's just as simple to send a param with desired confirmation speed up front.

This though is a valid point and would be the justification for change over the dos point.

> A mint has to track/hold all quotes it creates. It just seems wasteful on resources to have multiple quotes per request. Could also lead to a DoS vector. I don't think this is too much of a concern it's really only a couple extra db entry's and there is nothing really to monitor like there is for mint quotes. > It's different to the way all other methods are handled. In CTS, for example, I had to write custom handlers vs leaning on the generics. I just don't know as we need it when it's just as simple to send a param with desired confirmation speed up front. This though is a valid point and would be the justification for change over the dos point.
orangeshyguy21 (Migrated from github.com) reviewed 2026-04-29 17:08:53 +00:00
@ -14,6 +14,7 @@ Method-specific NUTs describe how to handle different payment methods. The curre
orangeshyguy21 (Migrated from github.com) commented 2026-04-29 17:08:53 +00:00

nit pick: the plural here would conform better to existing language

- [NUT-XX][XX] for onchain Bitcoin transactions
nit pick: the plural here would conform better to existing language ```suggestion - [NUT-XX][XX] for onchain Bitcoin transactions ```
robwoodgate (Migrated from github.com) reviewed 2026-04-29 18:56:35 +00:00
robwoodgate (Migrated from github.com) commented 2026-04-29 18:56:35 +00:00

I'd propose adding a "priority" param for PostMeltQuoteOnchainRequest, something along the lines of:

"priority": <str_enum[L,M,H]>,

where priority is one of the following values:

  • L: 11+ blocks
  • M: 3 - 10 blocks
  • H: 1 - 2 blocks
I'd propose adding a "`priority`" param for `PostMeltQuoteOnchainRequest`, something along the lines of: ``` "priority": <str_enum[L,M,H]>, ``` where `priority` is one of the following values: - `L`: 11+ blocks - `M`: 3 - 10 blocks - `H`: 1 - 2 blocks
gudnuf (Migrated from github.com) reviewed 2026-04-29 21:37:13 +00:00
gudnuf (Migrated from github.com) commented 2026-04-29 21:37:13 +00:00

I'd propose adding a "priority" param for PostMeltQuoteOnchainRequest

A concern I have with this is that I think a wallet UI should be able to show the user the actual fee for each priority option. If the wallet has to pick the priority up front, then the only way I can think to know the actual fee is by creating a quote for each.

I would prefer to just get all the onchain quotes at once -> present them to the user -> fire the melt on a quote being selected.

> I'd propose adding a "priority" param for PostMeltQuoteOnchainRequest A concern I have with this is that I think a wallet UI should be able to show the user the actual fee for each priority option. If the wallet has to pick the priority up front, then the only way I can think to know the actual fee is by creating a quote for each. I would prefer to just get all the onchain quotes at once -> present them to the user -> fire the melt on a quote being selected.
robwoodgate (Migrated from github.com) reviewed 2026-04-30 19:00:54 +00:00
robwoodgate (Migrated from github.com) commented 2026-04-30 19:00:54 +00:00

An alternative approach would be to set a max_fee param to PostMeltQuoteOnchainRequest, then the mint can return the "fastest" settlement quote that fits within the user-set maximum.

I don't really mind how it is done, but I do think allowing NUT-05 to return multiple quotes is a line we should not cross without careful consideration. Although I've already written the code for CTS, it still seems "wrong" to have this exception.

I'm reminded of ZEN no.8: "Special cases aren't special enough to break the rules."

An alternative approach would be to set a `max_fee` param to `PostMeltQuoteOnchainRequest`, then the mint can return the "fastest" settlement quote that fits within the user-set maximum. I don't really mind how it is done, but I do think allowing NUT-05 to return multiple quotes is a line we should not cross without careful consideration. Although I've already written the code for CTS, it still seems "wrong" to have this exception. I'm reminded of ZEN no.8: _"Special cases aren't special enough to break the rules."_
minibits-cash (Migrated from github.com) reviewed 2026-04-30 21:57:45 +00:00
minibits-cash (Migrated from github.com) commented 2026-04-30 21:57:45 +00:00

I did not meant to make the mint handle any blocks, just fee estimate. As it is always an estimate, it could be done both by passing L,M,H in the request or - as I've proposed - to pass acceptable number of blocks to confirm, e.g. 1 or 10. Mint's fee estimation would then calculate the fee. Both are in essence the same, keep user specific concern on the wallet side and do not introduce special structures in the response.

I did not meant to make the mint handle any blocks, just fee estimate. As it is always an estimate, it could be done both by passing L,M,H in the request or - as I've proposed - to pass acceptable number of blocks to confirm, e.g. 1 or 10. Mint's fee estimation would then calculate the fee. Both are in essence the same, keep user specific concern on the wallet side and do not introduce special structures in the response.
thesimplekid (Migrated from github.com) reviewed 2026-05-01 12:40:09 +00:00
@ -14,6 +14,7 @@ Method-specific NUTs describe how to handle different payment methods. The curre
thesimplekid (Migrated from github.com) commented 2026-05-01 12:40:09 +00:00

One way to avoid the change here would be to define the onchain response as having an interior list instead of returning a list of quotes this avoids any change to nut05 wording. But is maybe just working around the spec and not in the spirit of it

One way to avoid the change here would be to define the onchain response as having an interior list instead of returning a list of quotes this avoids any change to nut05 wording. But is maybe just working around the spec and not in the spirit of it
thesimplekid (Migrated from github.com) reviewed 2026-05-01 12:44:36 +00:00
thesimplekid (Migrated from github.com) commented 2026-05-01 12:44:36 +00:00

This still has the issue gudnuf pointed out of the user does not know the fee before hand for x confs. The user is choosing between 1 or 10 confs based on the cost but if they do not know the cost they have to create two separate network calls to create 2 quotes if they are not returned as a list up front.

This still has the issue gudnuf pointed out of the user does not know the fee before hand for x confs. The user is choosing between 1 or 10 confs based on the cost but if they do not know the cost they have to create two separate network calls to create 2 quotes if they are not returned as a list up front.
thesimplekid (Migrated from github.com) reviewed 2026-05-04 19:42:09 +00:00
thesimplekid (Migrated from github.com) commented 2026-05-04 19:42:08 +00:00
addressed in https://github.com/cashubtc/nuts/pull/365/commits/706ef5c474270a53a4ce1c94cd336d934d2f18f7
robwoodgate (Migrated from github.com) approved these changes 2026-05-04 20:33:02 +00:00
robwoodgate (Migrated from github.com) left a comment

The new fee_options works for me - best of both worlds. ACK.

The new fee_options works for me - best of both worlds. ACK.
@ -0,0 +200,4 @@
],
"selected_fee_index": <int|null>,
"outpoint": <str|null>
}
robwoodgate (Migrated from github.com) commented 2026-05-04 20:28:53 +00:00

nit - could we order the NUT-05 defaults first? makes it easier to see what has been added by the NUT

{
  "quote": <str>,
  "amount": <int>,
  "unit": <str_enum[UNIT]>,
  "state": <str_enum[STATE]>,
  "expiry": <int>,
  "request": <str>,
  "fee_options": [
    {
      "fee": <int>,
      "estimated_blocks": <int>
    }
  ],
  "selected_estimated_blocks": <int|null>,
  "outpoint": <str|null>
}
nit - could we order the NUT-05 defaults first? makes it easier to see what has been added by the NUT ```suggestion { "quote": <str>, "amount": <int>, "unit": <str_enum[UNIT]>, "state": <str_enum[STATE]>, "expiry": <int>, "request": <str>, "fee_options": [ { "fee": <int>, "estimated_blocks": <int> } ], "selected_estimated_blocks": <int|null>, "outpoint": <str|null> } ```
robwoodgate (Migrated from github.com) reviewed 2026-05-05 15:11:36 +00:00
robwoodgate (Migrated from github.com) commented 2026-05-05 15:11:35 +00:00

I think keeping it here does more good than harm. Easy to overlook a warning if it is in another NUT.

I think keeping it here does more good than harm. Easy to overlook a warning if it is in another NUT.
gudnuf (Migrated from github.com) approved these changes 2026-05-05 18:42:07 +00:00
robwoodgate commented 2026-05-06 07:35:53 +00:00 (Migrated from github.com)

A thought on SIG_ALL: onchain melt SIG_ALL currently commits to inputs || quote because there are no NUT-08 change outputs. It does not commit to the selected estimated_blocks.

The exact input amount binds the selected fee amount, BUT if two fee_options have the same fee and different estimated_blocks, the same signed proofs can authorize either fee option.

I'm not sure that distinction matters, but if it does, duplicate fee values should also be forbidden... otherwise estimated_blocks would have to be included in the signed message, which is making onchain "special" again :/

A thought on SIG_ALL: onchain melt SIG_ALL currently commits to `inputs || quote` because there are no NUT-08 change outputs. It does not commit to the selected `estimated_blocks`. The exact input amount binds the selected fee amount, BUT if two `fee_options` have the same fee and different `estimated_blocks`, the same signed proofs can authorize either fee option. I'm not sure that distinction matters, but if it does, duplicate fee values should also be forbidden... otherwise `estimated_blocks` would have to be included in the signed message, which is making onchain "special" again :/
thesimplekid commented 2026-05-06 10:34:46 +00:00 (Migrated from github.com)

A thought on SIG_ALL: onchain melt SIG_ALL currently commits to inputs || quote because there are no NUT-08 change outputs. It does not commit to the selected estimated_blocks.

The exact input amount binds the selected fee amount, BUT if two fee_options have the same fee and different estimated_blocks, the same signed proofs can authorize either fee option.

I'm not sure that distinction matters, but if it does, duplicate fee values should also be forbidden... otherwise estimated_blocks would have to be included in the signed message, which is making onchain "special" again :/

Lets just avoid this and forbid two from having the same fee. There is no reason for two to have the same fee.

> A thought on SIG_ALL: onchain melt SIG_ALL currently commits to `inputs || quote` because there are no NUT-08 change outputs. It does not commit to the selected `estimated_blocks`. > > The exact input amount binds the selected fee amount, BUT if two `fee_options` have the same fee and different `estimated_blocks`, the same signed proofs can authorize either fee option. > > I'm not sure that distinction matters, but if it does, duplicate fee values should also be forbidden... otherwise `estimated_blocks` would have to be included in the signed message, which is making onchain "special" again :/ Lets just avoid this and forbid two from having the same fee. There is no reason for two to have the same fee.
thesimplekid (Migrated from github.com) reviewed 2026-05-06 12:30:48 +00:00
thesimplekid (Migrated from github.com) commented 2026-05-06 12:30:48 +00:00
Addressed in https://github.com/cashubtc/nuts/pull/365/commits/e62c585648b833a010dc8a94100d05f35df853d0
thesimplekid commented 2026-05-06 12:31:24 +00:00 (Migrated from github.com)

A thought on SIG_ALL: onchain melt SIG_ALL currently commits to inputs || quote because there are no NUT-08 change outputs. It does not commit to the selected estimated_blocks.

The exact input amount binds the selected fee amount, BUT if two fee_options have the same fee and different estimated_blocks, the same signed proofs can authorize either fee option.

I'm not sure that distinction matters, but if it does, duplicate fee values should also be forbidden... otherwise estimated_blocks would have to be included in the signed message, which is making onchain "special" again :/

A thought on SIG_ALL: onchain melt SIG_ALL currently commits to inputs || quote because there are no NUT-08 change outputs. It does not commit to the selected estimated_blocks.

The exact input amount binds the selected fee amount, BUT if two fee_options have the same fee and different estimated_blocks, the same signed proofs can authorize either fee option.

I'm not sure that distinction matters, but if it does, duplicate fee values should also be forbidden... otherwise estimated_blocks would have to be included in the signed message, which is making onchain "special" again :/

Addressed in https://github.com/cashubtc/nuts/pull/365/commits/e62c585648b833a010dc8a94100d05f35df853d0

> A thought on SIG_ALL: onchain melt SIG_ALL currently commits to `inputs || quote` because there are no NUT-08 change outputs. It does not commit to the selected `estimated_blocks`. > > The exact input amount binds the selected fee amount, BUT if two `fee_options` have the same fee and different `estimated_blocks`, the same signed proofs can authorize either fee option. > > I'm not sure that distinction matters, but if it does, duplicate fee values should also be forbidden... otherwise `estimated_blocks` would have to be included in the signed message, which is making onchain "special" again :/ > A thought on SIG_ALL: onchain melt SIG_ALL currently commits to `inputs || quote` because there are no NUT-08 change outputs. It does not commit to the selected `estimated_blocks`. > > The exact input amount binds the selected fee amount, BUT if two `fee_options` have the same fee and different `estimated_blocks`, the same signed proofs can authorize either fee option. > > I'm not sure that distinction matters, but if it does, duplicate fee values should also be forbidden... otherwise `estimated_blocks` would have to be included in the signed message, which is making onchain "special" again :/ Addressed in https://github.com/cashubtc/nuts/pull/365/commits/e62c585648b833a010dc8a94100d05f35df853d0
thesimplekid (Migrated from github.com) reviewed 2026-05-06 12:31:42 +00:00
@ -0,0 +200,4 @@
],
"selected_fee_index": <int|null>,
"outpoint": <str|null>
}
thesimplekid (Migrated from github.com) commented 2026-05-06 12:31:42 +00:00

addressed in 208799ca

addressed in 208799ca
thesimplekid (Migrated from github.com) reviewed 2026-05-06 12:32:48 +00:00
thesimplekid (Migrated from github.com) commented 2026-05-06 12:32:48 +00:00

Think this is addressed in other threads on this PR marking as resolved.

Think this is addressed in other threads on this PR marking as resolved.
thesimplekid (Migrated from github.com) reviewed 2026-05-06 12:33:43 +00:00
@ -0,0 +42,4 @@
- `expiry` is the Unix timestamp until which the mint quote is valid
- `pubkey` is the public key from the request
- `amount_paid` is the total confirmed amount paid to the request in UTXOs that are eligible for minting
- `amount_issued` is the amount of ecash that has been issued for the given mint quote
thesimplekid (Migrated from github.com) commented 2026-05-06 12:33:43 +00:00

I think including it in the quote implies it can be different per quote and is not a global config of the mint that should not change much so it can be kept in mint info.

I think including it in the quote implies it can be different per quote and is not a global config of the mint that should not change much so it can be kept in mint info.
thesimplekid (Migrated from github.com) reviewed 2026-05-06 12:37:10 +00:00
thesimplekid (Migrated from github.com) commented 2026-05-06 12:37:10 +00:00

I think this is addressed by https://github.com/cashubtc/nuts/pull/365/commits/cdb5d264cfcce615fefa07ef63e975f747bd7afb so going to mark as resolved.

I think this is addressed by https://github.com/cashubtc/nuts/pull/365/commits/cdb5d264cfcce615fefa07ef63e975f747bd7afb so going to mark as resolved.
TheMhv (Migrated from github.com) approved these changes 2026-05-06 12:38:57 +00:00
TheMhv (Migrated from github.com) left a comment

ACK e62c585648b833a010dc8a94100d05f35df853d0

ACK e62c585648b833a010dc8a94100d05f35df853d0
thesimplekid (Migrated from github.com) reviewed 2026-05-06 12:44:49 +00:00
thesimplekid (Migrated from github.com) commented 2026-05-06 12:44:49 +00:00

Resolved in d76e7e55

Resolved in d76e7e55
TheMhv (Migrated from github.com) approved these changes 2026-05-06 13:02:59 +00:00
TheMhv (Migrated from github.com) left a comment

re-ACK d76e7e55588f53bdc3e24a0ecfd3b4e916f09788

re-ACK d76e7e55588f53bdc3e24a0ecfd3b4e916f09788
thesimplekid commented 2026-05-06 16:20:07 +00:00 (Migrated from github.com)

I am rethinking the use of an absolute fee and wondering if we should allow change.

With no ability to give change the mint must be able to fairly accurately as over estimating too much will cause users to complain of high fees and underestimating will cause tx's to fail at melt time when attempting to construct the final one.

I think this problem is exacerbated in payjoin where onchain inputs could change and thus the fee is changed but with a fixed fee up front there is less flexibility that allowing change would provide.

The issue of change being stuck in pending for 10+ min is still valid but this could be made a wallet problem where they should send no more then amount + fee_reserve not that there is no change given.

After CDK dev call seems like this is a good idea and we should allow change

I am rethinking the use of an absolute fee and wondering if we should allow change. With no ability to give change the mint must be able to fairly accurately as over estimating too much will cause users to complain of high fees and underestimating will cause tx's to fail at melt time when attempting to construct the final one. I think this problem is exacerbated in payjoin where onchain inputs could change and thus the fee is changed but with a fixed fee up front there is less flexibility that allowing change would provide. The issue of change being stuck in pending for 10+ min is still valid but this could be made a wallet problem where they **should** send no more then amount + fee_reserve not that there is no change given. After CDK dev call seems like this is a good idea and we should allow change
SatsAndSports commented 2026-05-06 16:28:31 +00:00 (Migrated from github.com)

Resolved. It'll be in the mint-info settings. See responses below

How about including the mint's confirmations number in the PostMintQuoteOnchainResponse, so that the wallet and the end user know how long they might have to wait? A large - but hidden - confirmations number could be a bad UX

(I tried to post this comment in line at XX.md:29, but Github is giving me errors. Hence I'll try this comment)

*Resolved. It'll be in the mint-info settings. See responses below* How about including the mint's `confirmations` number in the `PostMintQuoteOnchainResponse`, so that the wallet and the end user know how long they might have to wait? A large - but hidden - `confirmations` number could be a bad UX _(I tried to post this comment in line at `XX.md:29`, but Github is giving me errors. Hence I'll try this comment)_
SatsAndSports commented 2026-05-06 16:40:35 +00:00 (Migrated from github.com)

TL/DR: I think the only unambiguous way to give any meaning to 'expiry' is to say that the mint must honour any payment which has achieved sufficient confirmations (e.g. 6) within 2,016 blocks of the expiry. So maybe the 'expiry' should be specified as a block height instead of a time?

'evicted' in XX.md:47 might be too ambiguous a term for a NUT. A transaction may be evicted from one node's mempool, but not from another node's mempool, and it could still be confirmed even after being evicted from every Bitcoin Core mempool.

Once a transaction is seen by anyone, the only thing that can stop it being confirmed is if at least one of its inputs is spent in another block (where that block has multiple confirmations). Until that 'double spend' happens, the transaction remains 'confirmable' for the indefinite future.

Ultimately, I don't think we can specify anything related to the mempool.

The only reason to even have an expiry is to free the mint from having to permanently monitor an arbitrarily large set of addresses. Therefore, I think we say that a payment either had sufficient confirmations before a deadline, or it was too late and the mint is free to ignore further payments to that address

(Again, Github is giving me an "Failed to save comment: An internal error occurred, please try again.." error if I try to make the inline comment in the right place)

_TL/DR: I think the only unambiguous way to give any meaning to 'expiry' is to say that the mint must honour any payment which has achieved sufficient confirmations (e.g. 6) within 2,016 blocks of the expiry. So maybe the 'expiry' should be specified as a block height instead of a time?_ 'evicted' in `XX.md:47` might be too ambiguous a term for a NUT. A transaction may be evicted from one node's mempool, but not from another node's mempool, and it could still be confirmed even after being evicted from every Bitcoin Core mempool. Once a transaction is seen by anyone, the only thing that can stop it being confirmed is if at least one of its inputs is spent in another block (where that block has multiple confirmations). Until that 'double spend' happens, the transaction remains 'confirmable' for the indefinite future. Ultimately, I don't think we can specify anything related to the mempool. The only reason to even have an expiry is to free the mint from having to permanently monitor an arbitrarily large set of addresses. Therefore, I think we say that a payment either had sufficient confirmations before a deadline, or it was too late and the mint is free to ignore further payments to that address _(Again, Github is giving me an "Failed to save comment: An internal error occurred, please try again.." error if I try to make the inline comment in the right place)_
thesimplekid commented 2026-05-06 16:45:07 +00:00 (Migrated from github.com)
  "amount_issued": <int>,
  "amount_incoming": <int|null>

Its come up a few times that it may benefit wallet ux to have an amount_incoming value that is the amount the mint sees in the mempool or onchain that has not reached the required number of confs.

This would increase complexity of the mint having to mange mempool tx's that could be dropped from the mempool or removed via RBF.

Also some node backends or services may not provide a mempool so the mint does not have this information. For example a mint that is using compact block filters does not have a mempool.

Given this I lean towards not adding this, as we cannot guarantee mints can support it and I think providing it optionally is worse then not at all and it is up to wallets to use a mempool explorer if they want to show pending.

Also having issues with inline comments.

```suggestion "amount_issued": <int>, "amount_incoming": <int|null> ``` Its come up a few times that it may benefit wallet ux to have an amount_incoming value that is the amount the mint sees in the mempool or onchain that has not reached the required number of confs. This would increase complexity of the mint having to mange mempool tx's that could be dropped from the mempool or removed via RBF. Also some node backends or services may not provide a mempool so the mint does not have this information. For example a mint that is using compact block filters does not have a mempool. Given this I lean towards not adding this, as we cannot guarantee mints can support it and I think providing it optionally is worse then not at all and it is up to wallets to use a mempool explorer if they want to show pending. Also having issues with inline comments.
SatsAndSports commented 2026-05-06 16:45:32 +00:00 (Migrated from github.com)

While minting: Are units other than sat and msat meaningful?

If yes, and usd is meaningful, then maybe the mint should be required to return an exchange rate in PostMintQuoteOnchainResponse?

And I guess a similar question apples to melting, but I haven't read that section in detail yet

While minting: Are `unit`s other than `sat` and `msat` meaningful? If yes, and `usd` is meaningful, then maybe the mint should be required to return an exchange rate in `PostMintQuoteOnchainResponse`? And I guess a similar question apples to melting, but I haven't read that section in detail yet
thesimplekid commented 2026-05-06 16:46:51 +00:00 (Migrated from github.com)

How about including the mint's confirmations number in the PostMintQuoteOnchainResponse, so that the wallet and the end user know how long they might have to wait? A large - but hidden - confirmations number could be a bad UX

(I tried to post this comment in line at XX.md:29, but Github is giving me errors. Hence I'll try this comment)

This is in the mint info, and its up to the wallet how to display this. https://github.com/cashubtc/nuts/pull/365#discussion_r3195422747

> How about including the mint's `confirmations` number in the `PostMintQuoteOnchainResponse`, so that the wallet and the end user know how long they might have to wait? A large - but hidden - `confirmations` number could be a bad UX > > _(I tried to post this comment in line at `XX.md:29`, but Github is giving me errors. Hence I'll try this comment)_ This is in the mint info, and its up to the wallet how to display this. https://github.com/cashubtc/nuts/pull/365#discussion_r3195422747
SatsAndSports commented 2026-05-06 17:02:37 +00:00 (Migrated from github.com)

Nice. Sorry I missed that:

This is in the mint info, and its up to the wallet how to display this. #365 (comment)

How about adding a few words to refer to NUT-04 and make this more explicit? I guess we can assume that mint developers would know this; but wallet devs mightn't know exactly where to check. Something like:

### Example `MintMethodSetting`

See NUT-04 for exactly how the mint can report, via a `MintMethodSetting` in its _mint info_, that it supports `onchain`:

Nice. Sorry I missed that: > This is in the mint info, and its up to the wallet how to display this. [#365 (comment)](https://github.com/cashubtc/nuts/pull/365#discussion_r3195422747) How about adding a few words to refer to NUT-04 and make this more explicit? I guess we can assume that mint developers would know this; but wallet devs mightn't know exactly where to check. Something like: ``` ### Example `MintMethodSetting` See NUT-04 for exactly how the mint can report, via a `MintMethodSetting` in its _mint info_, that it supports `onchain`: ```
robwoodgate (Migrated from github.com) approved these changes 2026-05-11 17:36:58 +00:00
robwoodgate (Migrated from github.com) left a comment

ACK @ 700cf7934ad96359c85b749e23fb1a96b0fbfd17. LGTM!

ACK @ 700cf7934ad96359c85b749e23fb1a96b0fbfd17. LGTM!
KvngMikey (Migrated from github.com) approved these changes 2026-05-15 17:05:23 +00:00
thesimplekid commented 2026-05-18 15:02:17 +00:00 (Migrated from github.com)
@robwoodgate reword prefer async as we discussed https://github.com/cashubtc/nuts/pull/365/commits/cd2132b4bd5adf7b7bd1334847ddd0d4f5bd0966
robwoodgate commented 2026-05-18 19:07:23 +00:00 (Migrated from github.com)

@robwoodgate reword prefer async as we discussed cd2132b

Looks great to me - ACK

> @robwoodgate reword prefer async as we discussed [cd2132b](https://github.com/cashubtc/nuts/commit/cd2132b4bd5adf7b7bd1334847ddd0d4f5bd0966) Looks great to me - ACK
TheMhv (Migrated from github.com) requested changes 2026-05-19 13:00:44 +00:00
TheMhv (Migrated from github.com) requested changes 2026-05-19 13:36:51 +00:00
TheMhv (Migrated from github.com) left a comment

We need to define all fields on request/response structs, even if is optional

We need to define all fields on request/response structs, even if is optional
TheMhv commented 2026-05-19 14:42:42 +00:00 (Migrated from github.com)
ACK 53a7fea28ef8b21a1c26aa5fa952aa246654ba4d
callebtc (Migrated from github.com) requested changes 2026-05-20 15:29:09 +00:00
@ -0,0 +1,408 @@
# NUT-30: Payment Method: Onchain
callebtc (Migrated from github.com) commented 2026-05-20 15:28:41 +00:00
Each item in `fee_options` represents one available fee reserve and confirmation estimate for the same payment. The wallet selects one of these options when executing the melt quote by including the option's `fee_index` value in the melt request. The mint **MUST** return at least one `fee_options` item. The returned `fee_options` are fixed for the lifetime of the quote.
```suggestion Each item in `fee_options` represents one available fee reserve and confirmation estimate for the same payment. The wallet selects one of these options when executing the melt quote by including the option's `fee_index` value in the melt request. The mint **MUST** return at least one `fee_options` item. The returned `fee_options` are fixed for the lifetime of the quote. ```
@ -0,0 +196,4 @@
"fee_index": <int>,
"fee_reserve": <int>,
"estimated_blocks": <int>
}
callebtc (Migrated from github.com) commented 2026-05-20 15:26:42 +00:00
    {
      "fee_index": <int>,
      "fee_reserve": <int>,
      "estimated_blocks": <int>
    }
```suggestion { "fee_index": <int>, "fee_reserve": <int>, "estimated_blocks": <int> } ```
callebtc (Migrated from github.com) requested changes 2026-05-20 16:02:03 +00:00
@ -0,0 +1,408 @@
# NUT-30: Payment Method: Onchain
callebtc (Migrated from github.com) commented 2026-05-20 15:49:19 +00:00
For each fee option with `fee_index`, `fee_reserve` is the maximum onchain transaction fee the mint may charge for that option, and `estimated_blocks` is the estimated number of blocks until confirmation. `selected_fee_index` is `null` before the quote is executed and is set by the mint to the selected fee option once the wallet executes the quote. The mint expects the wallet to include `Proofs` of _at least_ `total_amount = amount + selected_fee_reserve + input_fee` where `selected_fee_reserve` is the `fee_reserve` from the selected `fee_index` item and `input_fee` is calculated from the keyset's `input_fee_ppk` as described in [NUT-02][02]. If the mint does not claim the full `selected_fee_reserve` as the actual fee, the mint returns the unclaimed amount as `change` to the wallet as described in [NUT-08][08].
```suggestion For each fee option with `fee_index`, `fee_reserve` is the maximum onchain transaction fee the mint may charge for that option, and `estimated_blocks` is the estimated number of blocks until confirmation. `selected_fee_index` is `null` before the quote is executed and is set by the mint to the selected fee option once the wallet executes the quote. The mint expects the wallet to include `Proofs` of _at least_ `total_amount = amount + selected_fee_reserve + input_fee` where `selected_fee_reserve` is the `fee_reserve` from the selected `fee_index` item and `input_fee` is calculated from the keyset's `input_fee_ppk` as described in [NUT-02][02]. If the mint does not claim the full `selected_fee_reserve` as the actual fee, the mint returns the unclaimed amount as `change` to the wallet as described in [NUT-08][08]. ```
callebtc (Migrated from github.com) commented 2026-05-20 15:52:24 +00:00
  "fee_index": <int>,
```suggestion "fee_index": <int>, ```
callebtc (Migrated from github.com) commented 2026-05-20 15:55:04 +00:00
Where `fee_index` is the selected fee of the quote's `fee_options`. The mint **MUST** reject a melt request with a `fee_index` that was not returned in the quote. Once `selected_fee_index` is set, the mint **MUST NOT** execute the quote again with a different `fee_index` value.
```suggestion Where `fee_index` is the selected fee of the quote's `fee_options`. The mint **MUST** reject a melt request with a `fee_index` that was not returned in the quote. Once `selected_fee_index` is set, the mint **MUST NOT** execute the quote again with a different `fee_index` value. ```
callebtc (Migrated from github.com) commented 2026-05-20 15:56:22 +00:00
  "selected_fee_index": <int>,
```suggestion "selected_fee_index": <int>, ```
callebtc (Migrated from github.com) commented 2026-05-20 15:56:32 +00:00
  "selected_fee_index": null,
```suggestion "selected_fee_index": null, ```
callebtc (Migrated from github.com) commented 2026-05-20 15:56:47 +00:00
  "selected_fee_index": null,
```suggestion "selected_fee_index": null, ```
callebtc (Migrated from github.com) commented 2026-05-20 15:57:05 +00:00
  "selected_fee_index": 1,
```suggestion "selected_fee_index": 1, ```
callebtc (Migrated from github.com) commented 2026-05-20 15:58:00 +00:00
  "selected_fee_index": 1,
```suggestion "selected_fee_index": 1, ```
callebtc (Migrated from github.com) commented 2026-05-20 15:58:44 +00:00
The wallet selects one of the returned `fee_options` by including that option's `fee_index` value in the melt request. Once the quote is executed, quote state checks return the same response shape with `selected_fee_index` set to the selected value and `outpoint` set once the transaction has been broadcast.
```suggestion The wallet selects one of the returned `fee_options` by including that option's `fee_index` value in the melt request. Once the quote is executed, quote state checks return the same response shape with `selected_fee_index` set to the selected value and `outpoint` set once the transaction has been broadcast. ```
callebtc (Migrated from github.com) commented 2026-05-20 15:59:35 +00:00
Where `fee_reserve` is the maximum onchain transaction fee the mint may charge for that option, and `estimated_blocks` is the estimated number of blocks until confirmation. `selected_fee_option` is `null` before the quote is executed and is set to the selected `fee_index` value once the wallet executes the quote. The mint expects the wallet to include `Proofs` of _at least_ `total_amount = amount + selected_fee_reserve + input_fee` where `selected_fee_reserve` is the `fee_reserve` from the selected `fee_options` item and `input_fee` is calculated from the keyset's `input_fee_ppk` as described in [NUT-02][02]. If the mint does not claim the full `selected_fee_reserve` as the actual fee, the mint returns the unclaimed amount as `change` to the wallet as described in [NUT-08][08].
```suggestion Where `fee_reserve` is the maximum onchain transaction fee the mint may charge for that option, and `estimated_blocks` is the estimated number of blocks until confirmation. `selected_fee_option` is `null` before the quote is executed and is set to the selected `fee_index` value once the wallet executes the quote. The mint expects the wallet to include `Proofs` of _at least_ `total_amount = amount + selected_fee_reserve + input_fee` where `selected_fee_reserve` is the `fee_reserve` from the selected `fee_options` item and `input_fee` is calculated from the keyset's `input_fee_ppk` as described in [NUT-02][02]. If the mint does not claim the full `selected_fee_reserve` as the actual fee, the mint returns the unclaimed amount as `change` to the wallet as described in [NUT-08][08]. ```
@ -0,0 +247,4 @@
"fee_index": <int>,
"fee_reserve": <int>,
"estimated_blocks": <int>
}
callebtc (Migrated from github.com) commented 2026-05-20 15:55:39 +00:00
    {
      "fee_index": <int>,
      "fee_reserve": <int>,
      "estimated_blocks": <int>
    }
```suggestion { "fee_index": <int>, "fee_reserve": <int>, "estimated_blocks": <int> } ```
@ -0,0 +342,4 @@
"fee_index": 2,
"fee_reserve": 800,
"estimated_blocks": 144
}
callebtc (Migrated from github.com) commented 2026-05-20 15:57:41 +00:00
    {
      "fee_index": 0,
      "fee_reserve": 5000,
      "estimated_blocks": 1
    },
    {
      "fee_index": 1,    
      "fee_reserve": 2000,
      "estimated_blocks": 6
    },
    {
      "fee_index": 2,    
      "fee_reserve": 800,
      "estimated_blocks": 144
    }
```suggestion { "fee_index": 0, "fee_reserve": 5000, "estimated_blocks": 1 }, { "fee_index": 1, "fee_reserve": 2000, "estimated_blocks": 6 }, { "fee_index": 2, "fee_reserve": 800, "estimated_blocks": 144 } ```
@ -0,0 +376,4 @@
"fee_index": 2,
"fee_reserve": 800,
"estimated_blocks": 144
}
callebtc (Migrated from github.com) commented 2026-05-20 15:58:18 +00:00
    {
      "fee_index": 0,    
      "fee_reserve": 5000,
      "estimated_blocks": 1
    },
    {
      "fee_index": 1,    
      "fee_reserve": 2000,
      "estimated_blocks": 6
    },
    {
      "fee_index": 2,    
      "fee_reserve": 800,
      "estimated_blocks": 144
    }
```suggestion { "fee_index": 0, "fee_reserve": 5000, "estimated_blocks": 1 }, { "fee_index": 1, "fee_reserve": 2000, "estimated_blocks": 6 }, { "fee_index": 2, "fee_reserve": 800, "estimated_blocks": 144 } ```
Sign in to join this conversation.
No description provided.