-
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
Conversation
looks great so far, nice work! question about the minting How do we know when the payment is complete? Do we just try to proceed with mint, and see if it works? Or is there a way to look up the Also, is there a way to let the client know, how many more confs are required, before the mint can take place? Last question, what happens if the tx confirmation time exceeds the expiry? |
18.md
Outdated
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. | ||
|
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 think it's worth mentioning that there are (at least) two ways of handing the variable fee rate problem here.
One option could be that the wallet chooses a fee in PostMeltQuoteBtcOnchainRequest
this would be a bit like a Lightning node choosing the max_fee
for a payment. The mint could then respond with a response or an error depending on whether it agrees. I think this would have to accompanied with some info setting where the mint announces it's accepted fee range.
The other option, as you've proposed, would be for the mint to limit the users's choices by returning multiple melt quotes.
Did you consider the first option as well? I wonder if it has any benefits. Happy to hear thoughts from others on this as well.
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.
Interesting idea. No haven't thought about it yet.
You can use the following endpoint as per
This would be cool but I suspect it won't be generally possible to determine this number with every on-chain backend. Take for example a service like Strike or Blink. Obviously, this would be possible with a bitcoin core, lnd, cln, ... on-chain wallet.
That's a very good question. While LN invoices can have a clear expiry, on-chain is a bit more tricky. It might be worth to consider removing this from onchain mints. I think melt expiry doesn't have this problem. |
17.md
Outdated
"quote": <str>, | ||
"address": <str>, | ||
"paid": <bool>, | ||
"expiry": <int> |
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.
This expiry field might be problematic (cf @gandlafbtc's comment).
Awesome NUT, feels complete! Looking forward to implementing it. Left a few comments, I don't see any issues other than the |
Co-authored-by: callebtc <[email protected]>
Co-authored-by: callebtc <[email protected]>
Co-authored-by: thesimplekid <[email protected]>
Good point. I think it's problematic in both way: A wallet could choose a fee that is to low to get in the next block before the quote expires. |
Worth noting that for sat denominated quotes a long expiry is fine, but for units with an exchange rate (ie sat/usd) it introduces some risk to the mint where wallets could attempt to arbitrage the exchange rate since its up to them to execute the quote or not so i don't think long expiry times should be recommended for non sat units. EDIT: Is this NUT limited to only the sat unit like NUT04? if so the above can be ignored. |
This comment was marked as resolved.
This comment was marked as resolved.
18.md
Outdated
GET https://mint.host:3338/v1/melt/btconchain/{tx_id} | ||
``` | ||
|
||
The mint `Bob` responds with a `GetMeltBtcOnchainResponse`. | ||
|
||
```json | ||
{ | ||
"paid": false | ||
} | ||
``` |
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.
is this paid
field different than calling GET /v1/melt/quote/btconchain/{quote_id}
and getting that paid
status there?
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 think this should be removed. I don't see any benefit over just using GET /v1/melt/quote/btconchain/{quote_id}
.
A related open question I have is should the mint broadcast the transaction as soon as it receives valid proofs for the melt, or can the mint decide to wait and include it in a batch of multiple melts?
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 think this should be removed. I don't see any benefit over just using
GET /v1/melt/quote/btconchain/{quote_id}
.
yeah
A related open question I have is should the mint broadcast the transaction as soon as it receives valid proofs for the melt, or can the mint decide to wait and include it in a batch of multiple melts?
interesting. That will require some changes to the response of the quote state call
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 removed the redundant GET https://mint.host:3338/v1/melt/btconchain/{tx_id} endpoint
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 think this should be removed. I don't see any benefit over just using
GET /v1/melt/quote/btconchain/{quote_id}
.A related open question I have is should the mint broadcast the transaction as soon as it receives valid proofs for the melt, or can the mint decide to wait and include it in a batch of multiple melts?
IMO the mint can decide if it wants to batch transaction or not. This is same as centralized btc exchanges handle this. We could add a property to the info-endpoint to signal that a mint might delay the melt for batching, but I don't think this is necessary
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.
IMO the mint can decide if it wants to batch transaction or not. This is same as centralized btc exchanges handle this. We could add a property to the info-endpoint to signal that a mint might delay the melt for batching, but I don't think this is necessary
I think we do need to signal this and it needs to be more concrete then the mint may delay. I would suggest we add a time component to the MeltQuoteResponse
this way if the user wants the withdrawal more quickly they will need to pay a higher fee if they are willing to wait and allow the mint to batch they can select a Quote
with a lower fee and wait longer.
Allowing the mint to delay for batching but not defining what a delay means will lead to a bad user experience where they don't know when their transaction will be process and they may need it immediately and be willing to pay for that.
This comment was marked as resolved.
This comment was marked as resolved.
Co-authored-by: thesimplekid <[email protected]>
Co-authored-by: thesimplekid <[email protected]>
No idea if this is relevant but was reviewing it and thought of this thread/PR/NUT.
|
17.md
Outdated
```json | ||
{ | ||
"amount": <int>, | ||
"unit": <str_enum["sat"]> |
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.
"unit": <str_enum["sat"]> |
Note: All PostMintQuoteBtcOnchainRequest
's are denominated in sat.
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.
Why should we remove the unit-field from the PostMintQuoteOnchainRequest? If the mint uses blink as a backend for handling the onchain transactions it can support onchain snd/receive with usd as a currency. Hopefully in the future there will be similar services. https://dev.blink.sv/api/usd-onchain-send
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.
This is a question of scoping this NUT. To support other units where a conversion rate is involved how expiration is handled as well as how confirmation time is defined is much more important as the conversion rate could change outside an acceptable slippage. So in order to support other units how confirmation and expiration is defined needs to be included as well.
So what should we do? I think it would be best to change the expiry for mint-requests to optional so a mint can use a expiry for usd based mints and no expiry for sat based mint requests? Any other suggestions @gandlafbtc @callebtc @thesimplekid |
Co-authored-by: thesimplekid <[email protected]>
Co-authored-by: thesimplekid <[email protected]>
The issue with on chain is there is no way to expire an address unlike ln, so how is a deposit after an expiration handled is it considered a donation to the mint? If that is that case I think that needs to be made explicit. I think this maybe okay since even in the case of removing expiration that is effectively the case if a deposit is made to a mint that is no longer in operation (if the mint operator still has the keys), so having the explicit handling of expiration might be an improvement. Of course there is the opportunity for a benevolent mint operator to settle a quote paid after out of band if they are known or have contact info set in the NUT-06. I noticed this is how the strike api handles it, though that has its own trade offs. As i mentioned above definition of confirmation needs to be included as well with the expiration to signal what needs to happen before the expiration in order for the quote to be settled, num blocks conf, transaction broadcast. This could even be split for example transaction needs to be broadcast before x time to get the exchange rate, but ecash wont be issued until x confirmations. |
Yes, good point. I have added min_confirmations to the info-endpoint |
"address": "bc1qkyfgd7mus7ykfd7qkwakq75qsf7rtm...", | ||
"state": "UNPAID", | ||
"expiry": 1701704757 | ||
} |
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.
Is it worth adding the txid here?
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.
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 comment
The 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 comment
The 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)
``` | ||
|
||
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 comment
The 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.
I wonder if we want to allow minting/melting on Liquid chain and if yes, then whether it should be part of these two nuts or a new pair of nuts. |
I think it makes more sense to keep separate NUTS, especially since its support should be signaled differently by NUT-06. |
Ah yes, that is indeed a good reason. |
It does not necessarily impact the spec, but something like this should be the recommended way to handle address generation: https://blog.zaprite.com/optimizing-address-usage-for-the-gap-limit/ |
```json | ||
[ | ||
{ | ||
"quote": <str>, | ||
"description": <str|null>, | ||
"amount": <int>, | ||
"fee": <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. |
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.
It maybe better if we express fee in sats but also with a time component. This would allow the mint to batch transactions. For example fee of x sats will be paid within the hour, fee of y sats within the day etc. This leaves it up to the mint to actually set the final sats per vbyte to meet whatever time period they agreed to in the quote.
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.
Should the client specify how fast they want the transaction confirmed in the quote request (e.g., with a target_blocks
field)?
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.
As its currently written the mint returns a list of quotes with different fees for different speed of confirmation. That could be flipped and the client tells the mint what it wants and the mint returns only one quote for that target.
I lean towards how its currently written where the mints returns a set of options and then the client picks the one that best suits it. But as i comments i think we need a time component in addition to the fee so that could be expressed in block time.
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.
Should the spec offer guidance to the mint implementors on what fee rates should be returned so wallet experiences can be similar even though the wallet isn't in control of requesting its fee rate?
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.
Should the spec offer guidance to the mint implementors on what fee rates should be returned so wallet experiences can be similar even though the wallet isn't in control of requesting its fee rate?
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.
what fee rates should be returned
Surely the mint should be able to pick whatever fee it wants? Needs to be flexible to feerate market swings, but also some mints might want to disincentivise on-chain melts by adding a premium, others might offer it at cost
```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> | ||
} | ||
``` |
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
- You can only mint BTC on-chain (sats only, no other units)
- You can then swap sats ecash for ecash in a different unit with the mint
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
`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 comment
The reason will be displayed to describe this comment to others. Learn more.
Is this when the transaction is still confirming?
"address": "bc1qkyfgd7mus7ykfd7qkwakq75qsf7rtm...", | ||
"state": "UNPAID", | ||
"expiry": 1701704757 | ||
} |
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.
Should we return the number of confirmations the transaction currently has?
```json | ||
[ | ||
{ | ||
"quote": <str>, | ||
"description": <str|null>, | ||
"amount": <int>, | ||
"fee": <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. |
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.
Should the client specify how fast they want the transaction confirmed in the quote request (e.g., with a target_blocks
field)?
All, Is there an implementation of this on-chain mint/melt? I'm thinking of starting one with nutshell. Checking to make sure I'm not duplicating work. |
PR for cdk is here cashubtc/cdk#172 haven't seen anything for nutshell. |
great. thanks. Will start to work on Nutshell. |
First draft for minting and melting tokens On-Chain.