Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
30ee84d
feat: add preferred_mints field to payment requests
a1denvalu3 Mar 26, 2026
033c19a
prettier
a1denvalu3 Mar 26, 2026
a2799e2
fix(nut-26): rename preferred_mint to preferred_mints
a1denvalu3 Mar 26, 2026
b136d5b
prettier
a1denvalu3 Mar 26, 2026
2e4a57b
fix(nut-18): clarify mints vs preferred_mints usage
a1denvalu3 Mar 26, 2026
6a1aa4c
Update 18.md
a1denvalu3 Mar 28, 2026
5fd8dbd
fix(nut-18, nut-26): make m and pm mutually exclusive, add test vectors
a1denvalu3 Apr 16, 2026
2287835
feat(nut-18, nut-26): add supported_methods field to payment requests
a1denvalu3 May 20, 2026
61aeddf
feat: preferred mints
d4rp4t May 26, 2026
a884687
feat: add mint_strict to nut28
d4rp4t May 26, 2026
257fcee
fix: prettier
d4rp4t May 26, 2026
69a46cf
feat: add fee reserve to payment requests for non-strict mint list
robwoodgate May 27, 2026
389d4a8
chore: put onus on sender
robwoodgate May 27, 2026
49f8cda
chore: change may to should ignore other mints
robwoodgate Jun 25, 2026
8e367e3
flip ms (mint_strict) to mp (mint_preferred)
robwoodgate Jun 26, 2026
faedcdb
format
robwoodgate Jun 26, 2026
6ea4e3a
fix creqA test
robwoodgate Jun 26, 2026
286800d
change fee_reserve to fee_required
robwoodgate Jun 30, 2026
13e7cc8
rename to supported_method in nut26
robwoodgate Jun 30, 2026
46f6f6e
feat: per-method fee (mn/mf) on supported_methods
robwoodgate Jul 1, 2026
68bcf8f
docs(nut-18): drop external CBOR viewer link from example
robwoodgate Jul 1, 2026
b678e1c
refactor(nut18): drop fr (fee_required), add nf (net_fees). clarify m…
robwoodgate Jul 6, 2026
7ff7864
refactor(nut18): drop nf, requested amount is always net of input fees
robwoodgate Jul 8, 2026
27207ff
docs(nut26): state that u64 fields are fixed 8-byte big-endian
robwoodgate Jul 8, 2026
51bb42b
fix: specify sm relates to MELTING methods
robwoodgate Jul 8, 2026
93b463d
fix(nut18): bind sm melt support to the request unit
robwoodgate Jul 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions 18.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ A Payment Request is defined as follows
"u": str <optional>,
"s": bool <optional>,
"m": Array[str] <optional>,
"mp": bool <optional>,
"sm": Array[SupportedMethod] <optional>,
"d": str <optional>,
"t": Array[Transport] <optional>,
"nut10": NUT10Option <optional>,
Expand All @@ -33,14 +35,52 @@ A Payment Request is defined as follows
Here, the fields are

