NUT-25: BOLT12
optional
depends on: NUT-04, NUT-05, NUT-20
This document describes minting and melting ecash with the
bolt12 payment method, which uses Lightning Network
offers. It is an extension of NUT-04 and
NUT-05 which cover the protocol steps of
minting and melting ecash shared by any supported payment
method.
Mint Quote
For the bolt12 method, the wallet includes the
following specific PostMintQuoteBolt12Request
data:
{
"amount": <int|null>,
"unit": <str_enum[UNIT]>,
"description": <str|null>,
"pubkey": <str>
}
Note: While a pubkey is optional as per NUT-20 for NUT-04 it is required in this NUT and the mint MUST NOT issue a mint quote if one is not included.
Privacy: To prevent linking multiple mint quotes together, wallets SHOULD generate a unique public key for each mint quote request.
The mint responds with a
PostMintQuoteBolt12Response:
{
"quote": <str>,
"request": <str>,
"amount": <int|null>,
"unit": <str_enum[UNIT]>,
"method": "bolt12",
"expiry": <int|null>,
"pubkey": <str>,
"amount_paid": <int>,
"amount_issued": <int>,
"updated_at": <int>
}
Where:
quoteis the quote IDrequestis the bolt12 offeramountechoes the requested amount, ornullfor an amountless offerexpiryis the Unix timestamp until which the mint quote is validamount_paidis the amount that has been paid to the mint via the bolt12 offeramount_issuedis the amount of ecash that has been issued for the given mint quoteupdated_atis defined in NUT-04
Example
Request with curl:
curl -X POST http://localhost:3338/v1/mint/quote/bolt12 -d \
'{"amount": 10, "unit": "sat", "pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac"}' \
-H "Content-Type: application/json"
Response:
{
"quote": "019e6d5a-2347-7000-8449-370fefb42fed",
"request": "lno1qcp...",
"amount": 10,
"unit": "sat",
"method": "bolt12",
"expiry": 1701704757,
"pubkey": "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac",
"amount_paid": 0,
"amount_issued": 0,
"updated_at": 1701704657
}
Check mint quote:
curl -X GET http://localhost:3338/v1/mint/quote/bolt12/019e6d5a-2347-7000-8449-370fefb42fed
Minting tokens:
curl -X POST https://mint.host:3338/v1/mint/bolt12 -H "Content-Type: application/json" -d \
'{
"quote": "019e6d5a-2347-7000-8449-370fefb42fed",
"outputs": [
{
"amount": 8,
"id": "009a1f293253e41e",
"B_": "035015e6d7ade60ba8426cefaf1832bbd27257636e44a76b922d78e79b47cb689d"
},
{
"amount": 2,
"id": "009a1f293253e41e",
"B_": "0288d7649652d0a83fc9c966c969fb217f15904431e61a44b14999fabc1b5d9ac6"
}
]
}'
Response:
{
"signatures": [
{
"id": "009a1f293253e41e",
"amount": 2,
"C_": "0224f1c4c564230ad3d96c5033efdc425582397a5a7691d600202732edc6d4b1ec"
},
{
"id": "009a1f293253e41e",
"amount": 8,
"C_": "0277d1de806ed177007e5b94a8139343b6382e472c752a74e99949d511f7194f6c"
}
]
}
Multiple Issuances
Unlike BOLT11 invoices, BOLT12 offers can be paid multiple
times, allowing the wallet to mint multiple times for one quote.
The wallet can call the check bolt12 endpoint, where the mint will
return the PostMintQuoteBolt12Response including
amount_paid and amount_issued. The
difference between these values represents how much the wallet can
mint by calling the mint endpoint. Wallets MAY mint any amount up
to this available difference; in particular, they can mint less
than the amount mintable. Mints MUST accept mint requests whose
total output amount is less than or equal to
(amount_paid - amount_issued).
Mint Settings
A description option SHOULD be
set to indicate whether the bolt12 payment method
backend supports providing an offer description.
Example
MintMethodSetting
{
"method": "bolt12",
"unit": <str>,
"min_amount": <int|null>,
"max_amount": <int|null>,
"options": {
"description": true
}
}
Melt Quote
For the bolt12 method, the wallet includes the
following specific PostMeltQuoteBolt12Request
data:
{
"request": <str>,
"unit": <str_enum[UNIT]>,
"options": { // Optional
"amountless": {
"amount_msat": <int>
}
}
}
Here, request is the bolt12 Offer to be paid and
unit is the unit the wallet would like to pay with.
For amount-less offers, the
options.amountless.amount_msat field can be used to
specify the amount in millisatoshis to pay to the offer. If
options.amountless.amount_msat is defined and the
offer has an amount, they MUST be equal.
The mint responds with a
PostMeltQuoteBolt12Response:
{
"quote": <str>,
"request": <str>,
"amount": <int>,
"unit": <str_enum[UNIT]>,
"method": "bolt12",
"fee_reserve": <int>,
"state": <str_enum[STATE]>,
"expiry": <int>,
"payment_preimage": <str|null>
}
Where fee_reserve is the additional fee reserve
required for the Lightning payment. The mint expects the wallet to
include Proofs of at least
total_amount = amount + fee_reserve + fee where
fee is calculated from the keyset’s
input_fee_ppk as described in NUT-02.
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.
Melting Tokens
For the bolt12 method, the wallet can include an
optional outputs field in the melt request to receive
change for overpaid Lightning fees (see NUT-08):
{
"quote": <str>,
"inputs": <Array[Proof]>,
"outputs": <Array[BlindedMessage]> // Optional
}
If the outputs field is included and there is
excess from the fee_reserve, the mint will respond
with a change field containing blind signatures for
the overpaid amount:
{
"quote": <str>,
"request": <str>,
"amount": <int>,
"unit": <str_enum[UNIT]>,
"method": "bolt12",
"fee_reserve": <int>,
"state": <str_enum[STATE]>,
"expiry": <int>,
"payment_preimage": <str>,
"change": <Array[BlindSignature]> // Present if outputs were included and there's change
}
Example
Melt quote request:
curl -X POST https://mint.host:3338/v1/melt/quote/bolt12 -d \
'{"request": "lno1qcp4256ypqpq86q69t5wv5629arxqurn8cxg9p5qmmqy2e5xq...", "unit": "sat"}'
Melt quote response:
{
"quote": "019e6d5a-2347-7000-82b3-56e12c3fcdf2",
"request": "lno1qcp4256ypqpq86q69t5wv5629arxqurn8cxg9p5qmmqy2e5xq...",
"amount": 10,
"unit": "sat",
"method": "bolt12",
"fee_reserve": 2,
"state": "UNPAID",
"expiry": 1701704757
}
Check quote state:
curl -X GET http://localhost:3338/v1/melt/quote/bolt12/019e6d5a-2347-7000-82b3-56e12c3fcdf2
Melt request:
curl -X POST https://mint.host:3338/v1/melt/bolt12 -d \
'{"quote": "019e6d5a-2347-7000-82b3-56e12c3fcdf2", "inputs": [...]}'
Successful melt response:
{
"quote": "019e6d5a-2347-7000-82b3-56e12c3fcdf2",
"request": "lno1qcp4256ypqpq86q69t5wv5629arxqurn8cxg9p5qmmqy2e5xq...",
"amount": 10,
"unit": "sat",
"method": "bolt12",
"fee_reserve": 2,
"state": "PAID",
"expiry": 1701704757,
"payment_preimage": "c5a1ae1f639e1f4a3872e81500fd028bece7bedc1152f740cba5c3417b748c1b"
}
Example
MeltMethodSetting
{
"method": "bolt12",
"unit": <str>,
"min_amount": <int|null>,
"max_amount": <int|null>
}