Skip to content

Commit

Permalink
feat: mint onchain with NUT-19
Browse files Browse the repository at this point in the history
  • Loading branch information
thesimplekid committed Nov 21, 2024
1 parent 642e04f commit 05e7ae7
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 31 deletions.
69 changes: 46 additions & 23 deletions 22.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
NUT-18: Mint tokens Bitcoin On-Chain
NUT-22: Mint tokens Bitcoin On-Chain
==========================

`optional`

`depends on: [NUT-19][19]`
---

This NUT describes the process of minting tokens on Bitcoin On-Chain analog to NUT-04 for Bitcoin Lightning.
This NUT describes the process of minting tokens on Bitcoin On-Chain analog to [NUT-04][04] for Bitcoin Lightning.

# Mint quote

Expand All @@ -20,7 +21,8 @@ The wallet of `Alice` includes the following `PostMintQuoteBtcOnchainRequest` da
```json
{
"amount": <int>,
"unit": <str_enum["sat"]>
"unit": <str_enum["sat"]>,
"pubkey": <str>
}
```
with the requested `amount` and the `unit`.
Expand All @@ -31,41 +33,41 @@ The wallet of `Alice` includes the following `PostMintQuoteBtcOnchainRequest` da
{
"quote": <str>,
"address": <str>,
"state": <str_enum[STATE]>,
"expiry": <int>
"expiry": <int>,
"unconfirmed_amount", <int>,
"amount_paid": <int>,
"amount_issued": <int>,
"pubkey": <str>
}
```

Where `quote` is the quote ID and `address` is the payment request to fulfill.

`state` is an enum string field with possible values `"UNPAID"`, `"PAID"`, `"PENDING"`, `"ISSUED"`:
- `"UNPAID"` means that the quote's request has not been paid yet.
- `"PAID"` means that the request has been paid.
- `"PENDING"` means that the quote is currently being issued.
- `"ISSUED"` means that the quote has already been issued.
Where `quote` is the quote ID and `request` is the bitcoin onchain address. `expiry` is the Unix timestamp until which the mint quote is valid. `unconfirmed_amount` is the value of UTXOs in sats that has not met the minimum confirmations set in the info endpoint. `amount_paid` is the amount that has been paid to the mint via the onchain address. `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 tokens that this operation mints.
**Note**: Any UTXOs with a value less than the `min_amount` in the info response will not be added to the `amount_paid` value and will be considered dust. Ecash cannot be minted for these UTXOs.

## Example

Request of `Alice` with curl:

```bash
curl -X POST https://mint.host:3338/v1/mint/quote/btconchain -d '{"amount": 10, "unit": "sat"}' -H "Content-Type: application/json"
curl -X POST https://mint.host:3338/v1/mint/quote/btconchain -d '{"amount": 10, "unit": "sat", "pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac"}' -H "Content-Type: application/json"
```

Response of `Bob`:

```json
{
"quote": "DSGLX9kevM...",
"address": "bc1qkyfgd7mus7ykfd7qkwakq75qsf7rtm...",
"state": "UNPAID",
"expiry": 1701704757
"request": "bc1q...",
"expiry": 1701704757,
"amount_paid": 0,
"amount_issued": 0,
"pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac"
}
```

The wallet **MUST** store the `amount` in the request and the `quote` id in the response in its database so it can later request the tokens after paying the request. After payment, the wallet continues with the next section.
The wallet **MUST** store the `quote` id in the response in its database so it can later request the tokens after paying the request. After payment, the wallet continues with the next section.

## Check mint quote state

Expand All @@ -83,6 +85,23 @@ Example request of `Alice` with curl:
curl -X GET https://mint.host:3338/v1/mint/quote/btconchain/DSGLX9kevM...
```

Response of `Bob`:

```json
{
"quote": "DSGLX9kevM...",
"request": "bc1q...",
"expiry": 1701704757,
"amount_paid": 0,
"amount_issued": 0,
"pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac"
}
```

### Witness

In order to mint ecash the wallet **MUST** include a signature as defined in [NUT-19][19].

# Minting tokens

After requesting a mint quote and paying the request, the wallet proceeds with minting new tokens by calling the `POST /v1/mint/{method}` endpoint where `method` is the payment method requested (here `btconchain`).
Expand All @@ -96,10 +115,12 @@ The wallet `Alice` includes the following `PostMintBtcOnchainRequest` data in it
```json
{
"quote": <str>,
"outputs": <Array[BlindedMessage]>
"outputs": <Array[BlindedMessage]>,
"witness": <str>
}
```
with the `quote` being the quote ID from the previous step and `outputs` being `BlindedMessages` (see [NUT-00][00]) that the wallet requests signatures on whose sum is `amount` as requested in the quote.

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`). `witness` is the signature on the mint quote id as defined above.

The mint `Bob` then responds with a `PostMintBtcOnchainResponse`:

Expand Down Expand Up @@ -130,7 +151,8 @@ curl -X POST https://mint.host:3338/v1/mint/btconchain -H "Content-Type: applica
"id": "009a1f293253e41e",
"B_": "0288d7649652d0a83fc9c966c969fb217f15904431e61a44b14999fabc1b5d9ac6"
}
]
],
"witness": "d4b386f21f7aa7172f0994ee6e4dd966539484247ea71c99b81b8e09b1bb2acbc0026a43c221fd773471dc30d6a32b04692e6837ddaccf0830a63128308e4ee0"
}'
```

Expand Down Expand Up @@ -178,8 +200,7 @@ Upon receiving the `BlindSignatures` from the mint `Bob`, the wallet of `Alice`
The settings for this nut indicate the supported method-unit pairs for minting and whether minting is supported or not. They are part of the info response of the mint ([NUT-06][06]) which in this case reads
```json
{
"18": {
"supported": true,
"22": {
"methods": [
{
"method": "btconchain",
Expand All @@ -188,7 +209,8 @@ The settings for this nut indicate the supported method-unit pairs for minting a
"max_amount": 1000000,
"min_confirmations": 3
}
]
],
"disabled": false,
}
}
```
Expand All @@ -206,3 +228,4 @@ The settings for this nut indicate the supported method-unit pairs for minting a
[10]: 10.md
[11]: 11.md
[12]: 12.md
[19]: 19.md
26 changes: 19 additions & 7 deletions 23.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
NUT-19: Melt tokens Bitcoin On-Chain
NUT-23: Melt tokens Bitcoin On-Chain
==========================

`optional`
Expand Down Expand Up @@ -34,15 +34,24 @@ The mint `Bob` then responds with an array of `PostMeltQuoteBtcOnchainResponse`:
[
{
"quote": <str>,
"description": <str|null>,
"amount": <int>,
"fee": <int>,
"estimated_blocks": <int>,
"state": <str_enum[STATE]>,
"expiry": <int>
}
]
```
The mint can return multiple `PostMeltQuoteBtcOnchainResponse` with different `fees` and `expiry` dates. The wallet can choose which one to pay and the other ones will expire. Where `quote` is the quote ID, `amount` the amount that needs to be provided, and `fee` the additional fee that is required. The mint expects `Alice` to include `Proofs` of *at least* `total_amount = amount + fee`. `paid` indicates whether the request as been paid and `expiry` is the Unix timestamp until which the melt quote is valid.
The mint can return multiple `PostMeltQuoteBtcOnchainResponse` with different `fees` and `expiry` dates. The wallet can choose which one to pay and the other ones will expire. Where `quote` is the quote ID, `amount` the amount that needs to be provided, and `fee` the additional fee that is required in the unit of the `PostMeltQuoteBtcOnchainRequest`. `estimated_blocks` is the number of blocks estimated untill confimation. The mint expects `Alice` to include `Proofs` of *at least* `total_amount = amount + fee`. `expiry` is the Unix timestamp until which the melt quote is valid.


`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

Expand All @@ -53,6 +62,7 @@ curl -X POST https://mint.host:3338/v1/melt/quote/btconchain -d \
{
"address": "bc1qkyfgd7mus7ykfd7qkwakq75qsf7rtm...",
"unit": "sat"
"amount": 100000
}
```

Expand All @@ -64,15 +74,17 @@ Response of `Bob`:
"quote": "TRmjduhIsPxd...",
"description": "1 sat per vbyte",
"amount": 10,
"fee": 2,
"fee": 3000,
"estimated_blocks": 1,
"state": "UNPAID",
"expiry": 1701704757
},
{
"quote": "OewtRaqe...",
"description": "5 sat per vbyte",
"amount": 10,
"fee": 10,
"fee": 300,
"estimated_blocks": 50,
"state": "UNPAID",
"expiry": 1701704757
}
Expand Down Expand Up @@ -171,8 +183,7 @@ Response of `Bob`:
The 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": {
"supported": true,
"23": {
"methods": [
{
"method": "btconchain",
Expand All @@ -181,6 +192,7 @@ The settings for this nut indicate the supported method-unit pairs for melting.
"max_amount": 1000000
}
]
"disabled": false,
}
}
```
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ Wallets and mints `MUST` implement all mandatory specs and `CAN` implement optio
| [16][16] | Animated QR codes | [Cashu.me][cashume] | - |
| [17][17] | WebSocket subscriptions | [Nutshell][py] | [Nutshell][py] |
| [18][18] | Payment requests | [Cashu.me][cashume], [Boardwalk][bwc], [cdk-cli] | - |

| [22][22] | Minting onchain | | |
| [23][23] | Melting onchain | | |

#### Wallets:

- [Nutshell][py]
Expand Down

0 comments on commit 05e7ae7

Please sign in to comment.