- `i`: Payment id to be included in the payment payload
- `a`: The amount of the requested payment
- `a`: The amount of the requested payment, net of input fees (see [Input fees](#input-fees))
- `u`: The unit of the requested payment (MUST be set if `a` is set)
- `s`: Whether the payment request is for single use
- `m`: A set of mints from which the payment is requested
- `m`: The mint list from which payment will be accepted
- `mp`: Whether the mint list is advisory (`true`) or strict (`false` or omitted)
- `sm`: A list of `SupportedMethod` entries (e.g. `"bolt11"`, `"bolt12"`, `"onchain"`) that the payee will accept, each with an optional per-method fee
- `d`: A human readable description that the sending wallet will display after scanning the request
- `t`: The method of `Transport` chosen to transmit the payment (can be multiple, sorted by preference)
- `nut10`: The required [NUT-10][10] locking condition

## Mint list

A Payment Request containing a mint list `m` describes the mints the receiver accepts payments from or prefers.

If `m` is set and `mp` is not present, or is `false`, the mint list is strict: the sender MUST only send proofs from these mints, and the receiver SHOULD ignore payments sent from other mints.

If `m` is set and `mp` is `true`, the mint list is preferred: the receiver accepts payments from other mints, but the payer SHOULD use a mint in `m` when possible.

If `m` is not set, `mp` SHOULD be ignored.

## Supported payment methods

If `sm` is set, the payer MUST send ecash from a mint that supports melting (see [NUT-05][05]) via at least one of the listed payment methods. Each `SupportedMethod` is defined as:

```json
{
"mn": str,
"mf": int <optional> // omitted = 0
}
```

- `mn`: the method name (e.g. `"bolt11"`, `"bolt12"`, `"onchain"`)
- `mf`: additional fee, in the request unit, that compensates the receiver for melting out via this method

The per-method fee applies only to payments the receiver may need to melt out of: those from a mint outside `m`, or from any mint if `m` is not set. Payments from a mint in `m` carry no per-method fee.

When a fee applies, the payer owes the lowest `mf` among the listed methods their mint supports, and MUST add it to the requested amount. For example, if the request lists `bolt11` (no fee) and `onchain` (`mf` = 50), a mint supporting both owes nothing, while an onchain-only mint owes 50.

## Input fees

Received proofs cost the receiver an input fee when they are later swapped or melted (see [NUT-02][02]). The requested amount is therefore net of input fees: the payer MUST select proofs such that

`sum(proofs) - input_fee(proofs) >= a + mf (where applicable)`

with `input_fee` computed from the sending mint's keyset `input_fee_ppk` values as described in [NUT-02][02]. This protects the receiver from dust proof sets that are expensive to redeem (e.g. 1000 x 1 sat proofs at 250 ppk would cost the receiver 250 sats to swap).

## Locking conditions

The payment request can include _optional_ locking conditions the payee requires from the payer. For example, the payee might require a P2PK-locked token so that they can receive payments offline.
Expand Down Expand Up @@ -146,11 +186,13 @@ This is an example payment request expressed as JSON:
}
```

This payment request serializes to (see [here](https://cbor.nemo157.com/#type=hex&value=a3617482a261694800ffd48b8f5ecf80617081a36161016173784061636331323433356537623834383463336366313835303134393231386166393066373136613532626634613565643334376534386563633133663737333838616358210244538319de485d55bed3b29a642bee5879375ab9e7a620e11e48ba482421f3cfa261694800ad268c4d1f5826617082a3616102617378403133323364336434373037613538616432653233616461346539663166343966356135623461633762373038656230643631663733386634383330376538656561635821023456aa110d84b4ac747aebd82c3b005aca50bf457ebd5737a4414fac3ae7d94da36161016173784035366263626362623763633634303662336661356435376432313734663465666638623434303262313736393236643361353764336333646362623539643537616358210273129c5719e599379a974a626363c333c56cafc0e6d01abe46d5808280789c63616d75687474703a2f2f6c6f63616c686f73743a33333338617563736174)):
This payment request serializes to:

```sh
creqApWF0gaNhdGVub3N0cmFheKlucHJvZmlsZTFxeTI4d3VtbjhnaGo3dW45ZDNzaGp0bnl2OWtoMnVld2Q5aHN6OW1od2RlbjV0ZTB3ZmprY2N0ZTljdXJ4dmVuOWVlaHFjdHJ2NWhzenJ0aHdkZW41dGUwZGVoaHh0bnZkYWtxcWd5ZGFxeTdjdXJrNDM5eWtwdGt5c3Y3dWRoZGh1NjhzdWNtMjk1YWtxZWZkZWhrZjBkNDk1Y3d1bmw1YWeBgmFuYjE3YWloYjdhOTAxNzZhYQphdWNzYXRhbYF4Imh0dHBzOi8vbm9mZWVzLnRlc3RudXQuY2FzaHUuc3BhY2U=
```

[00]: 00.md
[02]: 02.md
[05]: 05.md
[10]: 10.md
35 changes: 25 additions & 10 deletions 26.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,22 @@ The payment request is encoded as a sequence of TLV fields. Each TLV entry consi
- **Length** (2 bytes, big-endian): Length of value in bytes
- **Value** (variable): Field data

Fields of type `u64` are encoded as fixed 8-byte big-endian values.

### Top-Level TLV Tags

| Tag | Field | Type | Description |
| ---- | ----------- | --------- | -------------------------------------------------------------------- |
| 0x01 | id | string | Payment identifier (corresponds to `i` in JSON) |
| 0x02 | amount | u64 | Amount in base units (corresponds to `a` in JSON) |
| 0x03 | unit | u8/string | Currency unit (corresponds to `u` in JSON) |
| 0x04 | single_use | u8 | Single-use flag: 0=false, 1=true (corresponds to `s` in JSON) |
| 0x05 | mint | string | Mint URL (repeatable for multiple mints, corresponds to `m` in JSON) |
| 0x06 | description | string | Human-readable description (corresponds to `d` in JSON) |
| 0x07 | transport | sub-TLV | Transport configuration (repeatable, corresponds to `t` in JSON) |
| 0x08 | nut10 | sub-TLV | NUT-10 spending conditions (corresponds to `nut10` in JSON) |
| Tag | Field | Type | Description |
| ---- | ---------------- | --------- | ------------------------------------------------------------------------------------------------- |
| 0x01 | id | string | Payment identifier (corresponds to `i` in JSON) |
| 0x02 | amount | u64 | Amount in base units (corresponds to `a` in JSON) |
| 0x03 | unit | u8/string | Currency unit (corresponds to `u` in JSON) |
| 0x04 | single_use | u8 | Single-use flag: 0=false, 1=true (corresponds to `s` in JSON) |
| 0x05 | mint | string | Mint URL (repeatable for multiple mints, corresponds to `m` in JSON) |
| 0x06 | description | string | Human-readable description (corresponds to `d` in JSON) |
| 0x07 | transport | sub-TLV | Transport configuration (repeatable, corresponds to `t` in JSON) |
| 0x08 | nut10 | sub-TLV | NUT-10 spending conditions (corresponds to `nut10` in JSON) |
| 0x09 | mint_preferred | u8 | Mint list strictness flag: 0=false, 1=true; defaults to 0 if absent (corresponds to `mp` in JSON) |
| 0x0a | supported_method | sub-TLV | Supported payment method with optional fee (repeatable, corresponds to `sm` in JSON) |

All fields are optional. Unknown tags MUST be ignored to maintain forward compatibility.

Expand Down Expand Up @@ -146,6 +150,17 @@ The following kind values are defined for NUT-10 spending conditions:

Additional kind values may be defined in future NUT specifications. Unknown kind values SHOULD be preserved when re-encoding but MAY be ignored during validation.

### Supported Method Sub-TLV (Tag 0x0a)

Each supported method is encoded as a nested TLV structure. The tag is repeatable, one sub-TLV per method, decoding to the `sm` array in JSON:

| Sub-Tag | Field | Type | Description |
| ------- | ------ | ------ | ----------------------------------------------------------------- |
| 0x01 | method | string | Method name, e.g. `"bolt11"` (corresponds to `mn` in JSON) |
| 0x02 | fee | u64 | Optional per-method fee; absent = 0 (corresponds to `mf` in JSON) |

When the per-method fee applies and how much the payer owes is defined in [NUT-18][18].

## Example

This is an example payment request expressed as JSON:
Expand Down
23 changes: 22 additions & 1 deletion tests/18-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ A basic payment request with required fields.
Encoded (CBOR serialized to base64url):

```
creqApWF0gaNhdGVub3N0cmFheKlucHJvZmlsZTFxeTI4d3VtbjhnaGo3dW45ZDNzaGp0bnl2OWtoMnVld2Q5aHN6OW1od2RlbjV0ZTB3ZmprY2N0ZTljdXJ4dmVuOWVlaHFjdHJ2NWhzenJ0aHdkZW41dGUwZGVoaHh0bnZkYWtxcWd5ZGFxeTdjdXJrNDM5eWtwdGt5c3Y3dWRoZGh1NjhzdWNtMjk1YWtxZWZkZWhrZjBkNDk1Y3d1bmw1YWeBgmFuYjE3YWloYjdhOTAxNzZhYQphdWNzYXRhbYF4Imh0dHBzOi8vbm9mZWVzLnRlc3RudXQuY2FzaHUuc3BhY2U=
creqApWF0gaNhdGVub3N0cmFheKlucHJvZmlsZTFxcXNnbTZxZmEzYzhkdHoyZnZ6aHZmcWVhY213bTBlNTBwZTNrNXRmbXZwamptbjB2ajdtMnRncHozbWh4dWU2OXVoaHlldHZ2OXVqdWVycGQ0Nmh4dG5mZHVxM3dhbW53dmF6N3RtanY0a3h6N2Z3OHFlbnh2ZXd3ZGN4emNtOTl1cXM2YW1ud3Zhejd0bXdkYWVqdW1yMGRzNGxqaDduYWeBgmFuYjE3YWloYjdhOTAxNzZhYQphdWNzYXRhbYF3aHR0cHM6Ly84MzMzLnNwYWNlOjMzMzg
```

### Complete Payment Request
Expand Down Expand Up @@ -157,3 +157,24 @@ Encoded:
```
creqApWFpaGM5ZTQ1ZDJhYWEZAfRhdWNzYXRhbYF4GGh0dHBzOi8vbWludC5leGFtcGxlLmNvbWVudXQxMKNha2RQMlBLYWR4QjAyYzNiNWJiMjdlMzYxNDU3YzkyZDkzZDc4ZGQ3M2QzZDUzNzMyMTEwYjJjZmU4YjUwZmJjMGFiYzYxNWU5YzMzMWF0gYJndGltZW91dGQzNjAw
```

### Preferred Mint List with Supported Methods

A payment request specifying a preferred mint list with `mp` set to `true` and the supported payment methods (`sm`). `bolt12` carries a per-method fee (`mf`) that applies to payments from non-preferred mints.

```json
{
"i": "preferred_fee_methods",
"a": 100,
"u": "sat",
"m": ["https://mint.example.com"],
"mp": true,
"sm": [{ "mn": "bolt11" }, { "mn": "bolt12", "mf": 5 }]
}
```

Encoded:

```
creqApmFpdXByZWZlcnJlZF9mZWVfbWV0aG9kc2FhGGRhdWNzYXRhbYF4GGh0dHBzOi8vbWludC5leGFtcGxlLmNvbWJtcPVic22CoWJtbmZib2x0MTGiYm1uZmJvbHQxMmJtZgU=
```
23 changes: 23 additions & 0 deletions tests/26-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,3 +413,26 @@ Encoded:
```
CREQB1QYQQKCM4WD6X7M2LW4HXJAQZQQYQQQQQQQQQQQRYQVQQXCN5VVZSQXRGW368QUE69UHK66TWWSHX27RPD4CXCEFWVDHK6PZHCW8
```

---

### Preferred Mint List with Supported Methods

A payment request specifying a preferred mint list with `mp` set to `true` (tag 0x09) and the supported payment methods (`sm`, tag 0x0a). `bolt12` carries a per-method fee (`mf`, sub-tag 0x02) that applies to payments from non-preferred mints.

```json
{
"i": "preferred_fee_methods",
"a": 100,
"u": "sat",
"m": ["https://mint.example.com"],
"mp": true,
"sm": [{ "mn": "bolt11" }, { "mn": "bolt12", "mf": 5 }]
}
```

Encoded:

```
CREQB1QYQP2URJV4NX2UNJV4J97EN9V40K6ET5DPHKGUCZQQYQQQQQQQQQQQRYQVQQZQQ9QQVXSAR5WPEN5TE0D45KUAPWV4UXZMTSD3JJUCM0D5YSQQGPPGQQJQGQQE3X7MR5XYCS5QQ5QYQQVCN0D36RZVSZQQYQQQQQQQQQQQQ9FJ2568
```
Loading