forked from cashubtc/nuts
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f866a25
commit 3d2f85e
Showing
3 changed files
with
376 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
# NUT-19: Mint tokens via BOLT12 | ||
|
||
`optional` | ||
|
||
--- | ||
|
||
Similar to [NUT-04][04], which covers minting via BOLT11 invoices, minting via [BOLT12](https://github.com/lightning/bolts/blob/master/12-offer-encoding.md) is also a two-step process: requesting a mint quote and minting tokens. This document describes both steps, focusing on BOLT12-specific considerations. | ||
|
||
# Mint quote | ||
|
||
To request a mint quote, the wallet of `Alice` makes a `POST /v1/mint/quote/bolt12`. | ||
|
||
```http | ||
POST https://mint.host:3338/v1/mint/quote/bolt12 | ||
``` | ||
|
||
The wallet of `Alice` includes the following `PostMintQuoteBolt12Request` data in its request: | ||
|
||
```json | ||
{ | ||
"amount": <int|null>, | ||
"unit": <str_enum["sat"]>, | ||
"description": <str|null>, | ||
"expiry": <int|null>, | ||
"single_use": <bool> | ||
} | ||
``` | ||
|
||
The `amount` field is optional and specifies the amount to mint. The `unit` field is mandatory. An optional `description` can be passed if the mint signals support for it in `MintMethodSetting`. If `single_use` is false, the offer can be paid multiple times. The optional `expiry` field specifies the unix timestamp when the offer expires it **MUST** be before the `max_expiry` in the `MintMethodSettings` if one is given. | ||
|
||
The mint `Bob` then responds with a `PostMintQuoteBolt12Response`: | ||
|
||
```json | ||
{ | ||
"quote": <str>, | ||
"request": <str>, | ||
"expiry": <int>, | ||
"amount_paid": <int>, | ||
"amount_issued": <int> | ||
} | ||
``` | ||
|
||
Where `quote` is the quote ID and `request` is the bolt12 offer. `expiry` is the Unix timestamp until which the mint quote is valid. `amount_paid` is the amount that has been paid to the mint via the bolt12 offer. `amount_issued` is the amount of ecash that has been issued for the given mint quote. `amount_paid` - `amount_issued` represents the amount of ecash a wallet can still mint. | ||
|
||
Note: `quote` is a **unique and random** id generated by the mint to internally look up the payment state. `quote` **MUST** remain a secret between user and mint and **MUST NOT** be derivable from the payment request. A third party who knows the `quote` ID can front-run and steal the ecash that this operation mints. | ||
|
||
## Example | ||
|
||
Request of `Alice` with curl: | ||
|
||
```bash | ||
curl -X POST http://localhost:3338/v1/mint/quote/bolt12 -d '{"amount": 10, "unit": "sat", "single_use": true}' -H "Content-Type: application/json" | ||
``` | ||
|
||
Response of `Bob`: | ||
|
||
```json | ||
{ | ||
"quote": "DSGLX9kevM...", | ||
"request": "lno1qcp...", | ||
"expiry": 1701704757, | ||
"amount_paid": 0, | ||
"amount_issued": 0 | ||
} | ||
``` | ||
|
||
After payment, the wallet continues with the next section. | ||
|
||
## Check mint quote | ||
|
||
To check whether a mint quote has been paid and has ecash that can be issued, `Alice` makes a `GET /v1/mint/quote/bolt12/{quote_id}`. | ||
|
||
```http | ||
GET https://mint.host:3338/v1/mint/quote/bolt12/{quote_id} | ||
``` | ||
|
||
The mint `Bob` responds with a `PostMintQuoteBolt12Response`. | ||
|
||
Example request of `Alice` with curl: | ||
|
||
```bash | ||
curl -X GET http://localhost:3338/v1/mint/quote/bolt12/DSGLX9kevM... | ||
``` | ||
|
||
# Minting tokens | ||
|
||
After requesting a mint quote and paying the request, the wallet proceeds with minting new tokens by calling `POST /v1/mint/bolt12`. | ||
|
||
```http | ||
POST https://mint.host:3338/v1/mint/bolt12 | ||
``` | ||
|
||
The wallet `Alice` includes the following `PostMintBolt12Request` data in its request: | ||
|
||
```json | ||
{ | ||
"quote": <str>, | ||
"outputs": <Array[BlindedMessage]> | ||
} | ||
``` | ||
|
||
The `quote` is the quote ID from the previous step and `outputs` are `BlindedMessages` (see [NUT-00][00]) that the wallet requests signatures on. The sum of the outputs must equal the amount that can be minted (`amount_paid` - `amount_issued`). | ||
|
||
The mint `Bob` then responds with a `PostMintBolt12Response`: | ||
|
||
```json | ||
{ | ||
"signatures": <Array[BlindSignature]> | ||
} | ||
``` | ||
|
||
where `signatures` is an array of blind signatures on the outputs. | ||
|
||
## Multiple issuances | ||
|
||
Unlike BOLT11 invoices, BOLT12 offers can be paid multiple times as long as the `single_use` flag is false. This allows the wallet to mint multiple times for one quote. The wallet can call the check bolt12 endpoint, where the mint will return the `PostMintQuoteBolt12Response` including `amount_paid` and `amount_issued`. The difference between these values represents how much the wallet can mint by calling the mint endpoint as defined above. | ||
|
||
## Settings | ||
|
||
The settings for this nut indicate the supported method-unit pairs for minting and whether minting is disabled or not. They are part of the info response of the mint ([NUT-06][06]) which in this case reads: | ||
|
||
```json | ||
{ | ||
"19": { | ||
"methods": [ | ||
<MintMethodSetting>, | ||
... | ||
], | ||
"disabled": <bool> | ||
} | ||
} | ||
``` | ||
|
||
`MintMethodSetting` indicates supported `method` and `unit` pairs and additional settings of the mint. `disabled` indicates whether this minting is disabled. | ||
|
||
`MintMethodSetting` is of the form: | ||
|
||
```json | ||
{ | ||
"method": <str>, | ||
"unit": <str>, | ||
"min_amount": <int|null>, | ||
"max_amount": <int|null>, | ||
"description": <bool|null>, | ||
"max_expiry": <int|null> | ||
} | ||
``` | ||
|
||
`min_amount` and `max_amount` define the allowed range for transactions using this method-unit pair. `max_expiry` specifies the latest timestamp (in Unix time) until which payments for this offer will be accepted. | ||
|
||
|
||
Example `MintMethodSetting`: | ||
|
||
```json | ||
{ | ||
"method": "bolt12", | ||
"unit": "sat", | ||
"min_amount": 0, | ||
"max_amount": 10000, | ||
"description": true, | ||
"max_expiry": 1825076378 | ||
} | ||
``` | ||
|
||
[00]: 00.md | ||
[01]: 01.md | ||
[02]: 02.md | ||
[03]: 03.md | ||
[04]: 04.md | ||
[05]: 05.md | ||
[06]: 06.md | ||
[07]: 07.md | ||
[08]: 08.md | ||
[09]: 09.md | ||
[10]: 10.md | ||
[11]: 11.md | ||
[12]: 12.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
# NUT-19: Melt tokens via BOLT12 | ||
|
||
`optional` | ||
|
||
Similar to [NUT-05][05], which covers melting via BOLT11 invoices, melting via [BOLT12](https://github.com/lightning/bolts/blob/master/12-offer-encoding.md) is also a two-step process: requesting a melt quote and melting tokens. This document describes both steps, focusing on BOLT12-specific considerations. | ||
|
||
# Melt quote | ||
|
||
To request a melt quote, the wallet of `Alice` makes a `POST /v1/melt/quote/bolt12`.. | ||
|
||
```http | ||
POST https://mint.host:3338/v1/melt/quote/bolt12 | ||
``` | ||
|
||
The wallet `Alice` includes the following `PostMeltQuoteBolt12Request` data in its request: | ||
|
||
```json | ||
{ | ||
"request": <str>, | ||
"unit": <str_enum["sat"]> | ||
"amount": <int|null> | ||
} | ||
``` | ||
|
||
Here, `request` is the bolt12 Lightning invoice to be paid and `unit` is the unit the wallet would like to pay with. `amount` is the amount the wallet would like to pay in the request unit. This `amount` can be omitted if the offer has an amount. | ||
|
||
The mint `Bob` then responds with a `PostMeltQuoteBolt12Response`: | ||
|
||
```json | ||
{ | ||
"quote": <str>, | ||
"amount": <int>, | ||
"fee_reserve": <int>, | ||
"state": <str_enum[STATE]>, | ||
"expiry": <int>, | ||
"payment_preimage": <str|null> | ||
} | ||
``` | ||
|
||
Where `quote` is the quote ID, `amount` the amount that needs to be provided, and `fee_reserve` the additional fee reserve that is required. The mint expects `Alice` to include `Proofs` of _at least_ `total_amount = amount + fee_reserve`. `expiry` is the Unix timestamp until which the melt quote is valid. `payment_preimage` is the payment preimage in case of a successful payment. | ||
|
||
`state` is an enum string field with possible values `"UNPAID"`, `"PENDING"`, `"PAID"`: | ||
|
||
- `"UNPAID"` means that the request has not been paid yet. | ||
- `"PENDING"` means that the request is currently being paid. | ||
- `"PAID"` means that the request has been paid successfully. | ||
|
||
## Example | ||
|
||
Request of `Alice` with curl: | ||
|
||
```bash | ||
curl -X POST https://mint.host:3338/v1/melt/quote/bolt12 -d \ | ||
{ | ||
"request": "lno1zrxq8pjw7qjlm68mtp7e3y...", | ||
"unit": "sat" | ||
} | ||
``` | ||
|
||
Response of `Bob`: | ||
|
||
```json | ||
{ | ||
"quote": "TRmjduhIsPxd...", | ||
"amount": 10, | ||
"fee_reserve": 2, | ||
"state": "UNPAID", | ||
"expiry": 1701704757 | ||
} | ||
``` | ||
|
||
## Check melt quote state | ||
|
||
To check whether a melt quote has been paid, `Alice` makes a `GET /v1/melt/quote/bolt12/{quote_id}`. | ||
|
||
```http | ||
GET https://mint.host:3338/v1/melt/quote/bolt12/{quote_id} | ||
``` | ||
|
||
Like before, the mint `Bob` responds with a `PostMeltQuoteBolt12Response`. | ||
|
||
Example request of `Alice` with curl: | ||
|
||
```bash | ||
curl -X GET http://localhost:3338/v1/melt/quote/bolt12/TRmjduhIsPxd... | ||
``` | ||
|
||
# Melting tokens | ||
|
||
Now that `Alice` knows what the total amount is (`amount + fee_reserve`) in her requested `unit`, she can proceed to melting tokens for which a payment will be executed by the mint. She calls the `POST /v1/melt/bolt12`. | ||
|
||
```http | ||
POST https://mint.host:3338/v1/melt/bolt12 | ||
``` | ||
|
||
⚠️ **Attention:** This call will block until the Lightning payment either succeeds or fails. This can take quite a long time in case the Lightning payment is slow. Make sure to **use no (or a very long) timeout when making this call**! | ||
|
||
The wallet of `Alice` includes the following `PostMeltBolt12Request` data in its request | ||
|
||
```json | ||
{ | ||
"quote": <str>, | ||
"inputs": <Array[Proof]> | ||
} | ||
``` | ||
|
||
Here, `quote` is the melt quote ID to be paid and `inputs` are the proofs with a total amount of at least `amount + fee_reserve` (see previous melt quote response). | ||
|
||
Like before, the mint `Bob` then responds with a `PostMeltQuoteBolt12Response`. If the payment was successful, the `state` field is set to `"PAID"` and the response includes the `payment_preimage` field containing the payment secret of the bolt11 payment. | ||
|
||
If `state=="PAID"`, `Alice`'s wallet can delete the `inputs` from her database (or move them to a history). If `state=="UNPAID"`, `Alice` can repeat the same request again until the payment is successful. | ||
|
||
## Example | ||
|
||
Request of `Alice` with curl: | ||
|
||
```bash | ||
curl -X POST https://mint.host:3338/v1/melt/bolt12 -d \ | ||
'{ | ||
"quote": "od4CN5smMMS3K3QVHkbGGNCTxfcAIyIXeq8IrfhP", | ||
"inputs": [ | ||
{ | ||
"amount": 4, | ||
"id": "009a1f293253e41e", | ||
"secret": "429700b812a58436be2629af8731a31a37fce54dbf8cbbe90b3f8553179d23f5", | ||
"C": "03b01869f528337e161a6768b480fcf9f75fd248b649c382f5e352489fd84fd011", | ||
}, | ||
{ | ||
"amount": 8, | ||
"id": "009a1f293253e41e", | ||
"secret": "4f3155acef6481108fcf354f6d06e504ce8b441e617d30c88924991298cdbcad", | ||
"C": "0278ab1c1af35487a5ea903b693e96447b2034d0fd6bac529e753097743bf73ca9", | ||
} | ||
] | ||
}' | ||
``` | ||
|
||
Response `PostMeltQuoteBolt12Response` of `Bob`: | ||
|
||
```json | ||
{ | ||
"quote": "TRmjduhIsPxd...", | ||
"amount": 10, | ||
"fee_reserve": 2, | ||
"state": "PAID", | ||
"expiry": 1701704757, | ||
"payment_preimage": "c5a1ae1f639e1f4a3872e81500fd028bece7bedc1152f740cba5c3417b748c1b" | ||
} | ||
``` | ||
|
||
## Settings | ||
|
||
The mint's settings for this nut indicate the supported method-unit pairs for melting. They are part of the info response of the mint ([NUT-06][06]) which in this case reads | ||
|
||
```json | ||
{ | ||
"19": { | ||
"methods": [ | ||
<MeltMethodSetting>, | ||
... | ||
], | ||
"disabled": <bool> | ||
} | ||
} | ||
``` | ||
|
||
`MeltMethodSetting` indicates supported `method` and `unit` pairs and additional settings of the mint. `disabled` indicates whether melting is disabled. | ||
|
||
`MeltMethodSetting` is of the form: | ||
|
||
```json | ||
{ | ||
"method": <str>, | ||
"unit": <str>, | ||
"min_amount": <int|null>, | ||
"max_amount": <int|null> | ||
} | ||
``` | ||
|
||
`min_amount` and `max_amount` indicate the minimum and maximum amount for an operation of this method-unit pair. | ||
|
||
Example `MeltMethodSetting`: | ||
|
||
```json | ||
{ | ||
"method": "bolt12", | ||
"unit": "sat", | ||
"min_amount": 100, | ||
"max_amount": 10000 | ||
} | ||
``` | ||
|
||
[05]: 05.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters