-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NUT-XX and NUT-XX+1: Mint / Melt Bitcoin On-Chain #107
base: main
Are you sure you want to change the base?
Changes from all commits
cc4da36
5a1edde
2290606
b4dc153
91f051f
46f9867
bba6d32
1aaf174
2e73249
7b54066
93a57f0
03f4d5c
d671cdf
f48d97a
f2d660d
94c139c
27a8e38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
NUT-18: Mint tokens Bitcoin On-Chain | ||
========================== | ||
|
||
`optional` | ||
|
||
--- | ||
|
||
This NUT describes the process of minting tokens on Bitcoin On-Chain analog to NUT-04 for Bitcoin Lightning. | ||
|
||
# Mint quote | ||
|
||
To request a mint quote, the wallet of `Alice` makes a `POST /v1/mint/quote/{method}` request where `method` is the payment method requested (here `btconchain`). | ||
|
||
```http | ||
POST https://mint.host:3338/v1/mint/quote/btconchain | ||
``` | ||
|
||
The wallet of `Alice` includes the following `PostMintQuoteBtcOnchainRequest` data in its request: | ||
|
||
```json | ||
{ | ||
"amount": <int>, | ||
"unit": <str_enum["sat"]> | ||
} | ||
``` | ||
with the requested `amount` and the `unit`. | ||
|
||
The mint `Bob` then responds with a `PostMintQuoteBtcOnchainResponse`: | ||
|
||
```json | ||
{ | ||
"quote": <str>, | ||
"address": <str>, | ||
"state": <str_enum[STATE]>, | ||
"expiry": <int> | ||
} | ||
``` | ||
|
||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this when the transaction is still confirming? |
||
- `"ISSUED"` means that the quote has already been issued. | ||
|
||
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. | ||
|
||
## 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" | ||
``` | ||
|
||
Response of `Bob`: | ||
|
||
```json | ||
{ | ||
"quote": "DSGLX9kevM...", | ||
"address": "bc1qkyfgd7mus7ykfd7qkwakq75qsf7rtm...", | ||
"state": "UNPAID", | ||
"expiry": 1701704757 | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it worth adding the txid here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might though this question made me think what if a a quote is payed with multiple transaction, I think that should still be valid? So to cover that it would need to be a list of txid? Unless we explicitly say it MUST be paid in one tx. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be explicit only one transaction is supported. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is always going to be possible to make multiple transactions paying the same address, so someone will do that... so how to handle? Multiple transactions also make reorg risk more complex and add consolidation cost for the Mint. Feels resonable that the requirement should be for a single transaction with one output paying the mint, anything else and the Mint can 'fail' the mint request and refund whatever it got somehow -- would need the user to provide a refund address, or the mint would have to issue an ecash claim on it (but then, consolidation cost etc., would be a 'mint with on-chain dust and then drain the mint's lightning channels' attackable behaviour) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we return the number of confirmations the transaction currently has? |
||
``` | ||
|
||
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. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be worth adding a note here that wallets SHOULD display the quote as a BIP21 URI or BIP21 URI encoded as a QR code to the user. This would help reduce errors of underpaying or overpaying accidentally. NOTE: There is a draft to replace BIP21 bitcoin/bips#1555 but as its backwards compatible i suggest we say bip21 for now and can update later. |
||
## Check mint quote state | ||
|
||
To check whether a mint quote has been paid, `Alice` makes a `GET /v1/mint/quote/btconchain/{quote_id}`. | ||
|
||
```http | ||
GET https://mint.host:3338/v1/mint/quote/btconchain/{quote_id} | ||
``` | ||
|
||
Like before, the mint `Bob` responds with a `PostMintQuoteBtcOnchainResponse`. | ||
|
||
Example request of `Alice` with curl: | ||
|
||
```bash | ||
curl -X GET https://mint.host:3338/v1/mint/quote/btconchain/DSGLX9kevM... | ||
``` | ||
|
||
# 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`). | ||
|
||
```http | ||
POST https://mint.host:3338/v1/mint/btconchain | ||
``` | ||
|
||
The wallet `Alice` includes the following `PostMintBtcOnchainRequest` data in its request | ||
|
||
```json | ||
{ | ||
"quote": <str>, | ||
"outputs": <Array[BlindedMessage]> | ||
} | ||
``` | ||
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 mint `Bob` then responds with a `PostMintBtcOnchainResponse`: | ||
|
||
```json | ||
{ | ||
"signatures": <Array[BlindSignature]> | ||
} | ||
``` | ||
|
||
where `signatures` is an array of blind signatures on the outputs. | ||
|
||
## Example | ||
|
||
Request of `Alice` with curl: | ||
|
||
```bash | ||
curl -X POST https://mint.host:3338/v1/mint/btconchain -H "Content-Type: application/json" -d \ | ||
'{ | ||
"quote": "DSGLX9kevM...", | ||
"outputs": [ | ||
{ | ||
"amount": 8, | ||
"id": "009a1f293253e41e", | ||
"B_": "035015e6d7ade60ba8426cefaf1832bbd27257636e44a76b922d78e79b47cb689d" | ||
}, | ||
{ | ||
"amount": 2, | ||
"id": "009a1f293253e41e", | ||
"B_": "0288d7649652d0a83fc9c966c969fb217f15904431e61a44b14999fabc1b5d9ac6" | ||
} | ||
] | ||
}' | ||
``` | ||
|
||
Response of `Bob`: | ||
|
||
```json | ||
{ | ||
"signatures": [ | ||
{ | ||
"id": "009a1f293253e41e", | ||
"amount": 2, | ||
"C_": "0224f1c4c564230ad3d96c5033efdc425582397a5a7691d600202732edc6d4b1ec" | ||
}, | ||
{ | ||
"id": "009a1f293253e41e", | ||
"amount": 8, | ||
"C_": "0277d1de806ed177007e5b94a8139343b6382e472c752a74e99949d511f7194f6c" | ||
} | ||
] | ||
} | ||
``` | ||
|
||
## Unblinding signatures | ||
|
||
Upon receiving the `BlindSignatures` from the mint `Bob`, the wallet of `Alice` unblinds them to generate `Proofs` (using the blinding factor `r` and the mint's public key `K`, see BDHKE [NUT-00][00]). The wallet then stores these `Proofs` in its database: | ||
|
||
```json | ||
[ | ||
{ | ||
"id": "009a1f293253e41e", | ||
"amount": 2, | ||
"secret": "407915bc212be61a77e3e6d2aeb4c727980bda51cd06a6afc29e2861768a7837", | ||
"C": "02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea" | ||
}, | ||
{ | ||
"id": "009a1f293253e41e", | ||
"amount": 8, | ||
"secret": "fe15109314e61d7756b0f8ee0f23a624acaa3f4e042f61433c728c7057b931be", | ||
"C": "029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059" | ||
} | ||
] | ||
``` | ||
|
||
## Settings | ||
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, | ||
"methods": [ | ||
{ | ||
"method": "btconchain", | ||
"unit": "sat", | ||
"min_amount": 10000, | ||
"max_amount": 1000000, | ||
"min_confirmations": 3 | ||
} | ||
] | ||
} | ||
} | ||
``` | ||
|
||
[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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If unit is included here don't we need to add an amount in sats to the quote response so the mint can signal the exchange rate. Otherwise if it is non bitcoin unit the wallet will not know how much bitcoin to send to the address to fill the quote.
That being said because of the imprecise nature of sending a bitcoin onchain transaction I would be for removing the unit and limiting it to sat denominated quotes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I support keeping it simple to start and only supporting sats as the unit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels simplest to say
So if you wanted to get e.g. "USD eCash by making an onchain tx", it's a two-step process. Has the advantage of a swap being atomic, much faster and less flaky than a mint depending on N confirmations