NUT-04/05: add amount, unit, request to melt and mint quote responses (#719)

* add amount, unit, request to melt and mint responses

* make new fields optional to not break compat with old mints

* make new flags optional for backwards compat
This commit is contained in:
callebtc
2025-03-08 18:23:34 +00:00
committed by GitHub
parent 3ab1e1dfb2
commit bae4855915
6 changed files with 41 additions and 2 deletions

View File

@@ -141,6 +141,8 @@ class PostMintQuoteRequest(BaseModel):
class PostMintQuoteResponse(BaseModel):
quote: str # quote id
request: str # input payment request
amount: Optional[int] # output amount (optional for backwards compat pre 0.16.6)
unit: Optional[str] # output unit (optional for backwards compat pre 0.16.6)
state: Optional[str] # state of the quote (optional for backwards compat)
expiry: Optional[int] # expiry of the quote
pubkey: Optional[str] = None # NUT-20 quote lock pubkey
@@ -222,6 +224,10 @@ class PostMeltQuoteRequest(BaseModel):
class PostMeltQuoteResponse(BaseModel):
quote: str # quote id
amount: int # input amount
unit: Optional[str] # input unit (optional for backwards compat pre 0.16.6)
request: Optional[
str
] # output payment request (optional for backwards compat pre 0.16.6)
fee_reserve: int # input fee reserve
paid: Optional[bool] = (
None # whether the request has been paid # DEPRECATED as per NUT PR #136

View File

@@ -667,7 +667,10 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
if not payment_quote.checking_id:
raise Exception("quote has no checking id")
# verify that payment quote amount is as expected
if melt_quote.is_mpp and melt_quote.mpp_amount != payment_quote.amount.to(Unit.msat).amount:
if (
melt_quote.is_mpp
and melt_quote.mpp_amount != payment_quote.amount.to(Unit.msat).amount
):
raise TransactionError("quote amount not as requested")
# make sure the backend returned the amount with a correct unit
if not payment_quote.amount.unit == unit:
@@ -759,6 +762,8 @@ class Ledger(LedgerVerification, LedgerSpendingConditions, LedgerTasks, LedgerFe
return PostMeltQuoteResponse(
quote=quote.quote,
amount=quote.amount,
unit=quote.unit,
request=quote.request,
fee_reserve=quote.fee_reserve,
paid=quote.paid, # deprecated
state=quote.state.value,

View File

@@ -163,8 +163,10 @@ async def mint_quote(
logger.trace(f"> POST /v1/mint/quote/bolt11: payload={payload}")
quote = await ledger.mint_quote(payload)
resp = PostMintQuoteResponse(
request=quote.request,
quote=quote.quote,
request=quote.request,
amount=quote.amount,
unit=quote.unit,
paid=quote.paid, # deprecated
state=quote.state.value,
expiry=quote.expiry,
@@ -190,6 +192,8 @@ async def get_mint_quote(request: Request, quote: str) -> PostMintQuoteResponse:
resp = PostMintQuoteResponse(
quote=mint_quote.quote,
request=mint_quote.request,
amount=mint_quote.amount,
unit=mint_quote.unit,
paid=mint_quote.paid, # deprecated
state=mint_quote.state.value,
expiry=mint_quote.expiry,
@@ -290,6 +294,8 @@ async def get_melt_quote(request: Request, quote: str) -> PostMeltQuoteResponse:
resp = PostMeltQuoteResponse(
quote=melt_quote.quote,
amount=melt_quote.amount,
unit=melt_quote.unit,
request=melt_quote.request,
fee_reserve=melt_quote.fee_reserve,
paid=melt_quote.paid,
state=melt_quote.state.value,

View File

@@ -469,6 +469,8 @@ class LedgerAPI(LedgerAPIDeprecated, SupportsAuth):
return PostMeltQuoteResponse(
quote=quote_id,
amount=amount_sat,
unit=unit.name,
request=payment_request,
fee_reserve=ret.fee or 0,
paid=False,
state=MeltQuoteState.unpaid.value,
@@ -550,6 +552,8 @@ class LedgerAPI(LedgerAPIDeprecated, SupportsAuth):
return PostMeltQuoteResponse(
quote=quote,
amount=0,
unit="sat",
request="lnbc0",
fee_reserve=0,
paid=ret.paid or False,
state=(

View File

@@ -257,9 +257,12 @@ class LedgerAPIDeprecated(SupportsHttpxClient, SupportsMintURL):
return_dict = resp.json()
mint_response = GetMintResponse_deprecated.parse_obj(return_dict)
decoded_invoice = bolt11.decode(mint_response.pr)
assert decoded_invoice.amount_msat, Exception("no amount in invoice")
return PostMintQuoteResponse(
quote=mint_response.hash,
request=mint_response.pr,
amount=decoded_invoice.amount_msat // 1000,
unit="sat",
paid=False,
state=MintQuoteState.unpaid.value,
expiry=decoded_invoice.date + (decoded_invoice.expiry or 0),

View File

@@ -202,6 +202,9 @@ async def test_mint_quote(ledger: Ledger):
resp_quote = PostMintQuoteResponse(**result)
assert resp_quote.quote == result["quote"]
assert resp_quote.state == MintQuoteState.unpaid.value
assert resp_quote.amount == 100
assert resp_quote.unit == "sat"
assert resp_quote.request == result["request"]
# check if DEPRECATED paid flag is also returned
assert result["paid"] is False
@@ -230,6 +233,9 @@ async def test_mint_quote(ledger: Ledger):
resp_quote = PostMintQuoteResponse(**result2)
assert resp_quote.quote == result["quote"]
assert resp_quote.state == MintQuoteState.paid.value
assert resp_quote.amount == 100
assert resp_quote.unit == "sat"
assert resp_quote.request == result["request"]
# check if DEPRECATED paid flag is also returned
assert result2["paid"] is True
@@ -338,6 +344,9 @@ async def test_melt_quote_internal(ledger: Ledger, wallet: Wallet):
assert resp_quote.payment_preimage is None
assert resp_quote.change is None
assert resp_quote.state == MeltQuoteState.unpaid.value
assert resp_quote.amount == 64
assert resp_quote.unit == "sat"
assert resp_quote.request == request
# check if DEPRECATED paid flag is also returned
assert result["paid"] is False
@@ -446,6 +455,9 @@ async def test_melt_internal(ledger: Ledger, wallet: Wallet):
assert resp_quote.payment_preimage is None
assert resp_quote.change == []
assert resp_quote.state == MeltQuoteState.paid.value
assert resp_quote.amount == 64
assert resp_quote.unit == "sat"
assert resp_quote.request == invoice_payment_request
# check if DEPRECATED paid flag is also returned
assert result["paid"] is True
@@ -504,6 +516,9 @@ async def test_melt_external(ledger: Ledger, wallet: Wallet):
# deserialize the response
resp_quote = PostMeltQuoteResponse(**result)
assert resp_quote.quote == quote.quote
assert resp_quote.amount == 62
assert resp_quote.unit == "sat"
assert resp_quote.request == invoice_payment_request
assert resp_quote.payment_preimage is not None
assert len(resp_quote.payment_preimage) == 64
assert resp_quote.change is not None