Browse Source

refactor: cdk-cli use multimint wallet

thesimplekid 9 months ago
parent
commit
ef66273554

+ 1 - 0
CHANGELOG.md

@@ -28,6 +28,7 @@
 
 ### Changed
 - cdk(wallet): `fn send` returns `Token` so the user can use the struct of convert it to a v3 or v4 string.
+- cdk(wallet): Publicly export `MultiMintWallet` ([thesimplekid]).
 
 ### Added
 - cdk(NUT-11): Add `Copy` on `SigFlag` ([thesimplekid]).

+ 21 - 21
crates/cdk-cli/src/main.rs

@@ -1,4 +1,3 @@
-use std::collections::HashMap;
 use std::fs;
 use std::path::PathBuf;
 use std::str::FromStr;
@@ -6,9 +5,9 @@ use std::sync::Arc;
 
 use anyhow::{bail, Result};
 use bip39::Mnemonic;
+use cdk::cdk_database;
 use cdk::cdk_database::WalletDatabase;
-use cdk::wallet::Wallet;
-use cdk::{cdk_database, UncheckedUrl};
+use cdk::wallet::{MultiMintWallet, Wallet};
 use cdk_redb::WalletRedbDatabase;
 use cdk_sqlite::WalletSqliteDatabase;
 use clap::{Parser, Subcommand};
@@ -126,7 +125,7 @@ async fn main() -> Result<()> {
         }
     };
 
-    let mut wallets: HashMap<UncheckedUrl, Wallet> = HashMap::new();
+    let mut wallets: Vec<Wallet> = Vec::new();
 
     let mints = localstore.get_mints().await?;
 
@@ -139,49 +138,50 @@ async fn main() -> Result<()> {
             None,
         );
 
-        wallets.insert(mint, wallet);
+        wallets.push(wallet);
     }
 
+    let multi_mint_wallet = MultiMintWallet::new(wallets);
+
     match &args.command {
         Commands::DecodeToken(sub_command_args) => {
             sub_commands::decode_token::decode_token(sub_command_args)
         }
-        Commands::Balance => sub_commands::balance::balance(wallets).await,
-        Commands::Pay => sub_commands::melt::pay(wallets).await,
+        Commands::Balance => sub_commands::balance::balance(&multi_mint_wallet).await,
+        Commands::Pay => sub_commands::melt::pay(multi_mint_wallet).await,
         Commands::Receive(sub_command_args) => {
-            sub_commands::receive::receive(
-                wallets,
-                &mnemonic.to_seed_normalized(""),
-                localstore,
-                sub_command_args,
-            )
-            .await
+            sub_commands::receive::receive(&multi_mint_wallet, localstore, sub_command_args).await
         }
         Commands::Send(sub_command_args) => {
-            sub_commands::send::send(wallets, sub_command_args).await
+            sub_commands::send::send(&multi_mint_wallet, sub_command_args).await
+        }
+        Commands::CheckSpendable => {
+            sub_commands::check_spent::check_spent(&multi_mint_wallet).await
         }
-        Commands::CheckSpendable => sub_commands::check_spent::check_spent(wallets).await,
         Commands::MintInfo(sub_command_args) => {
             sub_commands::mint_info::mint_info(sub_command_args).await
         }
         Commands::Mint(sub_command_args) => {
             sub_commands::mint::mint(
-                wallets,
+                &multi_mint_wallet,
                 &mnemonic.to_seed_normalized(""),
                 localstore,
                 sub_command_args,
             )
             .await
         }
-        Commands::MintPending => sub_commands::pending_mints::mint_pending(wallets).await,
+        Commands::MintPending => {
+            sub_commands::pending_mints::mint_pending(&multi_mint_wallet).await
+        }
         Commands::Burn(sub_command_args) => {
-            sub_commands::burn::burn(wallets, sub_command_args).await
+            sub_commands::burn::burn(&multi_mint_wallet, sub_command_args).await
         }
         Commands::Restore(sub_command_args) => {
-            sub_commands::restore::restore(wallets, sub_command_args).await
+            sub_commands::restore::restore(&multi_mint_wallet, sub_command_args).await
         }
         Commands::UpdateMintUrl(sub_command_args) => {
-            sub_commands::update_mint_url::update_mint_url(wallets, sub_command_args).await
+            sub_commands::update_mint_url::update_mint_url(&multi_mint_wallet, sub_command_args)
+                .await
         }
     }
 }

