Skip to content

NUT-05: Melting tokens

mandatory

used in: NUT-08, NUT-15


Melting tokens is the opposite of minting tokens (see NUT-04). Like minting tokens, melting is a two-step process: requesting a melt quote and melting tokens. Here, we describe both steps.

In the first request the wallet asks the mint for a quote for a request it wants paid by the mint and the unit the wallet would like to spend as inputs. The mint responds with a quote that includes a quote id and an amount the mint demands in the requested unit. For the method bolt11, the mint includes a fee_reserve field indicating the reserve fee for a Lightning payment.

In the second request, the wallet includes the quote id and provides inputs that sum up to amount+fee+fee_reserve in the first response where fee is calculated from the keyset's input_fee_ppk as described in NUT-02. For the method bolt11, the wallet can also include outputs in order for the mint to return overpaid Lightning fees (see NUT-08). The mint responds with a payment state. If the state is "PAID" the response includes a payment_preimage as a proof of payment. If the request included outputs, the mint may respond with change for the overpaid fees (see NUT-08).

We limit this document to mint quotes of unit="sat" and method="bolt11" which requests a bolt11 Lightning payment (typically paid by the mint from its Bitcoin reserves) using ecash denominated in Satoshis.

Melt quote

To request a melt quote, the wallet of Alice makes a POST /v1/melt/quote/{method} request where method is the payment method requested (here bolt11).

POST https://mint.host:3338/v1/melt/quote/bolt11

The wallet Alice includes the following PostMeltQuoteBolt11Request data in its request:

{
  "request": <str>,
  "unit": <str_enum[UNIT]>
  "options": { // Optional
    "amountless": {
      "amount_msat": <int>
    }
  }
}

Here, request is the bolt11 Lightning invoice to be paid and unit is the unit the wallet would like to pay with. amount MAY be passed to pay amountless bolt11 invoices. The amount MUST be in msat and if the bolt11 invoice has an amount, the amount field MUST be equal to the amount of the bolt11 invoice. The request MAY be an amountless invoice only if support for amountless invoices is signaled in the MeltMethodSettings.

The mint Bob then responds with a PostMeltQuoteBolt11Response:

{
  "quote": <str>,
  "request": <str>,
  "amount": <int>,
  "unit": <str_enum[UNIT]>,
  "fee_reserve": <int>,
  "state": <str_enum[STATE]>,
  "expiry": <int>,
  "payment_preimage": <str|null>
}

Where quote is the quote ID, amount and unit the amount and unit that need to be provided, request the payment request from the quote request, 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 bolt11 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:

curl -X POST https://mint.host:3338/v1/melt/quote/bolt11 -d \
{
  "request": "lnbc100n1p3kdrv5sp5lpdxzghe5j67q...",
  "unit": "sat"
}

Response of Bob:

{
  "quote": "TRmjduhIsPxd...",
  "request": "lnbc100n1p3kdrv5sp5lpdxzghe5j67q...",
  "amount": 10,
  "unit": "sat",
  "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/bolt11/{quote_id}.

GET https://mint.host:3338/v1/melt/quote/bolt11/{quote_id}

Like before, the mint Bob responds with a PostMeltQuoteBolt11Response.

Example request of Alice with curl:

curl -X GET http://localhost:3338/v1/melt/quote/bolt11/TRmjduhIsPxd...

Melting tokens

Now that Alice knows what the total amount is (amount + fee + fee_reserve) in her requested unit, she can proceed for melting tokens for which a payment will be executed by the mint. She calls the POST /v1/melt/{method} endpoint where method is the payment method requested (here bolt11).

POST https://mint.host:3338/v1/melt/bolt11

⚠️ 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 PostMeltBolt11Request data in its request

{
  "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 + fee_reserve (see previous melt quote response).

Like before, the mint Bob then responds with a PostMeltQuoteBolt11Response. 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:

curl -X POST https://mint.host:3338/v1/melt/bolt11 -d \
'{
  "quote": "od4CN5smMMS3K3QVHkbGGNCTxfcAIyIXeq8IrfhP",
  "inputs": [
    {
      "amount": 4,
      "id": "009a1f293253e41e",
      "secret": "429700b812a58436be2629af8731a31a37fce54dbf8cbbe90b3f8553179d23f5",
      "C": "03b01869f528337e161a6768b480fcf9f75fd248b649c382f5e352489fd84fd011",
    },
    {
      "amount": 8,
      "id": "009a1f293253e41e",
      "secret": "4f3155acef6481108fcf354f6d06e504ce8b441e617d30c88924991298cdbcad",
      "C": "0278ab1c1af35487a5ea903b693e96447b2034d0fd6bac529e753097743bf73ca9",
    }
  ]
}'

Response PostMeltQuoteBolt11Response of Bob:

{
  "quote": "TRmjduhIsPxd...",
  "request": "lnbc100n1p3kdrv5sp5lpdxzghe5j67q...",
  "amount": 10,
  "unit": "sat",
  "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) which in this case reads

{
  "5": {
    "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:

{
  "method": <str>,
  "unit": <str>,
  "min_amount": <int|null>,
  "max_amount": <int|null>,
  "amountless": <bool|null>
}

min_amount and max_amount indicate the minimum and maximum amount for an operation of this method-unit pair. amountless signals if the backed supports paying an amountless invoice.

Example MeltMethodSetting:

{
  "method": "bolt11",
  "unit": "sat",
  "min_amount": 100,
  "max_amount": 10000,
  "amountless": true
}