Procházet zdrojové kódy

Simplify `MultiMintWallet` interface (#664)

ok300 před 1 měsícem
rodič
revize
de4285bd9c

+ 7 - 35
crates/cdk-cli/src/main.rs

@@ -165,6 +165,7 @@ async fn main() -> Result<()> {
             mnemonic
         }
     };
+    let seed = mnemonic.to_seed_normalized("");
 
     let mut wallets: Vec<Wallet> = Vec::new();
 
@@ -187,7 +188,7 @@ async fn main() -> Result<()> {
         wallets.push(wallet);
     }
 
-    let multi_mint_wallet = MultiMintWallet::new(wallets);
+    let multi_mint_wallet = MultiMintWallet::new(localstore, Arc::new(seed), wallets);
 
     match &args.command {
         Commands::DecodeToken(sub_command_args) => {
@@ -198,14 +199,7 @@ async fn main() -> Result<()> {
             sub_commands::melt::pay(&multi_mint_wallet, sub_command_args).await
         }
         Commands::Receive(sub_command_args) => {
-            sub_commands::receive::receive(
-                &multi_mint_wallet,
-                localstore,
-                &mnemonic.to_seed_normalized(""),
-                sub_command_args,
-                &work_dir,
-            )
-            .await
+            sub_commands::receive::receive(&multi_mint_wallet, sub_command_args, &work_dir).await
         }
         Commands::Send(sub_command_args) => {
             sub_commands::send::send(&multi_mint_wallet, sub_command_args).await
@@ -217,13 +211,7 @@ async fn main() -> Result<()> {
             sub_commands::mint_info::mint_info(args.proxy, sub_command_args).await
         }
         Commands::Mint(sub_command_args) => {
-            sub_commands::mint::mint(
-                &multi_mint_wallet,
-                &mnemonic.to_seed_normalized(""),
-                localstore,
-                sub_command_args,
-            )
-            .await
+            sub_commands::mint::mint(&multi_mint_wallet, sub_command_args).await
         }
         Commands::MintPending => {
             sub_commands::pending_mints::mint_pending(&multi_mint_wallet).await
@@ -232,13 +220,7 @@ async fn main() -> Result<()> {
             sub_commands::burn::burn(&multi_mint_wallet, sub_command_args).await
         }
         Commands::Restore(sub_command_args) => {
-            sub_commands::restore::restore(
-                &multi_mint_wallet,
-                &mnemonic.to_seed_normalized(""),
-                localstore,
-                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(&multi_mint_wallet, sub_command_args)
@@ -259,28 +241,18 @@ async fn main() -> Result<()> {
         Commands::MintBlindAuth(sub_command_args) => {
             sub_commands::mint_blind_auth::mint_blind_auth(
                 &multi_mint_wallet,
-                &mnemonic.to_seed_normalized(""),
-                localstore,
                 sub_command_args,
                 &work_dir,
             )
             .await
         }
         Commands::CatLogin(sub_command_args) => {
-            sub_commands::cat_login::cat_login(
-                &multi_mint_wallet,
-                &mnemonic.to_seed_normalized(""),
-                localstore,
-                sub_command_args,
-                &work_dir,
-            )
-            .await
+            sub_commands::cat_login::cat_login(&multi_mint_wallet, sub_command_args, &work_dir)
+                .await
         }
         Commands::CatDeviceLogin(sub_command_args) => {
             sub_commands::cat_device_login::cat_device_login(
                 &multi_mint_wallet,
-                &mnemonic.to_seed_normalized(""),
-                localstore,
                 sub_command_args,
                 &work_dir,
             )

+ 9 - 11
crates/cdk-cli/src/sub_commands/cat_device_login.rs

@@ -1,14 +1,12 @@
 use std::path::Path;
 use std::str::FromStr;
-use std::sync::Arc;
 use std::time::Duration;
 
-use anyhow::Result;
-use cdk::cdk_database::{Error, WalletDatabase};
+use anyhow::{anyhow, Result};
 use cdk::mint_url::MintUrl;
 use cdk::nuts::{CurrencyUnit, MintInfo};
 use cdk::wallet::types::WalletKey;
-use cdk::wallet::{MultiMintWallet, Wallet};
+use cdk::wallet::MultiMintWallet;
 use cdk::OidcClient;
 use clap::Args;
 use serde::{Deserialize, Serialize};
@@ -32,8 +30,6 @@ pub struct CatDeviceLoginSubCommand {
 
 pub async fn cat_device_login(
     multi_mint_wallet: &MultiMintWallet,
-    seed: &[u8],
-    localstore: Arc<dyn WalletDatabase<Err = Error> + Sync + Send>,
     sub_command_args: &CatDeviceLoginSubCommand,
     work_dir: &Path,
 ) -> Result<()> {
@@ -46,14 +42,16 @@ pub async fn cat_device_login(
     {
         Some(wallet) => wallet.clone(),
         None => {
-            let wallet = Wallet::new(&mint_url.to_string(), unit, localstore, seed, None)?;
-
-            multi_mint_wallet.add_wallet(wallet.clone()).await;
-            wallet
+            multi_mint_wallet
+                .create_and_add_wallet(&mint_url.to_string(), unit, None)
+                .await?
         }
     };
 
-    let mint_info = wallet.get_mint_info().await?.expect("Mint info not found");
+    let mint_info = wallet
+        .get_mint_info()
+        .await?
+        .ok_or(anyhow!("Mint info not found"))?;
 
     let (access_token, refresh_token) =
         get_device_code_token(&mint_info, &sub_command_args.client_id).await;

+ 9 - 11
crates/cdk-cli/src/sub_commands/cat_login.rs

@@ -1,13 +1,11 @@
 use std::path::Path;
 use std::str::FromStr;
-use std::sync::Arc;
 
-use anyhow::Result;
-use cdk::cdk_database::{Error, WalletDatabase};
+use anyhow::{anyhow, Result};
 use cdk::mint_url::MintUrl;
 use cdk::nuts::{CurrencyUnit, MintInfo};
 use cdk::wallet::types::WalletKey;
-use cdk::wallet::{MultiMintWallet, Wallet};
+use cdk::wallet::MultiMintWallet;
 use cdk::OidcClient;
 use clap::Args;
 use serde::{Deserialize, Serialize};
@@ -34,8 +32,6 @@ pub struct CatLoginSubCommand {
 
 pub async fn cat_login(
     multi_mint_wallet: &MultiMintWallet,
-    seed: &[u8],
-    localstore: Arc<dyn WalletDatabase<Err = Error> + Sync + Send>,
     sub_command_args: &CatLoginSubCommand,
     work_dir: &Path,
 ) -> Result<()> {
@@ -48,14 +44,16 @@ pub async fn cat_login(
     {
         Some(wallet) => wallet.clone(),
         None => {
-            let wallet = Wallet::new(&mint_url.to_string(), unit, localstore, seed, None)?;
-
-            multi_mint_wallet.add_wallet(wallet.clone()).await;
-            wallet
+            multi_mint_wallet
+                .create_and_add_wallet(&mint_url.to_string(), unit, None)
+                .await?
         }
     };
 
-    let mint_info = wallet.get_mint_info().await?.expect("Mint info not found");
+    let mint_info = wallet
+        .get_mint_info()
+        .await?
+        .ok_or(anyhow!("Mint info not found"))?;
 
     let (access_token, refresh_token) = get_access_token(
         &mint_info,

+ 4 - 9
crates/cdk-cli/src/sub_commands/mint.rs

@@ -1,14 +1,12 @@
 use std::str::FromStr;
-use std::sync::Arc;
 
 use anyhow::{anyhow, Result};
 use cdk::amount::SplitTarget;
-use cdk::cdk_database::{Error, WalletDatabase};
 use cdk::mint_url::MintUrl;
 use cdk::nuts::nut00::ProofsMethods;
 use cdk::nuts::{CurrencyUnit, MintQuoteState, NotificationPayload};
 use cdk::wallet::types::WalletKey;
-use cdk::wallet::{MultiMintWallet, Wallet, WalletSubscription};
+use cdk::wallet::{MultiMintWallet, WalletSubscription};
 use cdk::Amount;
 use clap::Args;
 use serde::{Deserialize, Serialize};
@@ -32,8 +30,6 @@ pub struct MintSubCommand {
 
 pub async fn mint(
     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();
@@ -46,10 +42,9 @@ pub async fn mint(
     {
         Some(wallet) => wallet.clone(),
         None => {
-            let wallet = Wallet::new(&mint_url.to_string(), unit, localstore, seed, None)?;
-
-            multi_mint_wallet.add_wallet(wallet.clone()).await;
-            wallet
+            multi_mint_wallet
+                .create_and_add_wallet(&mint_url.to_string(), unit, None)
+                .await?
         }
     };
 

+ 4 - 9
crates/cdk-cli/src/sub_commands/mint_blind_auth.rs

@@ -1,13 +1,11 @@
 use std::path::Path;
 use std::str::FromStr;
-use std::sync::Arc;
 
 use anyhow::{anyhow, Result};
-use cdk::cdk_database::{Error, WalletDatabase};
 use cdk::mint_url::MintUrl;
 use cdk::nuts::{CurrencyUnit, MintInfo};
 use cdk::wallet::types::WalletKey;
-use cdk::wallet::{MultiMintWallet, Wallet};
+use cdk::wallet::MultiMintWallet;
 use cdk::{Amount, OidcClient};
 use clap::Args;
 use serde::{Deserialize, Serialize};
@@ -31,8 +29,6 @@ pub struct MintBlindAuthSubCommand {
 
 pub async fn mint_blind_auth(
     multi_mint_wallet: &MultiMintWallet,
-    seed: &[u8],
-    localstore: Arc<dyn WalletDatabase<Err = Error> + Sync + Send>,
     sub_command_args: &MintBlindAuthSubCommand,
     work_dir: &Path,
 ) -> Result<()> {
@@ -45,10 +41,9 @@ pub async fn mint_blind_auth(
     {
         Some(wallet) => wallet.clone(),
         None => {
-            let wallet = Wallet::new(&mint_url.to_string(), unit, localstore, seed, None)?;
-
-            multi_mint_wallet.add_wallet(wallet.clone()).await;
-            wallet
+            multi_mint_wallet
+                .create_and_add_wallet(&mint_url.to_string(), unit, None)
+                .await?
         }
     };
 

+ 7 - 19
crates/cdk-cli/src/sub_commands/receive.rs

@@ -1,15 +1,12 @@
 use std::collections::HashSet;
 use std::path::Path;
 use std::str::FromStr;
-use std::sync::Arc;
 
 use anyhow::{anyhow, Result};
-use cdk::cdk_database::{self, WalletDatabase};
 use cdk::nuts::{SecretKey, Token};
 use cdk::util::unix_time;
 use cdk::wallet::multi_mint_wallet::MultiMintWallet;
 use cdk::wallet::types::WalletKey;
-use cdk::wallet::Wallet;
 use cdk::Amount;
 use clap::Args;
 use nostr_sdk::nips::nip04;
@@ -40,8 +37,6 @@ pub struct ReceiveSubCommand {
 
 pub async fn receive(
     multi_mint_wallet: &MultiMintWallet,
-    localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
-    seed: &[u8],
     sub_command_args: &ReceiveSubCommand,
     work_dir: &Path,
 ) -> Result<()> {
@@ -68,8 +63,6 @@ pub async fn receive(
         Some(token_str) => {
             receive_token(
                 multi_mint_wallet,
-                localstore,
-                seed,
                 token_str,
                 &signing_keys,
                 &sub_command_args.preimage,
@@ -110,8 +103,6 @@ pub async fn receive(
             for token_str in &tokens {
                 match receive_token(
                     multi_mint_wallet,
-                    localstore.clone(),
-                    seed,
                     token_str,
                     &signing_keys,
                     &sub_command_args.preimage,
@@ -138,8 +129,6 @@ pub async fn receive(
 
 async fn receive_token(
     multi_mint_wallet: &MultiMintWallet,
-    localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
-    seed: &[u8],
     token_str: &str,
     signing_keys: &[SecretKey],
     preimage: &[String],
@@ -151,14 +140,13 @@ async fn receive_token(
     let wallet_key = WalletKey::new(mint_url.clone(), token.unit().unwrap_or_default());
 
     if multi_mint_wallet.get_wallet(&wallet_key).await.is_none() {
-        let wallet = Wallet::new(
-            &mint_url.to_string(),
-            token.unit().unwrap_or_default(),
-            localstore,
-            seed,
-            None,
-        )?;
-        multi_mint_wallet.add_wallet(wallet).await;
+        multi_mint_wallet
+            .create_and_add_wallet(
+                &mint_url.to_string(),
+                token.unit().unwrap_or_default(),
+                None,
+            )
+            .await?;
     }
 
     let amount = multi_mint_wallet

+ 4 - 9
crates/cdk-cli/src/sub_commands/restore.rs

@@ -1,12 +1,10 @@
 use std::str::FromStr;
-use std::sync::Arc;
 
 use anyhow::Result;
-use cdk::cdk_database::{Error, WalletDatabase};
 use cdk::mint_url::MintUrl;
 use cdk::nuts::CurrencyUnit;
 use cdk::wallet::types::WalletKey;
-use cdk::wallet::{MultiMintWallet, Wallet};
+use cdk::wallet::MultiMintWallet;
 use clap::Args;
 
 #[derive(Args)]
@@ -20,8 +18,6 @@ pub struct RestoreSubCommand {
 
 pub async fn restore(
     multi_mint_wallet: &MultiMintWallet,
-    seed: &[u8],
-    localstore: Arc<dyn WalletDatabase<Err = Error> + Sync + Send>,
     sub_command_args: &RestoreSubCommand,
 ) -> Result<()> {
     let unit = CurrencyUnit::from_str(&sub_command_args.unit)?;
@@ -33,10 +29,9 @@ pub async fn restore(
     {
         Some(wallet) => wallet.clone(),
         None => {
-            let wallet = Wallet::new(&mint_url.to_string(), unit, localstore, seed, None)?;
-
-            multi_mint_wallet.add_wallet(wallet.clone()).await;
-            wallet
+            multi_mint_wallet
+                .create_and_add_wallet(&mint_url.to_string(), unit, None)
+                .await?
         }
     };
 

+ 1 - 2
crates/cdk-integration-tests/tests/payment_processor.rs

@@ -121,12 +121,11 @@ async fn test_pay_invoice_twice() -> Result<()> {
         return Ok(());
     }
 
-    let seed = Mnemonic::generate(12)?.to_seed_normalized("");
     let wallet = Wallet::new(
         &get_mint_url("0"),
         CurrencyUnit::Sat,
         Arc::new(memory::empty().await?),
-        &seed,
+        &Mnemonic::generate(12)?.to_seed_normalized(""),
         None,
     )?;
 

+ 3 - 7
crates/cdk-integration-tests/tests/regtest.rs

@@ -257,12 +257,11 @@ async fn test_restore() -> Result<()> {
 async fn test_pay_invoice_twice() -> anyhow::Result<()> {
     let lnd_client = init_lnd_client().await;
 
-    let seed = Mnemonic::generate(12).unwrap().to_seed_normalized("");
     let wallet = Wallet::new(
         &get_mint_url("0"),
         CurrencyUnit::Sat,
         Arc::new(memory::empty().await.unwrap()),
-        &seed,
+        &Mnemonic::generate(12).unwrap().to_seed_normalized(""),
         None,
     )?;
 
@@ -328,12 +327,11 @@ async fn test_pay_invoice_twice() -> anyhow::Result<()> {
 async fn test_internal_payment() -> Result<()> {
     let lnd_client = init_lnd_client().await;
 
-    let seed = Mnemonic::generate(12)?.to_seed_normalized("");
     let wallet = Wallet::new(
         &get_mint_url("0"),
         CurrencyUnit::Sat,
         Arc::new(memory::empty().await?),
-        &seed,
+        &Mnemonic::generate(12)?.to_seed_normalized(""),
         None,
     )?;
 
@@ -349,13 +347,11 @@ async fn test_internal_payment() -> Result<()> {
 
     assert!(wallet.total_balance().await? == 100.into());
 
-    let seed = Mnemonic::generate(12)?.to_seed_normalized("");
-
     let wallet_2 = Wallet::new(
         &get_mint_url("0"),
         CurrencyUnit::Sat,
         Arc::new(memory::empty().await?),
-        &seed,
+        &Mnemonic::generate(12)?.to_seed_normalized(""),
         None,
     )?;
 

+ 2 - 2
crates/cdk/README.md

@@ -59,14 +59,14 @@ use cdk::wallet::Wallet;
 #[cfg(feature = "wallet")]
 use cdk::wallet::SendOptions;
 use cdk::Amount;
-use rand::Rng;
+use rand::random;
 use tokio::time::sleep;
 
 #[tokio::main]
 async fn main() {
     #[cfg(feature = "wallet")]
     {
-        let seed = rand::thread_rng().gen::<[u8; 32]>();
+        let seed = random::<[u8; 32]>();
 
         let mint_url = "https://testnut.cashu.space";
         let unit = CurrencyUnit::Sat;

+ 4 - 4
crates/cdk/examples/mint-token.rs

@@ -7,7 +7,7 @@ use cdk::nuts::{CurrencyUnit, MintQuoteState, NotificationPayload};
 use cdk::wallet::{SendOptions, Wallet, WalletSubscription};
 use cdk::Amount;
 use cdk_sqlite::wallet::memory;
-use rand::Rng;
+use rand::random;
 use tracing_subscriber::EnvFilter;
 
 #[tokio::main]
@@ -22,10 +22,10 @@ async fn main() -> Result<(), Error> {
     tracing_subscriber::fmt().with_env_filter(env_filter).init();
 
     // Initialize the memory store for the wallet
-    let localstore = memory::empty().await?;
+    let localstore = Arc::new(memory::empty().await?);
 
     // Generate a random seed for the wallet
-    let seed = rand::thread_rng().gen::<[u8; 32]>();
+    let seed = random::<[u8; 32]>();
 
     // Define the mint URL and currency unit
     let mint_url = "https://testnut.cashu.space";
@@ -33,7 +33,7 @@ async fn main() -> Result<(), Error> {
     let amount = Amount::from(10);
 
     // Create a new wallet
-    let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None)?;
+    let wallet = Wallet::new(mint_url, unit, localstore, &seed, None)?;
 
     // Request a mint quote from the wallet
     let quote = wallet.mint_quote(amount, None).await?;

+ 4 - 4
crates/cdk/examples/p2pk.rs

@@ -6,7 +6,7 @@ use cdk::nuts::{CurrencyUnit, MintQuoteState, NotificationPayload, SecretKey, Sp
 use cdk::wallet::{SendOptions, Wallet, WalletSubscription};
 use cdk::Amount;
 use cdk_sqlite::wallet::memory;
-use rand::Rng;
+use rand::random;
 use tracing_subscriber::EnvFilter;
 
 #[tokio::main]
@@ -21,10 +21,10 @@ async fn main() -> Result<(), Error> {
     tracing_subscriber::fmt().with_env_filter(env_filter).init();
 
     // Initialize the memory store for the wallet
-    let localstore = memory::empty().await?;
+    let localstore = Arc::new(memory::empty().await?);
 
     // Generate a random seed for the wallet
-    let seed = rand::thread_rng().gen::<[u8; 32]>();
+    let seed = random::<[u8; 32]>();
 
     // Define the mint URL and currency unit
     let mint_url = "https://testnut.cashu.space";
@@ -32,7 +32,7 @@ async fn main() -> Result<(), Error> {
     let amount = Amount::from(100);
 
     // Create a new wallet
-    let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None).unwrap();
+    let wallet = Wallet::new(mint_url, unit, localstore, &seed, None).unwrap();
 
     // Request a mint quote from the wallet
     let quote = wallet.mint_quote(amount, None).await?;

+ 4 - 4
crates/cdk/examples/proof-selection.rs

@@ -9,22 +9,22 @@ use cdk::nuts::{CurrencyUnit, MintQuoteState, NotificationPayload};
 use cdk::wallet::{Wallet, WalletSubscription};
 use cdk::Amount;
 use cdk_sqlite::wallet::memory;
-use rand::Rng;
+use rand::random;
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
     // Generate a random seed for the wallet
-    let seed = rand::thread_rng().gen::<[u8; 32]>();
+    let seed = random::<[u8; 32]>();
 
     // Mint URL and currency unit
     let mint_url = "https://testnut.cashu.space";
     let unit = CurrencyUnit::Sat;
 
     // Initialize the memory store
-    let localstore = memory::empty().await?;
+    let localstore = Arc::new(memory::empty().await?);
 
     // Create a new wallet
-    let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None).unwrap();
+    let wallet = Wallet::new(mint_url, unit, localstore, &seed, None)?;
 
     // Amount to mint
     for amount in [64] {

+ 4 - 4
crates/cdk/examples/wallet.rs

@@ -7,13 +7,13 @@ use cdk::nuts::{CurrencyUnit, MintQuoteState};
 use cdk::wallet::{SendOptions, Wallet};
 use cdk::Amount;
 use cdk_sqlite::wallet::memory;
-use rand::Rng;
+use rand::random;
 use tokio::time::sleep;
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
     // Generate a random seed for the wallet
-    let seed = rand::thread_rng().gen::<[u8; 32]>();
+    let seed = random::<[u8; 32]>();
 
     // Mint URL and currency unit
     let mint_url = "https://testnut.cashu.space";
@@ -21,10 +21,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
     let amount = Amount::from(10);
 
     // Initialize the memory store
-    let localstore = memory::empty().await?;
+    let localstore = Arc::new(memory::empty().await?);
 
     // Create a new wallet
-    let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None).unwrap();
+    let wallet = Wallet::new(mint_url, unit, localstore, &seed, None)?;
 
     // Request a mint quote from the wallet
     let quote = wallet.mint_quote(amount, None).await?;

+ 2 - 2
crates/cdk/src/wallet/README.md

@@ -11,11 +11,11 @@ The CDK [`Wallet`] is a high level Cashu wallet. The [`Wallet`] is for a single
   use cdk::nuts::CurrencyUnit;
   use cdk::wallet::Wallet;
   use cdk_sqlite::wallet::memory;
-  use rand::Rng;
+  use rand::random;
 
   #[tokio::main]
   async fn main() -> anyhow::Result<()> {
-    let seed = rand::thread_rng().gen::<[u8; 32]>();
+    let seed = random::<[u8; 32]>();
     let mint_url = "https://testnut.cashu.space";
     let unit = CurrencyUnit::Sat;
 

+ 4 - 4
crates/cdk/src/wallet/melt.rs

@@ -23,11 +23,11 @@ impl Wallet {
     ///  use cdk_sqlite::wallet::memory;
     ///  use cdk::nuts::CurrencyUnit;
     ///  use cdk::wallet::Wallet;
-    ///  use rand::Rng;
+    ///  use rand::random;
     ///
     /// #[tokio::main]
     /// async fn main() -> anyhow::Result<()> {
-    ///     let seed = rand::thread_rng().gen::<[u8; 32]>();
+    ///     let seed = random::<[u8; 32]>();
     ///     let mint_url = "https://testnut.cashu.space";
     ///     let unit = CurrencyUnit::Sat;
     ///
@@ -254,11 +254,11 @@ impl Wallet {
     ///  use cdk_sqlite::wallet::memory;
     ///  use cdk::nuts::CurrencyUnit;
     ///  use cdk::wallet::Wallet;
-    ///  use rand::Rng;
+    ///  use rand::random;
     ///
     /// #[tokio::main]
     /// async fn main() -> anyhow::Result<()> {
-    ///  let seed = rand::thread_rng().gen::<[u8; 32]>();
+    ///  let seed = random::<[u8; 32]>();
     ///  let mint_url = "https://testnut.cashu.space";
     ///  let unit = CurrencyUnit::Sat;
     ///

+ 4 - 4
crates/cdk/src/wallet/mint.rs

@@ -24,11 +24,11 @@ impl Wallet {
     /// use cdk_sqlite::wallet::memory;
     /// use cdk::nuts::CurrencyUnit;
     /// use cdk::wallet::Wallet;
-    /// use rand::Rng;
+    /// use rand::random;
     ///
     /// #[tokio::main]
     /// async fn main() -> anyhow::Result<()> {
-    ///     let seed = rand::thread_rng().gen::<[u8; 32]>();
+    ///     let seed = random::<[u8; 32]>();
     ///     let mint_url = "https://testnut.cashu.space";
     ///     let unit = CurrencyUnit::Sat;
     ///
@@ -147,11 +147,11 @@ impl Wallet {
     /// use cdk::nuts::nut00::ProofsMethods;
     /// use cdk::nuts::CurrencyUnit;
     /// use cdk::wallet::Wallet;
-    /// use rand::Rng;
+    /// use rand::random;
     ///
     /// #[tokio::main]
     /// async fn main() -> Result<()> {
-    ///     let seed = rand::thread_rng().gen::<[u8; 32]>();
+    ///     let seed = random::<[u8; 32]>();
     ///     let mint_url = "https://testnut.cashu.space";
     ///     let unit = CurrencyUnit::Sat;
     ///

+ 2 - 2
crates/cdk/src/wallet/mod.rs

@@ -139,10 +139,10 @@ impl Wallet {
     /// use cdk_sqlite::wallet::memory;
     /// use cdk::nuts::CurrencyUnit;
     /// use cdk::wallet::{Wallet, WalletBuilder};
-    /// use rand::Rng;
+    /// use rand::random;
     ///
     /// async fn test() -> anyhow::Result<()> {
-    ///     let seed = rand::thread_rng().gen::<[u8; 32]>();
+    ///     let seed = random::<[u8; 32]>();
     ///     let mint_url = "https://testnut.cashu.space";
     ///     let unit = CurrencyUnit::Sat;
     ///

+ 35 - 3
crates/cdk/src/wallet/multi_mint_wallet.rs

@@ -7,6 +7,9 @@ use std::collections::{BTreeMap, HashMap};
 use std::str::FromStr;
 use std::sync::Arc;
 
+use anyhow::Result;
+use cdk_common::database;
+use cdk_common::database::WalletDatabase;
 use cdk_common::wallet::WalletKey;
 use tokio::sync::Mutex;
 use tracing::instrument;
@@ -23,14 +26,23 @@ use crate::{ensure_cdk, Amount, Wallet};
 /// Multi Mint Wallet
 #[derive(Debug, Clone)]
 pub struct MultiMintWallet {
+    /// Storage backend
+    pub localstore: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
+    seed: Arc<[u8]>,
     /// Wallets
     pub wallets: Arc<Mutex<BTreeMap<WalletKey, Wallet>>>,
 }
 
 impl MultiMintWallet {
-    /// New Multimint wallet
-    pub fn new(wallets: Vec<Wallet>) -> Self {
+    /// Create a new [MultiMintWallet] with initial wallets
+    pub fn new(
+        localstore: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
+        seed: Arc<[u8]>,
+        wallets: Vec<Wallet>,
+    ) -> Self {
         Self {
+            localstore,
+            seed,
             wallets: Arc::new(Mutex::new(
                 wallets
                     .into_iter()
@@ -40,7 +52,7 @@ impl MultiMintWallet {
         }
     }
 
-    /// Add wallet to MultiMintWallet
+    /// Adds a [Wallet] to this [MultiMintWallet]
     #[instrument(skip(self, wallet))]
     pub async fn add_wallet(&self, wallet: Wallet) {
         let wallet_key = WalletKey::new(wallet.mint_url.clone(), wallet.unit.clone());
@@ -50,6 +62,26 @@ impl MultiMintWallet {
         wallets.insert(wallet_key, wallet);
     }
 
+    /// Creates a new [Wallet] and adds it to this [MultiMintWallet]
+    pub async fn create_and_add_wallet(
+        &self,
+        mint_url: &str,
+        unit: CurrencyUnit,
+        target_proof_count: Option<usize>,
+    ) -> Result<Wallet> {
+        let wallet = Wallet::new(
+            mint_url,
+            unit,
+            self.localstore.clone(),
+            self.seed.as_ref(),
+            target_proof_count,
+        )?;
+
+        self.add_wallet(wallet.clone()).await;
+
+        Ok(wallet)
+    }
+
     /// Remove Wallet from MultiMintWallet
     #[instrument(skip(self))]
     pub async fn remove_wallet(&self, wallet_key: &WalletKey) {

+ 4 - 4
crates/cdk/src/wallet/receive.rs

@@ -167,11 +167,11 @@ impl Wallet {
     ///  use cdk_sqlite::wallet::memory;
     ///  use cdk::nuts::CurrencyUnit;
     ///  use cdk::wallet::Wallet;
-    ///  use rand::Rng;
+    ///  use rand::random;
     ///
     /// #[tokio::main]
     /// async fn main() -> anyhow::Result<()> {
-    ///  let seed = rand::thread_rng().gen::<[u8; 32]>();
+    ///  let seed = random::<[u8; 32]>();
     ///  let mint_url = "https://testnut.cashu.space";
     ///  let unit = CurrencyUnit::Sat;
     ///
@@ -221,11 +221,11 @@ impl Wallet {
     ///  use cdk::nuts::CurrencyUnit;
     ///  use cdk::wallet::Wallet;
     ///  use cdk::util::hex;
-    ///  use rand::Rng;
+    ///  use rand::random;
     ///
     /// #[tokio::main]
     /// async fn main() -> anyhow::Result<()> {
-    ///  let seed = rand::thread_rng().gen::<[u8; 32]>();
+    ///  let seed = random::<[u8; 32]>();
     ///  let mint_url = "https://testnut.cashu.space";
     ///  let unit = CurrencyUnit::Sat;
     ///