Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use amount type in ln #291

Merged
merged 1 commit into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]).
Expand Down
49 changes: 24 additions & 25 deletions crates/cdk-axum/src/router_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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 {
Expand All @@ -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,
Expand All @@ -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)
}
};

Expand Down
41 changes: 33 additions & 8 deletions crates/cdk-cln/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();

Expand All @@ -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;

Expand Down Expand Up @@ -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)?;
Expand All @@ -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,
)?,
}
}
_ => {
Expand All @@ -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> {
Expand All @@ -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,
Expand Down
17 changes: 11 additions & 6 deletions crates/cdk-fake-wallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();

Expand All @@ -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> {
Expand All @@ -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))
Expand Down
14 changes: 8 additions & 6 deletions crates/cdk-strike/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -141,16 +142,16 @@ 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,
})
}

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
Expand All @@ -164,21 +165,22 @@ 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()?;

Ok(PayInvoiceResponse {
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> {
Expand Down
27 changes: 14 additions & 13 deletions crates/cdk/src/cdk_lightning/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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,
}
Expand Down Expand Up @@ -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),
}
}
Loading