|
@@ -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<C: Client, L: LocalStore> Wallet<C, L> {
|
|
|
Ok(quote)
|
|
|
}
|
|
|
|
|
|
+ // Select proofs
|
|
|
+ async fn select_proofs(
|
|
|
+ &self,
|
|
|
+ mint_url: UncheckedUrl,
|
|
|
+ unit: &CurrencyUnit,
|
|
|
+ amount: Amount,
|
|
|
+ ) -> Result<Proofs, Error> {
|
|
|
+ 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<KeySetInfo>, HashSet<KeySetInfo>) = mint_keysets
|
|
|
+ .into_iter()
|
|
|
+ .filter(|p| p.unit.eq(unit))
|
|
|
+ .partition(|x| x.active);
|
|
|
+
|
|
|
+ let active: HashSet<Id> = active.iter().map(|k| k.id).collect();
|
|
|
+ let inactive: HashSet<Id> = 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>() < amount {
|
|
|
+ selected_proofs.push(proof);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if selected_proofs.iter().map(|p| p.amount).sum::<Amount>() < amount {
|
|
|
+ return Err(Error::InsufficientFunds);
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(selected_proofs)
|
|
|
+ }
|
|
|
+
|
|
|
/// Melt
|
|
|
- pub async fn melt(
|
|
|
- &mut self,
|
|
|
- mint_url: &UncheckedUrl,
|
|
|
- quote_id: &str,
|
|
|
- proofs: Proofs,
|
|
|
- ) -> Result<Melted, Error> {
|
|
|
+ pub async fn melt(&mut self, mint_url: &UncheckedUrl, quote_id: &str) -> Result<Melted, Error> {
|
|
|
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<C: Client, L: LocalStore> Wallet<C, L> {
|
|
|
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(
|