diff --git a/crates/cashu-sdk/src/wallet.rs b/crates/cashu-sdk/src/wallet.rs index 06432e9d..882f1eb8 100644 --- a/crates/cashu-sdk/src/wallet.rs +++ b/crates/cashu-sdk/src/wallet.rs @@ -1,5 +1,5 @@ //! Cashu Wallet -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::str::FromStr; use bip39::Mnemonic; @@ -7,15 +7,14 @@ use cashu::dhke::{construct_proofs, unblind_message}; #[cfg(feature = "nut07")] use cashu::nuts::nut00::mint; use cashu::nuts::{ - BlindedSignature, CurrencyUnit, Id, Keys, PreMintSecrets, PreSwap, Proof, Proofs, SwapRequest, - Token, + BlindedSignature, CurrencyUnit, Id, KeySetInfo, Keys, PreMintSecrets, PreSwap, Proof, Proofs, + SwapRequest, Token, }; #[cfg(feature = "nut07")] use cashu::types::ProofsStatus; use cashu::types::{MeltQuote, Melted, MintQuote, SendProofs}; use cashu::url::UncheckedUrl; -use cashu::Amount; -pub use cashu::Bolt11Invoice; +use cashu::{Amount, Bolt11Invoice}; use thiserror::Error; use tracing::warn; @@ -495,13 +494,64 @@ impl Wallet { Ok(quote) } + // Select proofs + async fn select_proofs( + &self, + mint_url: UncheckedUrl, + unit: &CurrencyUnit, + amount: Amount, + ) -> Result { + let mint_proofs = self + .localstore + .get_proofs(mint_url.clone()) + .await? + .ok_or(Error::InsufficientFunds)?; + + let mint_keysets = self.localstore.get_mint_keysets(mint_url).await?.unwrap(); + + let (active, inactive): (HashSet, HashSet) = mint_keysets + .into_iter() + .filter(|p| p.unit.eq(unit)) + .partition(|x| x.active); + + let active: HashSet = active.iter().map(|k| k.id).collect(); + let inactive: HashSet = inactive.iter().map(|k| k.id).collect(); + + let mut active_proofs: Proofs = Vec::new(); + let mut inactive_proofs: Proofs = Vec::new(); + + for proof in mint_proofs { + if active.contains(&proof.keyset_id) { + active_proofs.push(proof); + } else if inactive.contains(&proof.keyset_id) { + inactive_proofs.push(proof); + } + } + + active_proofs.reverse(); + inactive_proofs.reverse(); + + inactive_proofs.append(&mut active_proofs); + + let proofs = inactive_proofs; + + let mut selected_proofs: Proofs = Vec::new(); + + for proof in proofs { + if selected_proofs.iter().map(|p| p.amount).sum::() < amount { + selected_proofs.push(proof); + } + } + + if selected_proofs.iter().map(|p| p.amount).sum::() < amount { + return Err(Error::InsufficientFunds); + } + + Ok(selected_proofs) + } + /// Melt - pub async fn melt( - &mut self, - mint_url: &UncheckedUrl, - quote_id: &str, - proofs: Proofs, - ) -> Result { + pub async fn melt(&mut self, mint_url: &UncheckedUrl, quote_id: &str) -> Result { let quote_info = self.localstore.get_melt_quote(quote_id).await?; let quote_info = if let Some(quote) = quote_info { @@ -521,6 +571,10 @@ impl Wallet { quote_info.fee_reserve, )?; + let proofs = self + .select_proofs(mint_url.clone(), "e_info.unit, quote_info.amount) + .await?; + let melt_response = self .client .post_melt(