+ 12 - 9
crates/cdk-cli/src/sub_commands/balance.rs

@@ -1,25 +1,28 @@
 use std::collections::HashMap;
 
 use anyhow::Result;
+use cdk::nuts::CurrencyUnit;
 use cdk::url::UncheckedUrl;
-use cdk::wallet::Wallet;
+use cdk::wallet::multi_mint_wallet::MultiMintWallet;
 use cdk::Amount;
 
-pub async fn balance(wallets: HashMap<UncheckedUrl, Wallet>) -> Result<()> {
-    mint_balances(wallets).await?;
+pub async fn balance(multi_mint_wallet: &MultiMintWallet) -> Result<()> {
+    mint_balances(multi_mint_wallet).await?;
     Ok(())
 }
 
 pub async fn mint_balances(
-    wallets: HashMap<UncheckedUrl, Wallet>,
-) -> Result<Vec<(Wallet, Amount)>> {
-    let mut wallets_vec: Vec<(Wallet, Amount)> = Vec::with_capacity(wallets.capacity());
+    multi_mint_wallet: &MultiMintWallet,
+) -> Result<Vec<(UncheckedUrl, Amount)>> {
+    let wallets: HashMap<UncheckedUrl, Amount> =
+        multi_mint_wallet.get_balances(&CurrencyUnit::Sat).await?;
 
-    for (i, (mint_url, wallet)) in wallets.iter().enumerate() {
+    let mut wallets_vec = Vec::with_capacity(wallets.capacity());
+
+    for (i, (mint_url, amount)) in wallets.iter().enumerate() {
         let mint_url = mint_url.clone();
-        let amount = wallet.total_balance().await?;
         println!("{i}: {mint_url} {amount}");
-        wallets_vec.push((wallet.clone(), amount));
+        wallets_vec.push((mint_url, *amount))
     }
     Ok(wallets_vec)
 }

+ 9 - 6
crates/cdk-cli/src/sub_commands/burn.rs

@@ -1,7 +1,7 @@
-use std::collections::HashMap;
-
 use anyhow::Result;
-use cdk::wallet::Wallet;
+use cdk::nuts::CurrencyUnit;
+use cdk::wallet::multi_mint_wallet::WalletKey;
+use cdk::wallet::MultiMintWallet;
 use cdk::{Amount, UncheckedUrl};
 use clap::Args;
 
@@ -12,17 +12,20 @@ pub struct BurnSubCommand {
 }
 
 pub async fn burn(
-    wallets: HashMap<UncheckedUrl, Wallet>,
+    multi_mint_wallet: &MultiMintWallet,
     sub_command_args: &BurnSubCommand,
 ) -> Result<()> {
     let mut total_burnt = Amount::ZERO;
     match &sub_command_args.mint_url {
         Some(mint_url) => {
-            let wallet = wallets.get(mint_url).unwrap();
+            let wallet = multi_mint_wallet
+                .get_wallet(&WalletKey::new(mint_url.clone(), CurrencyUnit::Sat))
+                .await
+                .unwrap();
             total_burnt = wallet.check_all_pending_proofs().await?;
         }
         None => {
-            for wallet in wallets.values() {
+            for wallet in multi_mint_wallet.get_wallets().await {
                 let amount_burnt = wallet.check_all_pending_proofs().await?;
                 total_burnt += amount_burnt;
             }

+ 3 - 6
crates/cdk-cli/src/sub_commands/check_spent.rs

@@ -1,11 +1,8 @@
-use std::collections::HashMap;
-
 use anyhow::Result;
-use cdk::url::UncheckedUrl;
-use cdk::wallet::Wallet;
+use cdk::wallet::MultiMintWallet;
 
-pub async fn check_spent(wallets: HashMap<UncheckedUrl, Wallet>) -> Result<()> {
-    for wallet in wallets.values() {
+pub async fn check_spent(multi_mint_wallet: &MultiMintWallet) -> Result<()> {
+    for wallet in multi_mint_wallet.get_wallets().await {
         let amount = wallet.check_all_pending_proofs().await?;
 
         println!("Amount marked as spent: {}", amount);

+ 10 - 6
crates/cdk-cli/src/sub_commands/melt.rs

@@ -1,16 +1,16 @@
-use std::collections::HashMap;
+use std::io;
 use std::io::Write;
 use std::str::FromStr;
-use std::{io, println};
 
 use anyhow::{bail, Result};
-use cdk::wallet::Wallet;
-use cdk::{Bolt11Invoice, UncheckedUrl};
+use cdk::nuts::CurrencyUnit;
+use cdk::wallet::multi_mint_wallet::{MultiMintWallet, WalletKey};
+use cdk::Bolt11Invoice;
 
 use crate::sub_commands::balance::mint_balances;
 
-pub async fn pay(wallets: HashMap<UncheckedUrl, Wallet>) -> Result<()> {
-    let mints_amounts = mint_balances(wallets).await?;
+pub async fn pay(multi_mint_wallet: MultiMintWallet) -> Result<()> {
+    let mints_amounts = mint_balances(&multi_mint_wallet).await?;
 
     println!("Enter mint number to melt from");
 
@@ -26,6 +26,10 @@ pub async fn pay(wallets: HashMap<UncheckedUrl, Wallet>) -> Result<()> {
     }
 
     let wallet = mints_amounts[mint_number].0.clone();
+    let wallet = multi_mint_wallet
+        .get_wallet(&WalletKey::new(wallet, CurrencyUnit::Sat))
+        .await
+        .expect("Known wallet");
 
     println!("Enter bolt11 invoice request");
 

+ 19 - 11
crates/cdk-cli/src/sub_commands/mint.rs

@@ -1,4 +1,3 @@
-use std::collections::HashMap;
 use std::sync::Arc;
 use std::time::Duration;
 
@@ -7,7 +6,8 @@ use cdk::amount::SplitTarget;
 use cdk::cdk_database::{Error, WalletDatabase};
 use cdk::nuts::{CurrencyUnit, MintQuoteState};
 use cdk::url::UncheckedUrl;
-use cdk::wallet::Wallet;
+use cdk::wallet::multi_mint_wallet::WalletKey;
+use cdk::wallet::{MultiMintWallet, Wallet};
 use cdk::Amount;
 use clap::Args;
 use tokio::time::sleep;
@@ -24,21 +24,29 @@ pub struct MintSubCommand {
 }
 
 pub async fn mint(
-    wallets: HashMap<UncheckedUrl, Wallet>,
+    multi_mint_wallet: &MultiMintWallet,
     seed: &[u8],
     localstore: Arc<dyn WalletDatabase<Err = Error> + Sync + Send>,
     sub_command_args: &MintSubCommand,
 ) -> Result<()> {
     let mint_url = sub_command_args.mint_url.clone();
-    let wallet = match wallets.get(&mint_url) {
+    let wallet = match multi_mint_wallet
+        .get_wallet(&WalletKey::new(mint_url.clone(), CurrencyUnit::Sat))
+        .await
+    {
         Some(wallet) => wallet.clone(),
-        None => Wallet::new(
-            &mint_url.to_string(),
-            CurrencyUnit::Sat,
-            localstore,
-            seed,
-            None,
-        ),
+        None => {
+            let wallet = Wallet::new(
+                &mint_url.to_string(),
+                CurrencyUnit::Sat,
+                localstore,
+                seed,
+                None,
+            );
+
+            multi_mint_wallet.add_wallet(wallet.clone()).await;
+            wallet
+        }
     };
 
     let quote = wallet

+ 7 - 11
crates/cdk-cli/src/sub_commands/pending_mints.rs

@@ -1,16 +1,12 @@
-use std::collections::HashMap;
-
 use anyhow::Result;
-use cdk::wallet::Wallet;
-use cdk::{Amount, UncheckedUrl};
+use cdk::wallet::MultiMintWallet;
+use cdk::Amount;
+
+pub async fn mint_pending(multi_mint_wallet: &MultiMintWallet) -> Result<()> {
+    let amounts = multi_mint_wallet.check_all_mint_quotes(None).await?;
 
-pub async fn mint_pending(wallets: HashMap<UncheckedUrl, Wallet>) -> Result<()> {
-    let mut amount_claimed = Amount::ZERO;
-    for wallet in wallets.values() {
-        let claimed = wallet.check_all_mint_quotes().await?;
-        amount_claimed += claimed;
-    }
+    let amount = amounts.into_values().sum::<Amount>();
 
-    println!("Amount minted: {amount_claimed}");
+    println!("Amount minted: {amount}");
     Ok(())
 }

+ 13 - 35
crates/cdk-cli/src/sub_commands/receive.rs

@@ -1,14 +1,13 @@
-use std::collections::{HashMap, HashSet};
+use std::collections::HashSet;
 use std::str::FromStr;
 use std::sync::Arc;
 
 use anyhow::{anyhow, Result};
-use cdk::amount::SplitTarget;
-use cdk::cdk_database::{Error, WalletDatabase};
-use cdk::nuts::{CurrencyUnit, SecretKey, Token};
+use cdk::cdk_database::{self, WalletDatabase};
+use cdk::nuts::SecretKey;
 use cdk::util::unix_time;
-use cdk::wallet::Wallet;
-use cdk::{Amount, UncheckedUrl};
+use cdk::wallet::multi_mint_wallet::MultiMintWallet;
+use cdk::Amount;
 use clap::Args;
 use nostr_sdk::nips::nip04;
 use nostr_sdk::{Filter, Keys, Kind, Timestamp};
@@ -35,9 +34,8 @@ pub struct ReceiveSubCommand {
 }
 
 pub async fn receive(
-    wallets: HashMap<UncheckedUrl, Wallet>,
-    seed: &[u8],
-    localstore: Arc<dyn WalletDatabase<Err = Error> + Sync + Send>,
+    multi_mint_wallet: &MultiMintWallet,
+    localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
     sub_command_args: &ReceiveSubCommand,
 ) -> Result<()> {
     let mut signing_keys = Vec::new();
@@ -62,10 +60,8 @@ pub async fn receive(
     let amount = match &sub_command_args.token {
         Some(token_str) => {
             receive_token(
+                multi_mint_wallet,
                 token_str,
-                wallets,
-                seed,
-                &localstore,
                 &signing_keys,
                 &sub_command_args.preimage,
             )
@@ -97,10 +93,8 @@ pub async fn receive(
             let mut total_amount = Amount::ZERO;
             for token_str in &tokens {
                 match receive_token(
+                    multi_mint_wallet,
                     token_str,
-                    wallets.clone(),
-                    seed,
-                    &localstore,
                     &signing_keys,
                     &sub_command_args.preimage,
                 )
@@ -128,29 +122,13 @@ pub async fn receive(
 }
 
 async fn receive_token(
-    token_str: &str,
-    wallets: HashMap<UncheckedUrl, Wallet>,
-    seed: &[u8],
-    localstore: &Arc<dyn WalletDatabase<Err = Error> + Sync + Send>,
+    multi_mint_wallet: &MultiMintWallet,
+    token: &str,
     signing_keys: &[SecretKey],
     preimage: &[String],
 ) -> Result<Amount> {
-    let token = Token::from_str(token_str)?;
-    let mint_url = token.proofs().iter().next().unwrap().0.clone();
-
-    let wallet = match wallets.get(&mint_url) {
-        Some(wallet) => wallet.clone(),
-        None => Wallet::new(
-            &mint_url.to_string(),
-            CurrencyUnit::Sat,
-            Arc::clone(localstore),
-            seed,
-            None,
-        ),
-    };
-
-    let amount = wallet
-        .receive(token_str, SplitTarget::default(), signing_keys, preimage)
+    let amount = multi_mint_wallet
+        .receive(token, signing_keys, preimage)
         .await?;
     Ok(amount)
 }

+ 10 - 6
crates/cdk-cli/src/sub_commands/restore.rs

@@ -1,8 +1,8 @@
-use std::collections::HashMap;
-
 use anyhow::{anyhow, Result};
+use cdk::nuts::CurrencyUnit;
 use cdk::url::UncheckedUrl;
-use cdk::wallet::Wallet;
+use cdk::wallet::multi_mint_wallet::WalletKey;
+use cdk::wallet::MultiMintWallet;
 use clap::Args;
 
 #[derive(Args)]
@@ -12,11 +12,15 @@ pub struct RestoreSubCommand {
 }
 
 pub async fn restore(
-    wallets: HashMap<UncheckedUrl, Wallet>,
+    multi_mint_wallet: &MultiMintWallet,
     sub_command_args: &RestoreSubCommand,
 ) -> Result<()> {
-    let wallet = wallets
-        .get(&sub_command_args.mint_url)
+    let wallet = multi_mint_wallet
+        .get_wallet(&WalletKey::new(
+            sub_command_args.mint_url.clone(),
+            CurrencyUnit::Sat,
+        ))
+        .await
         .ok_or(anyhow!("Unknown mint url"))?;
 
     let amount = wallet.restore().await?;

+ 10 - 6
crates/cdk-cli/src/sub_commands/send.rs

@@ -1,14 +1,14 @@
-use std::collections::HashMap;
 use std::io;
 use std::io::Write;
 use std::str::FromStr;
 
 use anyhow::{bail, Result};
 use cdk::amount::SplitTarget;
-use cdk::nuts::{Conditions, PublicKey, SpendingConditions};
+use cdk::nuts::{Conditions, CurrencyUnit, PublicKey, SpendingConditions};
+use cdk::wallet::multi_mint_wallet::WalletKey;
 use cdk::wallet::types::SendKind;
-use cdk::wallet::Wallet;
-use cdk::{Amount, UncheckedUrl};
+use cdk::wallet::MultiMintWallet;
+use cdk::Amount;
 use clap::Args;
 
 use crate::sub_commands::balance::mint_balances;
@@ -48,10 +48,10 @@ pub struct SendSubCommand {
 }
 
 pub async fn send(
-    wallets: HashMap<UncheckedUrl, Wallet>,
+    multi_mint_wallet: &MultiMintWallet,
     sub_command_args: &SendSubCommand,
 ) -> Result<()> {
-    let mints_amounts = mint_balances(wallets).await?;
+    let mints_amounts = mint_balances(multi_mint_wallet).await?;
 
     println!("Enter mint number to create token");
 
@@ -155,6 +155,10 @@ pub async fn send(
     };
 
     let wallet = mints_amounts[mint_number].0.clone();
+    let wallet = multi_mint_wallet
+        .get_wallet(&WalletKey::new(wallet, CurrencyUnit::Sat))
+        .await
+        .expect("Known wallet");
 
     let send_kind = match (sub_command_args.offline, sub_command_args.tolerance) {
         (true, Some(amount)) => SendKind::OfflineTolerance(Amount::from(amount)),

+ 10 - 6
crates/cdk-cli/src/sub_commands/update_mint_url.rs

@@ -1,8 +1,8 @@
-use std::collections::HashMap;
-
 use anyhow::{anyhow, Result};
+use cdk::nuts::CurrencyUnit;
 use cdk::url::UncheckedUrl;
-use cdk::wallet::Wallet;
+use cdk::wallet::multi_mint_wallet::WalletKey;
+use cdk::wallet::MultiMintWallet;
 use clap::Args;
 
 #[derive(Args)]
@@ -14,7 +14,7 @@ pub struct UpdateMintUrlSubCommand {
 }
 
 pub async fn update_mint_url(
-    wallets: HashMap<UncheckedUrl, Wallet>,
+    multi_mint_wallet: &MultiMintWallet,
     sub_command_args: &UpdateMintUrlSubCommand,
 ) -> Result<()> {
     let UpdateMintUrlSubCommand {
@@ -22,8 +22,12 @@ pub async fn update_mint_url(
         new_mint_url,
     } = sub_command_args;
 
-    let mut wallet = wallets
-        .get(old_mint_url)
+    let mut wallet = multi_mint_wallet
+        .get_wallet(&WalletKey::new(
+            sub_command_args.old_mint_url.clone(),
+            CurrencyUnit::Sat,
+        ))
+        .await
         .ok_or(anyhow!("Unknown mint url"))?
         .clone();
 

+ 1 - 0
crates/cdk/src/wallet/mod.rs

@@ -35,6 +35,7 @@ pub mod multi_mint_wallet;
 pub mod types;
 pub mod util;
 
+pub use multi_mint_wallet::MultiMintWallet;
 pub use types::{MeltQuote, MintQuote, SendKind};
 
 /// CDK Wallet