diff --git a/CHANGELOG.md b/CHANGELOG.md index f337054bf..d8a70dd8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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]). diff --git a/crates/cdk-axum/src/router_handlers.rs b/crates/cdk-axum/src/router_handlers.rs index 86ecbd625..effa8312f 100644 --- a/crates/cdk-axum/src/router_handlers.rs +++ b/crates/cdk-axum/src/router_handlers.rs @@ -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, "e.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, "e.unit) + .map_err(|_| into_response(Error::UnsupportedUnit))?, + ) + } false => None, }; - let max_fee = to_unit(quote.fee_reserve, "e.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, "e.unit) + .map_err(|_| into_response(Error::UnsupportedUnit))?, }; - let input_amount_msats = - to_unit(inputs_amount_quote_unit, "e.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, "e.unit) + let amount_spent = to_unit(pre.total_spent, &ln.get_settings().unit, "e.unit) .map_err(|_| into_response(Error::UnsupportedUnit))?; - (pre.payment_preimage, amount_spent.into()) + (pre.payment_preimage, amount_spent) } }; diff --git a/crates/cdk-cln/src/lib.rs b/crates/cdk-cln/src/lib.rs index 3a89dccd0..c2107c76a 100644 --- a/crates/cdk-cln/src/lib.rs +++ b/crates/cdk-cln/src/lib.rs @@ -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, - max_fee_msats: Option, + partial_amount: Option, + max_fee: Option, ) -> Result { 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_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_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 { @@ -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, diff --git a/crates/cdk-fake-wallet/src/lib.rs b/crates/cdk-fake-wallet/src/lib.rs index 0e6167975..174af9c7f 100644 --- a/crates/cdk-fake-wallet/src/lib.rs +++ b/crates/cdk-fake-wallet/src/lib.rs @@ -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, - _max_fee_msats: Option, + _partial_msats: Option, + _max_fee_msats: Option, ) -> Result { 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 { @@ -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)) diff --git a/crates/cdk-strike/src/lib.rs b/crates/cdk-strike/src/lib.rs index 1db80c4e7..787faeed1 100644 --- a/crates/cdk-strike/src/lib.rs +++ b/crates/cdk-strike/src/lib.rs @@ -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, - _max_fee_msats: Option, + _partial_msats: Option, + _max_fee_msats: Option, ) -> Result { 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 { diff --git a/crates/cdk/src/cdk_lightning/mod.rs b/crates/cdk/src/cdk_lightning/mod.rs index 8cf7e4808..53a5d37b1 100644 --- a/crates/cdk/src/cdk_lightning/mod.rs +++ b/crates/cdk/src/cdk_lightning/mod.rs @@ -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; @@ -65,8 +66,8 @@ pub trait MintLightning { async fn pay_invoice( &self, melt_quote: mint::MeltQuote, - partial_msats: Option, - max_fee_msats: Option, + partial_amount: Option, + max_fee_amount: Option, ) -> Result; /// Listen for invoices to be paid to the mint @@ -102,8 +103,8 @@ pub struct PayInvoiceResponse { pub payment_preimage: Option, /// 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( amount: T, current_unit: &CurrencyUnit, target_unit: &CurrencyUnit, -) -> Result +) -> Result where T: Into, { 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), } }