Browse Source

feat: use amount type in ln

thesimplekid 7 months ago
parent
commit
03e2b86022

+ 2 - 0
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]).

+ 24 - 25
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, &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)
         }
     };
 

+ 33 - 8
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<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,

+ 11 - 6
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<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))

+ 8 - 6
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<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> {

+ 14 - 13
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<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),
     }
 }