mirror of
https://github.com/aljazceru/cdk.git
synced 2026-01-06 06:26:11 +01:00
feat: support custom unit
This commit is contained in:
@@ -33,7 +33,7 @@ impl JsMeltQuote {
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn unit(&self) -> JsCurrencyUnit {
|
||||
self.inner.unit.into()
|
||||
self.inner.unit.clone().into()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
|
||||
@@ -33,7 +33,7 @@ impl JsMintQuote {
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn unit(&self) -> JsCurrencyUnit {
|
||||
self.inner.unit.into()
|
||||
self.inner.unit.clone().into()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
|
||||
@@ -21,7 +21,7 @@ pub async fn pay_request(
|
||||
) -> Result<()> {
|
||||
let payment_request = &sub_command_args.payment_request;
|
||||
|
||||
let unit = payment_request.unit;
|
||||
let unit = &payment_request.unit;
|
||||
|
||||
let amount = match payment_request.amount {
|
||||
Some(amount) => amount,
|
||||
@@ -56,7 +56,7 @@ pub async fn pay_request(
|
||||
}
|
||||
|
||||
if let Some(unit) = unit {
|
||||
if wallet.unit != unit {
|
||||
if &wallet.unit != unit {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ pub async fn pay_request(
|
||||
id: payment_request.payment_id.clone(),
|
||||
memo: None,
|
||||
mint: matching_wallet.mint_url.clone(),
|
||||
unit: matching_wallet.unit,
|
||||
unit: matching_wallet.unit.clone(),
|
||||
proofs,
|
||||
};
|
||||
|
||||
|
||||
@@ -81,8 +81,8 @@ impl MintLightning for Cln {
|
||||
Settings {
|
||||
mpp: true,
|
||||
unit: CurrencyUnit::Msat,
|
||||
mint_settings: self.mint_settings,
|
||||
melt_settings: self.melt_settings,
|
||||
mint_settings: self.mint_settings.clone(),
|
||||
melt_settings: self.melt_settings.clone(),
|
||||
invoice_description: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ impl MintLightning for FakeWallet {
|
||||
Settings {
|
||||
mpp: true,
|
||||
unit: CurrencyUnit::Msat,
|
||||
mint_settings: self.mint_settings,
|
||||
melt_settings: self.melt_settings,
|
||||
mint_settings: self.mint_settings.clone(),
|
||||
melt_settings: self.melt_settings.clone(),
|
||||
invoice_description: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +176,7 @@ where
|
||||
Arc::new(database),
|
||||
ln_backends,
|
||||
supported_units,
|
||||
HashMap::new(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ pub async fn start_mint(
|
||||
Arc::new(MintMemoryDatabase::default()),
|
||||
ln_backends.clone(),
|
||||
supported_units,
|
||||
HashMap::new(),
|
||||
)
|
||||
.await?;
|
||||
let cache_time_to_live = 3600;
|
||||
|
||||
@@ -368,16 +368,12 @@ async fn test_fake_melt_change_in_quote() -> Result<()> {
|
||||
assert!(melt_response.change.is_some());
|
||||
|
||||
let check = wallet.melt_quote_status(&melt_quote.id).await?;
|
||||
let mut melt_change = melt_response.change.unwrap();
|
||||
melt_change.sort_by(|a, b| a.amount.cmp(&b.amount));
|
||||
|
||||
assert_eq!(
|
||||
melt_response
|
||||
.change
|
||||
.unwrap()
|
||||
.sort_by(|a, b| a.amount.cmp(&b.amount)),
|
||||
check
|
||||
.change
|
||||
.unwrap()
|
||||
.sort_by(|a, b| a.amount.cmp(&b.amount))
|
||||
);
|
||||
let mut check = check.change.unwrap();
|
||||
check.sort_by(|a, b| a.amount.cmp(&b.amount));
|
||||
|
||||
assert_eq!(melt_change, check);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ async fn new_mint(fee: u64) -> Mint {
|
||||
Arc::new(MintMemoryDatabase::default()),
|
||||
HashMap::new(),
|
||||
supported_units,
|
||||
HashMap::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
@@ -270,7 +271,8 @@ async fn test_swap_unbalanced() -> Result<()> {
|
||||
async fn test_swap_overpay_underpay_fee() -> Result<()> {
|
||||
let mint = new_mint(1).await;
|
||||
|
||||
mint.rotate_keyset(CurrencyUnit::Sat, 1, 32, 1).await?;
|
||||
mint.rotate_keyset(CurrencyUnit::Sat, 1, 32, 1, HashMap::new())
|
||||
.await?;
|
||||
|
||||
let keys = mint.pubkeys().await?.keysets.first().unwrap().clone().keys;
|
||||
let keyset_id = Id::from(&keys);
|
||||
|
||||
@@ -80,8 +80,8 @@ impl MintLightning for LNbits {
|
||||
Settings {
|
||||
mpp: false,
|
||||
unit: CurrencyUnit::Sat,
|
||||
mint_settings: self.mint_settings,
|
||||
melt_settings: self.melt_settings,
|
||||
mint_settings: self.mint_settings.clone(),
|
||||
melt_settings: self.melt_settings.clone(),
|
||||
invoice_description: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ impl MintLightning for Lnd {
|
||||
Settings {
|
||||
mpp: true,
|
||||
unit: CurrencyUnit::Msat,
|
||||
mint_settings: self.mint_settings,
|
||||
melt_settings: self.melt_settings,
|
||||
mint_settings: self.mint_settings.clone(),
|
||||
melt_settings: self.melt_settings.clone(),
|
||||
invoice_description: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
api_key.clone(),
|
||||
MintMethodSettings::default(),
|
||||
MeltMethodSettings::default(),
|
||||
unit,
|
||||
unit.clone(),
|
||||
Arc::new(Mutex::new(Some(receiver))),
|
||||
webhook_url.to_string(),
|
||||
)
|
||||
@@ -199,7 +199,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
.await?;
|
||||
routers.push(router);
|
||||
|
||||
let ln_key = LnKey::new(unit, PaymentMethod::Bolt11);
|
||||
let ln_key = LnKey::new(unit.clone(), PaymentMethod::Bolt11);
|
||||
|
||||
ln_backends.insert(ln_key, Arc::new(strike));
|
||||
|
||||
@@ -237,7 +237,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
let unit = CurrencyUnit::Sat;
|
||||
|
||||
let ln_key = LnKey::new(unit, PaymentMethod::Bolt11);
|
||||
let ln_key = LnKey::new(unit.clone(), PaymentMethod::Bolt11);
|
||||
|
||||
ln_backends.insert(ln_key, Arc::new(lnbits));
|
||||
|
||||
@@ -326,7 +326,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
let units = settings.fake_wallet.unwrap_or_default().supported_units;
|
||||
|
||||
for unit in units {
|
||||
let ln_key = LnKey::new(unit, PaymentMethod::Bolt11);
|
||||
let ln_key = LnKey::new(unit.clone(), PaymentMethod::Bolt11);
|
||||
|
||||
let wallet = Arc::new(FakeWallet::new(
|
||||
fee_reserve.clone(),
|
||||
@@ -361,13 +361,13 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
let m = MppMethodSettings {
|
||||
method: key.method,
|
||||
unit: key.unit,
|
||||
unit: key.unit.clone(),
|
||||
mpp: settings.mpp,
|
||||
};
|
||||
|
||||
let n4 = MintMethodSettings {
|
||||
method: key.method,
|
||||
unit: key.unit,
|
||||
unit: key.unit.clone(),
|
||||
min_amount: settings.mint_settings.min_amount,
|
||||
max_amount: settings.mint_settings.max_amount,
|
||||
description: settings.invoice_description,
|
||||
@@ -375,7 +375,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
let n5 = MeltMethodSettings {
|
||||
method: key.method,
|
||||
unit: key.unit,
|
||||
unit: key.unit.clone(),
|
||||
min_amount: settings.melt_settings.min_amount,
|
||||
max_amount: settings.melt_settings.max_amount,
|
||||
};
|
||||
@@ -438,6 +438,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
localstore,
|
||||
ln_backends.clone(),
|
||||
supported_units,
|
||||
HashMap::new(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -86,8 +86,8 @@ impl MintLightning for Phoenixd {
|
||||
Settings {
|
||||
mpp: false,
|
||||
unit: CurrencyUnit::Sat,
|
||||
mint_settings: self.mint_settings,
|
||||
melt_settings: self.melt_settings,
|
||||
mint_settings: self.mint_settings.clone(),
|
||||
melt_settings: self.melt_settings.clone(),
|
||||
invoice_description: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,9 +77,9 @@ impl MintLightning for Strike {
|
||||
fn get_settings(&self) -> Settings {
|
||||
Settings {
|
||||
mpp: false,
|
||||
unit: self.unit,
|
||||
mint_settings: self.mint_settings,
|
||||
melt_settings: self.melt_settings,
|
||||
unit: self.unit.clone(),
|
||||
mint_settings: self.mint_settings.clone(),
|
||||
melt_settings: self.melt_settings.clone(),
|
||||
invoice_description: true,
|
||||
}
|
||||
}
|
||||
@@ -288,7 +288,7 @@ impl MintLightning for Strike {
|
||||
payment_preimage: None,
|
||||
status: state,
|
||||
total_spent: from_strike_amount(invoice.total_amount, &self.unit)?.into(),
|
||||
unit: self.unit,
|
||||
unit: self.unit.clone(),
|
||||
}
|
||||
}
|
||||
Err(err) => match err {
|
||||
@@ -297,7 +297,7 @@ impl MintLightning for Strike {
|
||||
payment_preimage: None,
|
||||
status: MeltQuoteState::Unknown,
|
||||
total_spent: Amount::ZERO,
|
||||
unit: self.unit,
|
||||
unit: self.unit.clone(),
|
||||
},
|
||||
_ => {
|
||||
return Err(Error::from(err).into());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use bitcoin::bip32::DerivationPath;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::Error;
|
||||
@@ -89,14 +90,20 @@ impl Mint {
|
||||
derivation_path_index: u32,
|
||||
max_order: u8,
|
||||
input_fee_ppk: u64,
|
||||
custom_paths: HashMap<CurrencyUnit, DerivationPath>,
|
||||
) -> Result<(), Error> {
|
||||
let derivation_path = derivation_path_from_unit(unit, derivation_path_index);
|
||||
let derivation_path = match custom_paths.get(&unit) {
|
||||
Some(path) => path.clone(),
|
||||
None => derivation_path_from_unit(unit.clone(), derivation_path_index)
|
||||
.ok_or(Error::UnsupportedUnit)?,
|
||||
};
|
||||
|
||||
let (keyset, keyset_info) = create_new_keyset(
|
||||
&self.secp_ctx,
|
||||
self.xpriv,
|
||||
derivation_path,
|
||||
Some(derivation_path_index),
|
||||
unit,
|
||||
unit.clone(),
|
||||
max_order,
|
||||
input_fee_ppk,
|
||||
);
|
||||
|
||||
@@ -74,11 +74,11 @@ impl Mint {
|
||||
}
|
||||
};
|
||||
|
||||
self.check_melt_request_acceptable(amount, *unit, PaymentMethod::Bolt11)?;
|
||||
self.check_melt_request_acceptable(amount, unit.clone(), PaymentMethod::Bolt11)?;
|
||||
|
||||
let ln = self
|
||||
.ln
|
||||
.get(&LnKey::new(*unit, PaymentMethod::Bolt11))
|
||||
.get(&LnKey::new(unit.clone(), PaymentMethod::Bolt11))
|
||||
.ok_or_else(|| {
|
||||
tracing::info!("Could not get ln backend for {}, bolt11 ", unit);
|
||||
|
||||
@@ -97,7 +97,7 @@ impl Mint {
|
||||
|
||||
let quote = MeltQuote::new(
|
||||
request.to_string(),
|
||||
*unit,
|
||||
unit.clone(),
|
||||
payment_quote.amount,
|
||||
payment_quote.fee,
|
||||
unix_time() + self.quote_ttl.melt_ttl,
|
||||
@@ -447,7 +447,10 @@ impl Mint {
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let ln = match self.ln.get(&LnKey::new(quote.unit, PaymentMethod::Bolt11)) {
|
||||
let ln = match self
|
||||
.ln
|
||||
.get(&LnKey::new(quote.unit.clone(), PaymentMethod::Bolt11))
|
||||
{
|
||||
Some(ln) => ln,
|
||||
None => {
|
||||
tracing::info!("Could not get ln backend for {}, bolt11 ", quote.unit);
|
||||
|
||||
@@ -12,7 +12,7 @@ impl Mint {
|
||||
fn check_mint_request_acceptable(
|
||||
&self,
|
||||
amount: Amount,
|
||||
unit: CurrencyUnit,
|
||||
unit: &CurrencyUnit,
|
||||
) -> Result<(), Error> {
|
||||
let nut04 = &self.mint_info.nuts.nut04;
|
||||
|
||||
@@ -20,7 +20,7 @@ impl Mint {
|
||||
return Err(Error::MintingDisabled);
|
||||
}
|
||||
|
||||
match nut04.get_settings(&unit, &PaymentMethod::Bolt11) {
|
||||
match nut04.get_settings(unit, &PaymentMethod::Bolt11) {
|
||||
Some(settings) => {
|
||||
if settings
|
||||
.max_amount
|
||||
@@ -64,11 +64,11 @@ impl Mint {
|
||||
description,
|
||||
} = mint_quote_request;
|
||||
|
||||
self.check_mint_request_acceptable(amount, unit)?;
|
||||
self.check_mint_request_acceptable(amount, &unit)?;
|
||||
|
||||
let ln = self
|
||||
.ln
|
||||
.get(&LnKey::new(unit, PaymentMethod::Bolt11))
|
||||
.get(&LnKey::new(unit.clone(), PaymentMethod::Bolt11))
|
||||
.ok_or_else(|| {
|
||||
tracing::info!("Bolt11 mint request for unsupported unit");
|
||||
|
||||
@@ -98,7 +98,7 @@ impl Mint {
|
||||
let quote = MintQuote::new(
|
||||
self.mint_url.clone(),
|
||||
create_invoice_response.request.to_string(),
|
||||
unit,
|
||||
unit.clone(),
|
||||
amount,
|
||||
create_invoice_response.expiry.unwrap_or(0),
|
||||
create_invoice_response.request_lookup_id.clone(),
|
||||
|
||||
@@ -54,6 +54,7 @@ pub struct Mint {
|
||||
|
||||
impl Mint {
|
||||
/// Create new [`Mint`]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn new(
|
||||
mint_url: &str,
|
||||
seed: &[u8],
|
||||
@@ -63,6 +64,7 @@ impl Mint {
|
||||
ln: HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>,
|
||||
// Hashmap where the key is the unit and value is (input fee ppk, max_order)
|
||||
supported_units: HashMap<CurrencyUnit, (u64, u8)>,
|
||||
custom_paths: HashMap<CurrencyUnit, DerivationPath>,
|
||||
) -> Result<Self, Error> {
|
||||
let secp_ctx = Secp256k1::new();
|
||||
let xpriv = Xpriv::new_master(bitcoin::Network::Bitcoin, seed).expect("RNG busted");
|
||||
@@ -83,7 +85,7 @@ impl Mint {
|
||||
|
||||
let keysets_by_unit: HashMap<CurrencyUnit, Vec<MintKeySetInfo>> =
|
||||
keysets_infos.iter().fold(HashMap::new(), |mut acc, ks| {
|
||||
acc.entry(ks.unit).or_default().push(ks.clone());
|
||||
acc.entry(ks.unit.clone()).or_default().push(ks.clone());
|
||||
acc
|
||||
});
|
||||
|
||||
@@ -112,7 +114,7 @@ impl Mint {
|
||||
&secp_ctx,
|
||||
xpriv,
|
||||
highest_index_keyset.max_order,
|
||||
highest_index_keyset.unit,
|
||||
highest_index_keyset.unit.clone(),
|
||||
highest_index_keyset.derivation_path.clone(),
|
||||
);
|
||||
active_keysets.insert(id, keyset);
|
||||
@@ -125,37 +127,46 @@ impl Mint {
|
||||
highest_index_keyset.derivation_path_index.unwrap_or(0) + 1
|
||||
};
|
||||
|
||||
let derivation_path = derivation_path_from_unit(unit, derivation_path_index);
|
||||
let derivation_path = match custom_paths.get(&unit) {
|
||||
Some(path) => path.clone(),
|
||||
None => derivation_path_from_unit(unit.clone(), derivation_path_index)
|
||||
.ok_or(Error::UnsupportedUnit)?,
|
||||
};
|
||||
|
||||
let (keyset, keyset_info) = create_new_keyset(
|
||||
&secp_ctx,
|
||||
xpriv,
|
||||
derivation_path,
|
||||
Some(derivation_path_index),
|
||||
unit,
|
||||
unit.clone(),
|
||||
*max_order,
|
||||
*input_fee_ppk,
|
||||
);
|
||||
|
||||
let id = keyset_info.id;
|
||||
localstore.add_keyset_info(keyset_info).await?;
|
||||
localstore.set_active_keyset(unit, id).await?;
|
||||
localstore.set_active_keyset(unit.clone(), id).await?;
|
||||
active_keysets.insert(id, keyset);
|
||||
active_keyset_units.push(unit);
|
||||
active_keyset_units.push(unit.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unit, (fee, max_order)) in supported_units {
|
||||
if !active_keyset_units.contains(&unit) {
|
||||
let derivation_path = derivation_path_from_unit(unit, 0);
|
||||
let derivation_path = match custom_paths.get(&unit) {
|
||||
Some(path) => path.clone(),
|
||||
None => {
|
||||
derivation_path_from_unit(unit.clone(), 0).ok_or(Error::UnsupportedUnit)?
|
||||
}
|
||||
};
|
||||
|
||||
let (keyset, keyset_info) = create_new_keyset(
|
||||
&secp_ctx,
|
||||
xpriv,
|
||||
derivation_path,
|
||||
Some(0),
|
||||
unit,
|
||||
unit.clone(),
|
||||
max_order,
|
||||
fee,
|
||||
);
|
||||
@@ -194,7 +205,7 @@ impl Mint {
|
||||
let mint = Arc::clone(&mint_arc);
|
||||
let ln = Arc::clone(ln);
|
||||
let shutdown = Arc::clone(&shutdown);
|
||||
let key = *key;
|
||||
let key = key.clone();
|
||||
join_set.spawn(async move {
|
||||
if !ln.is_wait_invoice_active() {
|
||||
loop {
|
||||
@@ -438,7 +449,8 @@ impl Mint {
|
||||
|
||||
Ok(RestoreResponse {
|
||||
outputs,
|
||||
signatures,
|
||||
signatures: signatures.clone(),
|
||||
promises: Some(signatures),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -559,7 +571,7 @@ fn create_new_keyset<C: secp256k1::Signing>(
|
||||
);
|
||||
let keyset_info = MintKeySetInfo {
|
||||
id: keyset.id,
|
||||
unit: keyset.unit,
|
||||
unit: keyset.unit.clone(),
|
||||
active: true,
|
||||
valid_from: unix_time(),
|
||||
valid_to: None,
|
||||
@@ -571,12 +583,17 @@ fn create_new_keyset<C: secp256k1::Signing>(
|
||||
(keyset, keyset_info)
|
||||
}
|
||||
|
||||
fn derivation_path_from_unit(unit: CurrencyUnit, index: u32) -> DerivationPath {
|
||||
DerivationPath::from(vec![
|
||||
fn derivation_path_from_unit(unit: CurrencyUnit, index: u32) -> Option<DerivationPath> {
|
||||
let unit_index = match unit.derivation_index() {
|
||||
Some(index) => index,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
Some(DerivationPath::from(vec![
|
||||
ChildNumber::from_hardened_idx(0).expect("0 is a valid index"),
|
||||
ChildNumber::from_hardened_idx(unit.derivation_index()).expect("0 is a valid index"),
|
||||
ChildNumber::from_hardened_idx(unit_index).expect("0 is a valid index"),
|
||||
ChildNumber::from_hardened_idx(index).expect("0 is a valid index"),
|
||||
])
|
||||
]))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -598,7 +615,7 @@ mod tests {
|
||||
seed,
|
||||
2,
|
||||
CurrencyUnit::Sat,
|
||||
derivation_path_from_unit(CurrencyUnit::Sat, 0),
|
||||
derivation_path_from_unit(CurrencyUnit::Sat, 0).unwrap(),
|
||||
);
|
||||
|
||||
assert_eq!(keyset.unit, CurrencyUnit::Sat);
|
||||
@@ -642,7 +659,7 @@ mod tests {
|
||||
xpriv,
|
||||
2,
|
||||
CurrencyUnit::Sat,
|
||||
derivation_path_from_unit(CurrencyUnit::Sat, 0),
|
||||
derivation_path_from_unit(CurrencyUnit::Sat, 0).unwrap(),
|
||||
);
|
||||
|
||||
assert_eq!(keyset.unit, CurrencyUnit::Sat);
|
||||
@@ -722,6 +739,7 @@ mod tests {
|
||||
localstore,
|
||||
HashMap::new(),
|
||||
config.supported_units,
|
||||
HashMap::new(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -777,7 +795,8 @@ mod tests {
|
||||
assert!(keysets.keysets.is_empty());
|
||||
|
||||
// generate the first keyset and set it to active
|
||||
mint.rotate_keyset(CurrencyUnit::default(), 0, 1, 1).await?;
|
||||
mint.rotate_keyset(CurrencyUnit::default(), 0, 1, 1, HashMap::new())
|
||||
.await?;
|
||||
|
||||
let keysets = mint.keysets().await.unwrap();
|
||||
assert!(keysets.keysets.len().eq(&1));
|
||||
@@ -785,7 +804,8 @@ mod tests {
|
||||
let first_keyset_id = keysets.keysets[0].id;
|
||||
|
||||
// set the first keyset to inactive and generate a new keyset
|
||||
mint.rotate_keyset(CurrencyUnit::default(), 1, 1, 1).await?;
|
||||
mint.rotate_keyset(CurrencyUnit::default(), 1, 1, 1, HashMap::new())
|
||||
.await?;
|
||||
|
||||
let keysets = mint.keysets().await.unwrap();
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ where
|
||||
|
||||
/// Currency Unit
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
|
||||
#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
|
||||
pub enum CurrencyUnit {
|
||||
/// Sat
|
||||
@@ -373,17 +373,20 @@ pub enum CurrencyUnit {
|
||||
Usd,
|
||||
/// Euro
|
||||
Eur,
|
||||
/// Custom currency unit
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl CurrencyUnit {
|
||||
/// Derivation index mint will use for unit
|
||||
pub fn derivation_index(&self) -> u32 {
|
||||
pub fn derivation_index(&self) -> Option<u32> {
|
||||
match self {
|
||||
Self::Sat => 0,
|
||||
Self::Msat => 1,
|
||||
Self::Usd => 2,
|
||||
Self::Eur => 3,
|
||||
Self::Sat => Some(0),
|
||||
Self::Msat => Some(1),
|
||||
Self::Usd => Some(2),
|
||||
Self::Eur => Some(3),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,12 +394,13 @@ impl CurrencyUnit {
|
||||
impl FromStr for CurrencyUnit {
|
||||
type Err = Error;
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
match value {
|
||||
"sat" => Ok(Self::Sat),
|
||||
"msat" => Ok(Self::Msat),
|
||||
"usd" => Ok(Self::Usd),
|
||||
"eur" => Ok(Self::Eur),
|
||||
_ => Err(Error::UnsupportedUnit),
|
||||
let value = &value.to_uppercase();
|
||||
match value.as_str() {
|
||||
"SAT" => Ok(Self::Sat),
|
||||
"MSAT" => Ok(Self::Msat),
|
||||
"USD" => Ok(Self::Usd),
|
||||
"EUR" => Ok(Self::Eur),
|
||||
c => Ok(Self::Custom(c.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,15 +408,16 @@ impl FromStr for CurrencyUnit {
|
||||
impl fmt::Display for CurrencyUnit {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s = match self {
|
||||
CurrencyUnit::Sat => "sat",
|
||||
CurrencyUnit::Msat => "msat",
|
||||
CurrencyUnit::Usd => "usd",
|
||||
CurrencyUnit::Eur => "eur",
|
||||
CurrencyUnit::Sat => "SAT",
|
||||
CurrencyUnit::Msat => "MSAT",
|
||||
CurrencyUnit::Usd => "USD",
|
||||
CurrencyUnit::Eur => "EUR",
|
||||
CurrencyUnit::Custom(unit) => unit,
|
||||
};
|
||||
if let Some(width) = f.width() {
|
||||
write!(f, "{:width$}", s, width = width)
|
||||
write!(f, "{:width$}", s.to_lowercase(), width = width)
|
||||
} else {
|
||||
write!(f, "{}", s)
|
||||
write!(f, "{}", s.to_lowercase())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,8 +92,8 @@ impl Token {
|
||||
/// Unit
|
||||
pub fn unit(&self) -> Option<CurrencyUnit> {
|
||||
match self {
|
||||
Self::TokenV3(token) => *token.unit(),
|
||||
Self::TokenV4(token) => Some(token.unit()),
|
||||
Self::TokenV3(token) => token.unit().clone(),
|
||||
Self::TokenV4(token) => Some(token.unit().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,8 +326,8 @@ impl TokenV4 {
|
||||
|
||||
/// Unit
|
||||
#[inline]
|
||||
pub fn unit(&self) -> CurrencyUnit {
|
||||
self.unit
|
||||
pub fn unit(&self) -> &CurrencyUnit {
|
||||
&self.unit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,7 +525,7 @@ mod tests {
|
||||
token.token[0].proofs[0].clone().keyset_id,
|
||||
Id::from_str("009a1f293253e41e").unwrap()
|
||||
);
|
||||
assert_eq!(token.unit.unwrap(), CurrencyUnit::Sat);
|
||||
assert_eq!(token.unit.clone().unwrap(), CurrencyUnit::Sat);
|
||||
|
||||
let encoded = &token.to_string();
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ pub struct MintBolt11Response {
|
||||
}
|
||||
|
||||
/// Mint Method Settings
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
|
||||
pub struct MintMethodSettings {
|
||||
/// Payment Method e.g. bolt11
|
||||
@@ -179,7 +179,7 @@ impl Settings {
|
||||
) -> Option<MintMethodSettings> {
|
||||
for method_settings in self.methods.iter() {
|
||||
if method_settings.method.eq(method) && method_settings.unit.eq(unit) {
|
||||
return Some(*method_settings);
|
||||
return Some(method_settings.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ impl MeltBolt11Request {
|
||||
}
|
||||
|
||||
/// Melt Method Settings
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
|
||||
pub struct MeltMethodSettings {
|
||||
/// Payment Method e.g. bolt11
|
||||
@@ -264,7 +264,7 @@ impl Settings {
|
||||
) -> Option<MeltMethodSettings> {
|
||||
for method_settings in self.methods.iter() {
|
||||
if method_settings.method.eq(method) && method_settings.unit.eq(unit) {
|
||||
return Some(*method_settings);
|
||||
return Some(method_settings.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ pub struct RestoreResponse {
|
||||
pub outputs: Vec<BlindedMessage>,
|
||||
/// Signatures
|
||||
pub signatures: Vec<BlindSignature>,
|
||||
/// Promises
|
||||
// Temp compatibility with cashu-ts
|
||||
pub promises: Option<Vec<BlindSignature>>,
|
||||
}
|
||||
|
||||
mod test {
|
||||
|
||||
@@ -154,7 +154,7 @@ mod tests {
|
||||
|
||||
assert_eq!(&req.payment_id.unwrap(), "b7a90176");
|
||||
assert_eq!(req.amount.unwrap(), 10.into());
|
||||
assert_eq!(req.unit.unwrap(), CurrencyUnit::Sat);
|
||||
assert_eq!(req.unit.clone().unwrap(), CurrencyUnit::Sat);
|
||||
assert_eq!(
|
||||
req.mints.unwrap(),
|
||||
vec![MintUrl::from_str("https://nofees.testnut.cashu.space")?]
|
||||
@@ -190,7 +190,7 @@ mod tests {
|
||||
|
||||
assert_eq!(&req.payment_id.unwrap(), "b7a90176");
|
||||
assert_eq!(req.amount.unwrap(), 10.into());
|
||||
assert_eq!(req.unit.unwrap(), CurrencyUnit::Sat);
|
||||
assert_eq!(req.unit.clone().unwrap(), CurrencyUnit::Sat);
|
||||
assert_eq!(
|
||||
req.mints.unwrap(),
|
||||
vec![MintUrl::from_str("https://nofees.testnut.cashu.space")?]
|
||||
|
||||
@@ -141,7 +141,7 @@ impl ProofInfo {
|
||||
|
||||
/// Key used in hashmap of ln backends to identify what unit and payment method
|
||||
/// it is for
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct LnKey {
|
||||
/// Unit of Payment backend
|
||||
pub unit: CurrencyUnit,
|
||||
|
||||
@@ -19,7 +19,7 @@ impl Wallet {
|
||||
|
||||
// TODO If only the proofs for this wallet's unit are retrieved, why build a map with key = unit?
|
||||
let balances = proofs.iter().fold(HashMap::new(), |mut acc, proof| {
|
||||
*acc.entry(self.unit).or_insert(Amount::ZERO) += proof.amount;
|
||||
*acc.entry(self.unit.clone()).or_insert(Amount::ZERO) += proof.amount;
|
||||
acc
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ impl Wallet {
|
||||
|
||||
// TODO If only the proofs for this wallet's unit are retrieved, why build a map with key = unit?
|
||||
let balances = proofs.iter().fold(HashMap::new(), |mut acc, proof| {
|
||||
*acc.entry(self.unit).or_insert(Amount::ZERO) += proof.amount;
|
||||
*acc.entry(self.unit.clone()).or_insert(Amount::ZERO) += proof.amount;
|
||||
acc
|
||||
});
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ impl Wallet {
|
||||
|
||||
let quote_request = MeltQuoteBolt11Request {
|
||||
request: Bolt11Invoice::from_str(&request)?,
|
||||
unit: self.unit,
|
||||
unit: self.unit.clone(),
|
||||
options,
|
||||
};
|
||||
|
||||
@@ -79,7 +79,7 @@ impl Wallet {
|
||||
id: quote_res.quote,
|
||||
amount,
|
||||
request,
|
||||
unit: self.unit,
|
||||
unit: self.unit.clone(),
|
||||
fee_reserve: quote_res.fee_reserve,
|
||||
state: quote_res.state,
|
||||
expiry: quote_res.expiry,
|
||||
@@ -233,7 +233,7 @@ impl Wallet {
|
||||
proof,
|
||||
self.mint_url.clone(),
|
||||
State::Unspent,
|
||||
quote_info.unit,
|
||||
quote_info.unit.clone(),
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<ProofInfo>, _>>()?
|
||||
|
||||
@@ -46,7 +46,7 @@ impl Wallet {
|
||||
description: Option<String>,
|
||||
) -> Result<MintQuote, Error> {
|
||||
let mint_url = self.mint_url.clone();
|
||||
let unit = self.unit;
|
||||
let unit = self.unit.clone();
|
||||
|
||||
// If we have a description, we check that the mint supports it.
|
||||
if description.is_some() {
|
||||
@@ -67,7 +67,7 @@ impl Wallet {
|
||||
|
||||
let request = MintQuoteBolt11Request {
|
||||
amount,
|
||||
unit,
|
||||
unit: unit.clone(),
|
||||
description,
|
||||
};
|
||||
|
||||
@@ -80,7 +80,7 @@ impl Wallet {
|
||||
mint_url,
|
||||
id: quote_res.quote.clone(),
|
||||
amount,
|
||||
unit,
|
||||
unit: unit.clone(),
|
||||
request: quote_res.request,
|
||||
state: quote_res.state,
|
||||
expiry: quote_res.expiry.unwrap_or(0),
|
||||
@@ -269,7 +269,7 @@ impl Wallet {
|
||||
proof,
|
||||
self.mint_url.clone(),
|
||||
State::Unspent,
|
||||
quote_info.unit,
|
||||
quote_info.unit.clone(),
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<ProofInfo>, _>>()?;
|
||||
|
||||
@@ -330,7 +330,12 @@ impl Wallet {
|
||||
let unspent_proofs = unspent_proofs
|
||||
.into_iter()
|
||||
.map(|proof| {
|
||||
ProofInfo::new(proof, self.mint_url.clone(), State::Unspent, keyset.unit)
|
||||
ProofInfo::new(
|
||||
proof,
|
||||
self.mint_url.clone(),
|
||||
State::Unspent,
|
||||
keyset.unit.clone(),
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<ProofInfo>, _>>()?;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ impl MultiMintWallet {
|
||||
wallets: Arc::new(Mutex::new(
|
||||
wallets
|
||||
.into_iter()
|
||||
.map(|w| (WalletKey::new(w.mint_url.clone(), w.unit), w))
|
||||
.map(|w| (WalletKey::new(w.mint_url.clone(), w.unit.clone()), w))
|
||||
.collect(),
|
||||
)),
|
||||
}
|
||||
@@ -64,7 +64,7 @@ impl MultiMintWallet {
|
||||
/// Add wallet to MultiMintWallet
|
||||
#[instrument(skip(self, wallet))]
|
||||
pub async fn add_wallet(&self, wallet: Wallet) {
|
||||
let wallet_key = WalletKey::new(wallet.mint_url.clone(), wallet.unit);
|
||||
let wallet_key = WalletKey::new(wallet.mint_url.clone(), wallet.unit.clone());
|
||||
|
||||
let mut wallets = self.wallets.lock().await;
|
||||
|
||||
@@ -126,7 +126,7 @@ impl MultiMintWallet {
|
||||
|
||||
for (WalletKey { mint_url, unit: u }, wallet) in self.wallets.lock().await.iter() {
|
||||
let wallet_proofs = wallet.get_unspent_proofs().await?;
|
||||
mint_proofs.insert(mint_url.clone(), (wallet_proofs, *u));
|
||||
mint_proofs.insert(mint_url.clone(), (wallet_proofs, u.clone()));
|
||||
}
|
||||
Ok(mint_proofs)
|
||||
}
|
||||
@@ -198,7 +198,7 @@ impl MultiMintWallet {
|
||||
let amount = wallet.check_all_mint_quotes().await?;
|
||||
|
||||
amount_minted
|
||||
.entry(wallet.unit)
|
||||
.entry(wallet.unit.clone())
|
||||
.and_modify(|b| *b += amount)
|
||||
.or_insert(amount);
|
||||
}
|
||||
@@ -246,7 +246,7 @@ impl MultiMintWallet {
|
||||
let mint_url = token_data.mint_url()?;
|
||||
|
||||
// Check that all mints in tokes have wallets
|
||||
let wallet_key = WalletKey::new(mint_url.clone(), unit);
|
||||
let wallet_key = WalletKey::new(mint_url.clone(), unit.clone());
|
||||
if !self.has(&wallet_key).await {
|
||||
return Err(Error::UnknownWallet(wallet_key.clone()));
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ impl Wallet {
|
||||
.localstore
|
||||
.get_proofs(
|
||||
Some(self.mint_url.clone()),
|
||||
Some(self.unit),
|
||||
Some(self.unit.clone()),
|
||||
state,
|
||||
spending_conditions,
|
||||
)
|
||||
@@ -115,7 +115,7 @@ impl Wallet {
|
||||
.localstore
|
||||
.get_proofs(
|
||||
Some(self.mint_url.clone()),
|
||||
Some(self.unit),
|
||||
Some(self.unit.clone()),
|
||||
Some(vec![State::Pending, State::Reserved]),
|
||||
None,
|
||||
)
|
||||
|
||||
@@ -111,7 +111,7 @@ impl Wallet {
|
||||
let proofs_info = proofs
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|p| ProofInfo::new(p, self.mint_url.clone(), State::Pending, self.unit))
|
||||
.map(|p| ProofInfo::new(p, self.mint_url.clone(), State::Pending, self.unit.clone()))
|
||||
.collect::<Result<Vec<ProofInfo>, _>>()?;
|
||||
self.localstore
|
||||
.update_proofs(proofs_info.clone(), vec![])
|
||||
@@ -150,7 +150,7 @@ impl Wallet {
|
||||
|
||||
let recv_proof_infos = recv_proofs
|
||||
.into_iter()
|
||||
.map(|proof| ProofInfo::new(proof, mint_url.clone(), State::Unspent, self.unit))
|
||||
.map(|proof| ProofInfo::new(proof, mint_url.clone(), State::Unspent, self.unit.clone()))
|
||||
.collect::<Result<Vec<ProofInfo>, _>>()?;
|
||||
self.localstore
|
||||
.update_proofs(
|
||||
|
||||
@@ -16,7 +16,12 @@ impl Wallet {
|
||||
let ys = proofs.ys()?;
|
||||
self.localstore.reserve_proofs(ys).await?;
|
||||
|
||||
Ok(Token::new(self.mint_url.clone(), proofs, memo, self.unit))
|
||||
Ok(Token::new(
|
||||
self.mint_url.clone(),
|
||||
proofs,
|
||||
memo,
|
||||
self.unit.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Send
|
||||
|
||||
@@ -111,7 +111,9 @@ impl Wallet {
|
||||
let send_proofs_info = proofs_to_send
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|proof| ProofInfo::new(proof, mint_url.clone(), State::Reserved, *unit))
|
||||
.map(|proof| {
|
||||
ProofInfo::new(proof, mint_url.clone(), State::Reserved, unit.clone())
|
||||
})
|
||||
.collect::<Result<Vec<ProofInfo>, _>>()?;
|
||||
added_proofs = send_proofs_info;
|
||||
|
||||
@@ -126,7 +128,7 @@ impl Wallet {
|
||||
|
||||
let keep_proofs = change_proofs
|
||||
.into_iter()
|
||||
.map(|proof| ProofInfo::new(proof, mint_url.clone(), State::Unspent, *unit))
|
||||
.map(|proof| ProofInfo::new(proof, mint_url.clone(), State::Unspent, unit.clone()))
|
||||
.collect::<Result<Vec<ProofInfo>, _>>()?;
|
||||
added_proofs.extend(keep_proofs);
|
||||
|
||||
@@ -154,7 +156,7 @@ impl Wallet {
|
||||
.localstore
|
||||
.get_proofs(
|
||||
Some(self.mint_url.clone()),
|
||||
Some(self.unit),
|
||||
Some(self.unit.clone()),
|
||||
Some(vec![State::Unspent]),
|
||||
None,
|
||||
)
|
||||
|
||||
6
flake.lock
generated
6
flake.lock
generated
@@ -57,11 +57,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1730327045,
|
||||
"narHash": "sha256-xKel5kd1AbExymxoIfQ7pgcX6hjw9jCgbiBjiUfSVJ8=",
|
||||
"lastModified": 1730741070,
|
||||
"narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "080166c15633801df010977d9d7474b4a6c549d7",
|
||||
"rev": "d063c1dd113c91ab27959ba540c0d9753409edf3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
Reference in New Issue
Block a user