optional
Token exchanges are atomic swaps enabled by HTLCs (see [NUT-14][14]). Token exchanges provide a way for eCash tokens to be exchanged for lightning payments without the issuing mint having to run a lightning node. Token exchanges allow for lightning payments to be provided by other users of the same mint, users of a different mint supporting [NUT-17][17], or a mint supporting NUT-05.
The exchange has several steps:
Alice
submits a request for quote to the mintBob
gets the request and submits a quote to the mint. The quote data includes his pubkey.Alice
gets the quote and submits HTLC tokens to the mintBob
obtains the HTLC preimage by completing the requested payment and redeems the HTLC tokens
In the first request, the wallet of Alice
asks the mint for a quote for a request
it wants paid and the unit
the wallet would like to spend as inputs.
The wallet of Bob
responds with a quote that includes a quote
id and an amount
being demanded in the requested unit. For the method bolt11
, the Bob
includes a fee_reserve
field indicating the reserve fee for a Lightning payment.
The wallet of Alice
includes the quote
id and provides HTLC inputs
that sum up to amount+fee_reserve
in the first response. For the method bolt11
, the wallet can also include outputs
in order for Bob
to return overpaid Lightning fees (see NUT-08). The mint responds with a payment status paid
and 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 using ecash denominated in Satoshis.
To request an exchange quote, the wallet of Alice
makes a POST /v1/exchange/quote/{method}
request where method
is the payment method requested (here bolt11
).
POST https://mint.host:3338/v1/exchange/quote/bolt11
The wallet Alice
includes the following PostexchangeQuoteBolt11Request
data in its request:
{
"request": <str>,
"unit": <str_enum["sat"]>
}
Here, request
is the bolt11 Lightning invoice to be paid and unit
is the unit the wallet would like to pay with.
To obtain the array of outstanding requests, wallet Bob
makes a GET /v1/exchange/request/{method}
request where method
is the payment method requested (here bolt11
).
GET https://mint.host:3338/v1/exchange/quote/bolt11
To submit an exchange quote, the wallet of Bob
makes a POST /v1/melt/quote/{method}/{invoice}
request where method
is the payment method requested (here bolt11
).
POST https://mint.host:3338/v1/exchange/quote/bolt11/{invoice}
The wallet Bob
includes the following PostexchangeQuoteBolt11Response
data in its request:
{
"quote": <str>,
"amount": <int>,
"fee_reserve": <int>,
"expiry": <int>
"pubkey": <str>
}
Where quote
is the quote id, amount
the amount that needs to be provided, and fee_reserve
the additional fee reserve that is required. Bob
expects Alice
to include Proofs
of at least total_amount = amount + fee_reserve
in the form of NUT-14 tokens addressed to pubkey
. expiry
is the Unix timestamp until which the exchange quote is valid.
To obtain the array of quotes, wallet Alice
makes a GET /v1/exchange/request/{method}/{invoice}
request where method
is the payment method requested (here bolt11
).
GET https://mint.host:3338/v1/exchange/quote/bolt11/{invoice}
Wallet Alice
submits HTLC proofs as payments for the quote by making a POST /v1/exchange/request/{method}/{invoice}/{quote_id}
request where method
is the payment method requested (here bolt11
).
POST https://mint.host:3338/v1/exchange/quote/bolt11/{invoice}/{quote_id}
The wallet Alice
includes the following PostexchangeQuoteBolt11Payment
data in its request:
{
"inputs": <Array[Proof]>
}
The wallet Bob
obtains the HTLC proofs by making a GET /v1/exchange/request/{method}/{invoice}/{quote_id}
request where method
is the payment method requested (here bolt11
).
The wallet Bob
pays invoice
to obtain preimage
and uses preimage
to redeem the HTLC tokens.
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...",
"amount": 10,
"fee_reserve": 2,
"paid": false,
"expiry": 1701704757
}
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...
Now that Alice
knows what the total amount is (amount + 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
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_reserve
(see previous melt quote response).
The mint Bob
then responds with a PostMeltBolt11Response
:
{
"paid": <bool>,
"payment_preimage": <str|null>
}
paid
is a boolean indicating whether the payment was successful, and payment_preimage
is the bolt11 payment preimage in case of a successful payment.
If paid==true
, Alice
's wallet can delete the inputs
from her database (or move them to a history). If paid==false
, Alice
can repeat the same request again until the payment is successful.
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 of Bob
:
{
"paid": true,
"payment_preimage": "c5a1ae1f639e1f4a3872e81500fd028bece7bedc1152f740cba5c3417b748c1b"
}
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) 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 this melting is disabled.
MeltMethodSetting
is of the form:
{
"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
:
{
"method": "bolt11",
"unit": "sat",
"min_amount": 100,
"max_amount": 10000
}