feat: use amount type in ln

This commit is contained in:
thesimplekid
2024-08-13 11:13:17 +01:00
parent c0211e6c71
commit 03e2b86022
6 changed files with 92 additions and 58 deletions

View File

@@ -40,6 +40,8 @@
- cdk(mint): Refactored `MintKeySet::generate_from_xpriv` and `MintKeySet::generate_from_seed` methods to accept max_order, currency_unit, and derivation_path parameters directly ([vnprc]).
- cdk(wallet): Return WalletKey for UnknownWallet error ([davidcaseria]).
- cdk(cdk-lightning): `CreateInvoiceResponse` added expiry time to better support backends where it cannot be set ([thesimeplkid]).
- cdk(cdk-lightning): Use `Amount` type instead of `u64` ([thesimplekid]).
- cdk(cdk-lightning): `CreateInvoice` requires unit argument ([thesimplekid]).
### Added
- cdk(NUT-11): Add `Copy` on `SigFlag` ([thesimplekid]).

View File

@@ -4,6 +4,7 @@ use anyhow::Result;
use axum::extract::{Json, Path, State};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use cdk::amount::Amount;
use cdk::cdk_lightning::to_unit;
use cdk::error::{Error, ErrorResponse};
use cdk::nuts::nut05::MeltBolt11Response;
@@ -70,7 +71,7 @@ pub async fn get_mint_bolt11_quote(
let quote_expiry = unix_time() + state.quote_ttl;
let create_invoice_response = ln
.create_invoice(amount, "".to_string(), quote_expiry)
.create_invoice(amount, &payload.unit, "".to_string(), quote_expiry)
.await
.map_err(|err| {
tracing::error!("Could not create invoice: {}", err);
@@ -156,7 +157,7 @@ pub async fn get_melt_bolt11_quote(
.new_melt_quote(
payload.request.to_string(),
payload.unit,
payment_quote.amount.into(),
payment_quote.amount,
payment_quote.fee.into(),
unix_time() + state.quote_ttl,
payment_quote.request_lookup_id,
@@ -270,8 +271,7 @@ pub async fn post_melt_bolt11(
}
};
let mut partial_msats = None;
let mut max_fee_msats = None;
let mut partial_amount = None;
// If the quote unit is SAT or MSAT we can check that the expected fees are provided.
// We also check if the quote is less then the invoice amount in the case that it is a mmp
@@ -283,8 +283,8 @@ pub async fn post_melt_bolt11(
let quote_msats = to_unit(quote.amount, &quote.unit, &CurrencyUnit::Msat)
.expect("Quote unit is checked above that it can convert to msat");
let invoice_amount_msats = match invoice.amount_milli_satoshis() {
Some(amount) => amount,
let invoice_amount_msats: Amount = match invoice.amount_milli_satoshis() {
Some(amount) => amount.into(),
None => {
if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
tracing::error!("Could not reset melt quote state: {}", err);
@@ -293,30 +293,29 @@ pub async fn post_melt_bolt11(
}
};
partial_msats = match invoice_amount_msats > quote_msats {
true => Some(invoice_amount_msats - quote_msats),
partial_amount = match invoice_amount_msats > quote_msats {
true => {
let partial_msats = invoice_amount_msats - quote_msats;
Some(
to_unit(partial_msats, &CurrencyUnit::Msat, &quote.unit)
.map_err(|_| into_response(Error::UnsupportedUnit))?,
)
}
false => None,
};
let max_fee = to_unit(quote.fee_reserve, &quote.unit, &CurrencyUnit::Msat)
.expect("Quote unit is checked above that it can convert to msat");
max_fee_msats = Some(max_fee);
let amount_to_pay_msats = match partial_msats {
let amount_to_pay = match partial_amount {
Some(amount_to_pay) => amount_to_pay,
None => invoice_amount_msats,
None => to_unit(invoice_amount_msats, &CurrencyUnit::Msat, &quote.unit)
.map_err(|_| into_response(Error::UnsupportedUnit))?,
};
let input_amount_msats =
to_unit(inputs_amount_quote_unit, &quote.unit, &CurrencyUnit::Msat)
.expect("Quote unit is checked above that it can convert to msat");
if amount_to_pay_msats + max_fee > input_amount_msats {
if amount_to_pay + quote.fee_reserve > inputs_amount_quote_unit {
tracing::debug!(
"Not enough inuts provided: {} msats needed {} msats",
input_amount_msats,
amount_to_pay_msats
inputs_amount_quote_unit,
amount_to_pay
);
if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
@@ -339,7 +338,7 @@ pub async fn post_melt_bolt11(
};
let pre = match ln
.pay_invoice(quote.clone(), partial_msats, max_fee_msats)
.pay_invoice(quote.clone(), partial_amount, Some(quote.fee_reserve))
.await
{
Ok(pay) => pay,
@@ -358,10 +357,10 @@ pub async fn post_melt_bolt11(
}
};
let amount_spent = to_unit(pre.total_spent_msats, &ln.get_settings().unit, &quote.unit)
let amount_spent = to_unit(pre.total_spent, &ln.get_settings().unit, &quote.unit)
.map_err(|_| into_response(Error::UnsupportedUnit))?;
(pre.payment_preimage, amount_spent.into())
(pre.payment_preimage, amount_spent)
}
};

View File

@@ -10,6 +10,7 @@ use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait;
use cdk::amount::Amount;
use cdk::cdk_lightning::{
self, to_unit, CreateInvoiceResponse, MintLightning, MintMeltSettings, PayInvoiceResponse,
PaymentQuoteResponse, Settings,
@@ -130,7 +131,8 @@ impl MintLightning for Cln {
&melt_quote_request.unit,
)?;
let relative_fee_reserve = (self.fee_reserve.percent_fee_reserve * amount as f32) as u64;
let relative_fee_reserve =
(self.fee_reserve.percent_fee_reserve * u64::from(amount) as f32) as u64;
let absolute_fee_reserve: u64 = self.fee_reserve.min_fee_reserve.into();
@@ -149,8 +151,8 @@ impl MintLightning for Cln {
async fn pay_invoice(
&self,
melt_quote: mint::MeltQuote,
partial_msats: Option<u64>,
max_fee_msats: Option<u64>,
partial_amount: Option<Amount>,
max_fee: Option<Amount>,
) -> Result<PayInvoiceResponse, Self::Err> {
let mut cln_client = self.cln_client.lock().await;
@@ -181,9 +183,24 @@ impl MintLightning for Cln {
exemptfee: None,
localinvreqid: None,
exclude: None,
maxfee: max_fee_msats.map(CLN_Amount::from_msat),
maxfee: max_fee
.map(|a| {
let msat = to_unit(a, &melt_quote.unit, &CurrencyUnit::Msat)?;
Ok::<cln_rpc::primitives::Amount, Self::Err>(CLN_Amount::from_msat(
msat.into(),
))
})
.transpose()?,
description: None,
partial_msat: partial_msats.map(CLN_Amount::from_msat),
partial_msat: partial_amount
.map(|a| {
let msat = to_unit(a, &melt_quote.unit, &CurrencyUnit::Msat)?;
Ok::<cln_rpc::primitives::Amount, Self::Err>(CLN_Amount::from_msat(
msat.into(),
))
})
.transpose()?,
}))
.await
.map_err(Error::from)?;
@@ -199,7 +216,11 @@ impl MintLightning for Cln {
payment_preimage: Some(hex::encode(pay_response.payment_preimage.to_vec())),
payment_hash: pay_response.payment_hash.to_string(),
status,
total_spent_msats: pay_response.amount_sent_msat.msat(),
total_spent: to_unit(
pay_response.amount_sent_msat.msat(),
&CurrencyUnit::Msat,
&melt_quote.unit,
)?,
}
}
_ => {
@@ -213,7 +234,8 @@ impl MintLightning for Cln {
async fn create_invoice(
&self,
amount_msats: u64,
amount: Amount,
unit: &CurrencyUnit,
description: String,
unix_expiry: u64,
) -> Result<CreateInvoiceResponse, Self::Err> {
@@ -223,7 +245,10 @@ impl MintLightning for Cln {
let mut cln_client = self.cln_client.lock().await;
let label = Uuid::new_v4().to_string();
let amount_msat = AmountOrAny::Amount(CLN_Amount::from_msat(amount_msats));
let amount = to_unit(amount, unit, &CurrencyUnit::Msat)?;
let amount_msat = AmountOrAny::Amount(CLN_Amount::from_msat(amount.into()));
let cln_response = cln_client
.call(cln_rpc::Request::Invoice(InvoiceRequest {
amount_msat,

View File

@@ -11,6 +11,7 @@ use std::sync::Arc;
use async_trait::async_trait;
use bitcoin::hashes::{sha256, Hash};
use bitcoin::secp256k1::{Secp256k1, SecretKey};
use cdk::amount::Amount;
use cdk::cdk_lightning::{
self, to_unit, CreateInvoiceResponse, MintLightning, MintMeltSettings, PayInvoiceResponse,
PaymentQuoteResponse, Settings,
@@ -96,7 +97,8 @@ impl MintLightning for FakeWallet {
&melt_quote_request.unit,
)?;
let relative_fee_reserve = (self.fee_reserve.percent_fee_reserve * amount as f32) as u64;
let relative_fee_reserve =
(self.fee_reserve.percent_fee_reserve * u64::from(amount) as f32) as u64;
let absolute_fee_reserve: u64 = self.fee_reserve.min_fee_reserve.into();
@@ -115,20 +117,21 @@ impl MintLightning for FakeWallet {
async fn pay_invoice(
&self,
melt_quote: mint::MeltQuote,
_partial_msats: Option<u64>,
_max_fee_msats: Option<u64>,
_partial_msats: Option<Amount>,
_max_fee_msats: Option<Amount>,
) -> Result<PayInvoiceResponse, Self::Err> {
Ok(PayInvoiceResponse {
payment_preimage: Some("".to_string()),
payment_hash: "".to_string(),
status: MeltQuoteState::Paid,
total_spent_msats: melt_quote.amount.into(),
total_spent: melt_quote.amount,
})
}
async fn create_invoice(
&self,
amount_msats: u64,
amount: Amount,
unit: &CurrencyUnit,
description: String,
unix_expiry: u64,
) -> Result<CreateInvoiceResponse, Self::Err> {
@@ -149,11 +152,13 @@ impl MintLightning for FakeWallet {
let payment_hash = sha256::Hash::from_slice(&[0; 32][..]).unwrap();
let payment_secret = PaymentSecret([42u8; 32]);
let amount = to_unit(amount, unit, &CurrencyUnit::Msat)?;
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
.description(description)
.payment_hash(payment_hash)
.payment_secret(payment_secret)
.amount_milli_satoshis(amount_msats)
.amount_milli_satoshis(amount.into())
.current_timestamp()
.min_final_cltv_expiry_delta(144)
.build_signed(|hash| Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key))

View File

@@ -9,6 +9,7 @@ use std::sync::Arc;
use anyhow::{anyhow, bail};
use async_trait::async_trait;
use axum::Router;
use cdk::amount::Amount;
use cdk::cdk_lightning::{
self, CreateInvoiceResponse, MintLightning, MintMeltSettings, PayInvoiceResponse,
PaymentQuoteResponse, Settings,
@@ -141,7 +142,7 @@ impl MintLightning for Strike {
Ok(PaymentQuoteResponse {
request_lookup_id: quote.payment_quote_id,
amount: from_strike_amount(quote.amount, &melt_quote_request.unit)?,
amount: from_strike_amount(quote.amount, &melt_quote_request.unit)?.into(),
fee,
})
}
@@ -149,8 +150,8 @@ impl MintLightning for Strike {
async fn pay_invoice(
&self,
melt_quote: mint::MeltQuote,
_partial_msats: Option<u64>,
_max_fee_msats: Option<u64>,
_partial_msats: Option<Amount>,
_max_fee_msats: Option<Amount>,
) -> Result<PayInvoiceResponse, Self::Err> {
let pay_response = self
.strike_api
@@ -164,7 +165,7 @@ impl MintLightning for Strike {
InvoiceState::Pending => MeltQuoteState::Pending,
};
let total_spent_msats = from_strike_amount(pay_response.total_amount, &melt_quote.unit)?;
let total_spent = from_strike_amount(pay_response.total_amount, &melt_quote.unit)?.into();
let bolt11: Bolt11Invoice = melt_quote.request.parse()?;
@@ -172,13 +173,14 @@ impl MintLightning for Strike {
payment_hash: bolt11.payment_hash().to_string(),
payment_preimage: None,
status: state,
total_spent_msats,
total_spent,
})
}
async fn create_invoice(
&self,
amount: u64,
amount: Amount,
_unit: &CurrencyUnit,
description: String,
unix_expiry: u64,
) -> Result<CreateInvoiceResponse, Self::Err> {

View File

@@ -49,7 +49,8 @@ pub trait MintLightning {
/// Create a new invoice
async fn create_invoice(
&self,
amount: u64,
amount: Amount,
unit: &CurrencyUnit,
description: String,
unix_expiry: u64,
) -> Result<CreateInvoiceResponse, Self::Err>;
@@ -65,8 +66,8 @@ pub trait MintLightning {
async fn pay_invoice(
&self,
melt_quote: mint::MeltQuote,
partial_msats: Option<u64>,
max_fee_msats: Option<u64>,
partial_amount: Option<Amount>,
max_fee_amount: Option<Amount>,
) -> Result<PayInvoiceResponse, Self::Err>;
/// Listen for invoices to be paid to the mint
@@ -102,8 +103,8 @@ pub struct PayInvoiceResponse {
pub payment_preimage: Option<String>,
/// Status
pub status: MeltQuoteState,
/// Totoal Amount Spent in msats
pub total_spent_msats: u64,
/// Totoal Amount Spent
pub total_spent: Amount,
}
/// Payment quote response
@@ -112,7 +113,7 @@ pub struct PaymentQuoteResponse {
/// Request look up id
pub request_lookup_id: String,
/// Amount
pub amount: u64,
pub amount: Amount,
/// Fee required for melt
pub fee: u64,
}
@@ -158,18 +159,18 @@ pub fn to_unit<T>(
amount: T,
current_unit: &CurrencyUnit,
target_unit: &CurrencyUnit,
) -> Result<u64, Error>
) -> Result<Amount, Error>
where
T: Into<u64>,
{
let amount = amount.into();
match (current_unit, target_unit) {
(CurrencyUnit::Sat, CurrencyUnit::Sat) => Ok(amount),
(CurrencyUnit::Msat, CurrencyUnit::Msat) => Ok(amount),
(CurrencyUnit::Sat, CurrencyUnit::Msat) => Ok(amount * MSAT_IN_SAT),
(CurrencyUnit::Msat, CurrencyUnit::Sat) => Ok(amount / MSAT_IN_SAT),
(CurrencyUnit::Usd, CurrencyUnit::Usd) => Ok(amount),
(CurrencyUnit::Eur, CurrencyUnit::Eur) => Ok(amount),
(CurrencyUnit::Sat, CurrencyUnit::Sat) => Ok(amount.into()),
(CurrencyUnit::Msat, CurrencyUnit::Msat) => Ok(amount.into()),
(CurrencyUnit::Sat, CurrencyUnit::Msat) => Ok((amount * MSAT_IN_SAT).into()),
(CurrencyUnit::Msat, CurrencyUnit::Sat) => Ok((amount / MSAT_IN_SAT).into()),
(CurrencyUnit::Usd, CurrencyUnit::Usd) => Ok(amount.into()),
(CurrencyUnit::Eur, CurrencyUnit::Eur) => Ok(amount.into()),
_ => Err(Error::CannotConvertUnits),
}
}