Kaynağa Gözat

Stricter clippy checks (#1485)

tsk 2 hafta önce
ebeveyn
işleme
afd7c713f1
70 değiştirilmiş dosya ile 336 ekleme ve 66 silme
  1. 4 4
      Cargo.toml
  2. 22 2
      crates/cashu/Cargo.toml
  3. 2 2
      crates/cashu/src/nuts/nut01/public_key.rs
  4. 26 1
      crates/cashu/src/nuts/nut02.rs
  5. 2 2
      crates/cashu/src/util/hex.rs
  6. 1 0
      crates/cdk-axum/src/cache/backend/memory.rs
  7. 9 0
      crates/cdk-axum/src/cache/backend/redis.rs
  8. 9 0
      crates/cdk-axum/src/cache/mod.rs
  9. 2 0
      crates/cdk-axum/src/lib.rs
  10. 4 1
      crates/cdk-cli/src/sub_commands/melt.rs
  11. 33 5
      crates/cdk-cli/src/sub_commands/mint.rs
  12. 1 1
      crates/cdk-cli/src/sub_commands/mint_info.rs
  13. 9 0
      crates/cdk-cln/src/lib.rs
  14. 5 0
      crates/cdk-common/src/common.rs
  15. 1 1
      crates/cdk-common/src/database/mint/test/mod.rs
  16. 1 1
      crates/cdk-common/src/database/wallet/test/mod.rs
  17. 6 0
      crates/cdk-common/src/error.rs
  18. 1 0
      crates/cdk-common/src/mint.rs
  19. 1 1
      crates/cdk-common/src/payment.rs
  20. 1 0
      crates/cdk-common/src/pub_sub/pubsub.rs
  21. 4 0
      crates/cdk-common/src/pub_sub/remote_consumer.rs
  22. 1 0
      crates/cdk-common/src/pub_sub/subscriber.rs
  23. 4 0
      crates/cdk-common/src/task.rs
  24. 5 1
      crates/cdk-fake-wallet/src/lib.rs
  25. 1 0
      crates/cdk-ffi/src/lib.rs
  26. 4 0
      crates/cdk-ffi/src/multi_mint_wallet.rs
  27. 1 1
      crates/cdk-ffi/src/wallet.rs
  28. 2 1
      crates/cdk-integration-tests/tests/regtest.rs
  29. 9 0
      crates/cdk-ldk-node/src/lib.rs
  30. 8 0
      crates/cdk-lnbits/src/lib.rs
  31. 8 0
      crates/cdk-lnd/src/lib.rs
  32. 6 3
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/rotate_next_keyset.rs
  33. 2 2
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_contact.rs
  34. 1 1
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_icon_url.rs
  35. 1 1
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_long_description.rs
  36. 1 1
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_motd.rs
  37. 1 1
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_name.rs
  38. 1 1
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_nut04.rs
  39. 1 1
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_nut04_quote.rs
  40. 1 1
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_nut05.rs
  41. 1 1
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_short_description.rs
  42. 2 2
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_ttl.rs
  43. 2 2
      crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_urls.rs
  44. 1 0
      crates/cdk-mint-rpc/src/proto/server.rs
  45. 1 1
      crates/cdk-mintd/src/cli.rs
  46. 4 1
      crates/cdk-mintd/src/setup.rs
  47. 7 0
      crates/cdk-payment-processor/src/proto/client.rs
  48. 8 0
      crates/cdk-payment-processor/src/proto/server.rs
  49. 1 0
      crates/cdk-redb/src/wallet/mod.rs
  50. 1 1
      crates/cdk-signatory/src/bin/cli/mod.rs
  51. 5 0
      crates/cdk-signatory/src/db_signatory.rs
  52. 1 0
      crates/cdk-signatory/src/embedded.rs
  53. 0 2
      crates/cdk-signatory/src/lib.rs
  54. 1 0
      crates/cdk-signatory/src/proto/client.rs
  55. 5 0
      crates/cdk-signatory/src/proto/server.rs
  56. 1 0
      crates/cdk-sql-common/src/database.rs
  57. 1 0
      crates/cdk-sql-common/src/mint/mod.rs
  58. 1 0
      crates/cdk-sql-common/src/wallet/mod.rs
  59. 1 0
      crates/cdk-sqlite/src/async_sqlite.rs
  60. 9 0
      crates/cdk/src/mint/builder.rs
  61. 9 1
      crates/cdk/src/mint/mod.rs
  62. 2 0
      crates/cdk/src/mint/subscription.rs
  63. 2 8
      crates/cdk/src/mint/verification.rs
  64. 7 1
      crates/cdk/src/wallet/auth/auth_wallet.rs
  65. 24 4
      crates/cdk/src/wallet/builder.rs
  66. 12 3
      crates/cdk/src/wallet/mod.rs
  67. 9 1
      crates/cdk/src/wallet/multi_mint_wallet.rs
  68. 13 1
      crates/cdk/src/wallet/streams/payment.rs
  69. 2 0
      crates/cdk/src/wallet/streams/proof.rs
  70. 1 1
      crates/cdk/src/wallet/subscription/mod.rs

+ 4 - 4
Cargo.toml

@@ -10,20 +10,20 @@ resolver = "2"
 [workspace.lints.rust]
 unsafe_code = "forbid"
 # unreachable_pub = "warn"
-# missing_debug_implementations = "warn"
+missing_debug_implementations = "warn"
 
 [workspace.lints.clippy]
 # pedantic = { level = "warn", priority = -1 }
+# nursery = { level = "warn", priority = -1 }
 unwrap_used = "deny"
 # clone_on_ref_ptr = "warn"
 # missing_errors_doc = "warn"
-# missing_panics_doc = "warn"
+missing_panics_doc = "warn"
 missing_safety_doc = "warn"
-# nursery = { level = "warn", priority = -1 }
 # redundant_else = "warn"
 # redundant_closure_for_method_calls = "warn"
 # unneeded_field_pattern = "warn"
-# use_debug = "warn"
+use_debug = "warn"
 large_enum_variant = "warn"
 
 [workspace.lints.rustdoc]

+ 22 - 2
crates/cashu/Cargo.toml

@@ -45,5 +45,25 @@ uuid = { workspace = true, features = ["js"], optional = true }
 [dev-dependencies]
 bip39.workspace = true
 
-[lints]
-workspace = true
+[lints.rust]
+unsafe_code = "forbid"
+unreachable_pub = "warn"
+missing_debug_implementations = "warn"
+
+[lints.clippy]
+# pedantic = { level = "warn", priority = -1 }
+# nursery = { level = "warn", priority = -1 }
+unwrap_used = "deny"
+clone_on_ref_ptr = "warn"
+# missing_errors_doc = "warn"
+missing_panics_doc = "warn"
+missing_safety_doc = "warn"
+large_enum_variant = "warn"
+redundant_else = "warn"
+redundant_closure_for_method_calls = "warn"
+unneeded_field_pattern = "warn"
+use_debug = "warn"
+
+[lints.rustdoc]
+missing_docs = "warn"
+bare_urls = "warn"

+ 2 - 2
crates/cashu/src/nuts/nut01/public_key.rs

@@ -140,7 +140,7 @@ mod tests {
     use super::*;
 
     #[test]
-    pub fn test_public_key_from_hex() {
+    fn test_public_key_from_hex() {
         // Compressed
         assert!(PublicKey::from_hex(
             "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"
@@ -149,7 +149,7 @@ mod tests {
     }
 
     #[test]
-    pub fn test_invalid_public_key_from_hex() {
+    fn test_invalid_public_key_from_hex() {
         // Uncompressed (is valid but is cashu must be compressed?)
         assert!(PublicKey::from_hex("04fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de3625246cb2c27dac965cb7200a5986467eee92eb7d496bbf1453b074e223e481")
             .is_err())

+ 26 - 1
crates/cashu/src/nuts/nut02.rs

@@ -92,7 +92,7 @@ impl fmt::Display for KeySetVersion {
 }
 
 /// Keyset ID bytes
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
 pub enum IdBytes {
     /// Bytes for v1
@@ -157,6 +157,11 @@ impl Id {
     /// 4 - If a final expiration is specified, convert it into a radix-10 string and concatenate it (e.g "final_expiry:1896187313")
     /// 5 - HASH_SHA256 the concatenated byte array and take the first 31 bytes
     /// 6 - prefix it with a keyset ID version byte
+    ///
+    /// # Panics
+    ///
+    /// This function will not panic under normal circumstances as the hash output
+    /// is always valid hex and the correct length.
     pub fn v2_from_data(map: &Keys, unit: &CurrencyUnit, expiry: Option<u64>) -> Self {
         let mut keys: Vec<(&Amount, &super::PublicKey)> = map.iter().collect();
         keys.sort_by_key(|(amt, _v)| *amt);
@@ -198,6 +203,11 @@ impl Id {
     ///   3. HASH_SHA256 the concatenated public keys
     ///   4. take the first 14 characters of the hex-encoded hash
     ///   5. prefix it with a keyset ID version byte
+    ///
+    /// # Panics
+    ///
+    /// This function will not panic under normal circumstances as the hash output
+    /// is always valid hex and the correct length.
     pub fn v1_from_keys(map: &Keys) -> Self {
         let mut keys: Vec<(&Amount, &super::PublicKey)> = map.iter().collect();
         keys.sort_by_key(|(amt, _v)| *amt);
@@ -573,6 +583,11 @@ pub struct MintKeySet {
 #[cfg(feature = "mint")]
 impl MintKeySet {
     /// Generate new [`MintKeySet`]
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if the RNG fails or if key derivation fails,
+    /// which should not happen under normal circumstances.
     pub fn generate<C: secp256k1::Signing>(
         secp: &Secp256k1<C>,
         xpriv: Xpriv,
@@ -614,6 +629,11 @@ impl MintKeySet {
     }
 
     /// Generate new [`MintKeySet`] from seed
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if the RNG fails or if key derivation fails,
+    /// which should not happen under normal circumstances.
     pub fn generate_from_seed<C: secp256k1::Signing>(
         secp: &Secp256k1<C>,
         seed: &[u8],
@@ -637,6 +657,11 @@ impl MintKeySet {
     }
 
     /// Generate new [`MintKeySet`] from xpriv
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if the RNG fails or if key derivation fails,
+    /// which should not happen under normal circumstances.
     pub fn generate_from_xpriv<C: secp256k1::Signing>(
         secp: &Secp256k1<C>,
         xpriv: Xpriv,

+ 2 - 2
crates/cashu/src/util/hex.rs

@@ -109,13 +109,13 @@ mod tests {
     }
 
     #[test]
-    pub fn test_invalid_length() {
+    fn test_invalid_length() {
         assert_eq!(decode("1").unwrap_err(), Error::OddLength);
         assert_eq!(decode("666f6f6261721").unwrap_err(), Error::OddLength);
     }
 
     #[test]
-    pub fn test_invalid_char() {
+    fn test_invalid_char() {
         assert_eq!(
             decode("66ag").unwrap_err(),
             Error::InvalidHexCharacter { c: 'g', index: 3 }

+ 1 - 0
crates/cdk-axum/src/cache/backend/memory.rs

@@ -11,6 +11,7 @@ use crate::cache::{HttpCacheKey, HttpCacheStorage, DEFAULT_TTI_SECS, DEFAULT_TTL
 ///
 /// The cache is limited to 10,000 entries and it is not shared between
 /// instances nor persisted.
+#[allow(missing_debug_implementations)]
 pub struct InMemoryHttpCache(pub Cache<HttpCacheKey, Vec<u8>>);
 
 impl Default for InMemoryHttpCache {

+ 9 - 0
crates/cdk-axum/src/cache/backend/redis.rs

@@ -14,6 +14,15 @@ pub struct HttpCacheRedis {
     client: redis::Client,
 }
 
+impl std::fmt::Debug for HttpCacheRedis {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("HttpCacheRedis")
+            .field("cache_ttl", &self.cache_ttl)
+            .field("prefix", &self.prefix)
+            .finish_non_exhaustive()
+    }
+}
+
 /// Configuration for the Redis cache storage.
 #[derive(Debug, Clone, Serialize, Deserialize, Default)]
 pub struct Config {

+ 9 - 0
crates/cdk-axum/src/cache/mod.rs

@@ -45,6 +45,15 @@ pub struct HttpCache {
     storage: Arc<Box<dyn HttpCacheStorage + Send + Sync>>,
 }
 
+impl std::fmt::Debug for HttpCache {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("HttpCache")
+            .field("ttl", &self.ttl)
+            .field("tti", &self.tti)
+            .finish_non_exhaustive()
+    }
+}
+
 impl Default for HttpCache {
     fn default() -> Self {
         Self::new(

+ 2 - 0
crates/cdk-axum/src/lib.rs

@@ -58,6 +58,7 @@ use swagger_imports::*;
 
 /// CDK Mint State
 #[derive(Clone)]
+#[allow(missing_debug_implementations)]
 pub struct MintState {
     mint: Arc<Mint>,
     cache: Arc<cache::HttpCache>,
@@ -91,6 +92,7 @@ macro_rules! define_api_doc {
             )
         )]
         /// Swagger api docs
+        #[allow(missing_debug_implementations)]
         pub struct ApiDoc;
     };
 }

+ 4 - 1
crates/cdk-cli/src/sub_commands/melt.rs

@@ -269,7 +269,10 @@ pub async fn pay(
                     multi_mint_wallet.melt(&bolt11_str, options, None).await?
                 };
 
-                println!("Payment successful: {:?}", melted);
+                println!(
+                    "Payment successful: state={}, amount={}, fee_paid={}",
+                    melted.state, melted.amount, melted.fee_paid
+                );
                 if let Some(preimage) = melted.preimage {
                     println!("Payment preimage: {}", preimage);
                 }

+ 33 - 5
crates/cdk-cli/src/sub_commands/mint.rs

@@ -58,7 +58,13 @@ pub async fn mint(
                     .ok_or(anyhow!("Amount must be defined"))?;
                 let quote = wallet.mint_quote(Amount::from(amount), description).await?;
 
-                println!("Quote: {quote:#?}");
+                println!(
+                    "Quote: id={}, state={}, amount={}, expiry={}",
+                    quote.id,
+                    quote.state,
+                    quote.amount.map_or("none".to_string(), |a| a.to_string()),
+                    quote.expiry
+                );
 
                 println!("Please pay: {}", quote.request);
 
@@ -66,12 +72,23 @@ pub async fn mint(
             }
             PaymentMethod::Known(KnownMethod::Bolt12) => {
                 let amount = sub_command_args.amount;
-                println!("{:?}", sub_command_args.single_use);
+                println!(
+                    "Single use: {}",
+                    sub_command_args
+                        .single_use
+                        .map_or("none".to_string(), |b| b.to_string())
+                );
                 let quote = wallet
                     .mint_bolt12_quote(amount.map(|a| a.into()), description)
                     .await?;
 
-                println!("Quote: {quote:#?}");
+                println!(
+                    "Quote: id={}, state={}, amount={}, expiry={}",
+                    quote.id,
+                    quote.state,
+                    quote.amount.map_or("none".to_string(), |a| a.to_string()),
+                    quote.expiry
+                );
 
                 println!("Please pay: {}", quote.request);
 
@@ -79,7 +96,12 @@ pub async fn mint(
             }
             _ => {
                 let amount = sub_command_args.amount;
-                println!("{:?}", sub_command_args.single_use);
+                println!(
+                    "Single use: {}",
+                    sub_command_args
+                        .single_use
+                        .map_or("none".to_string(), |b| b.to_string())
+                );
                 let quote = wallet
                     .mint_quote_unified(
                         amount.map(|a| a.into()),
@@ -89,7 +111,13 @@ pub async fn mint(
                     )
                     .await?;
 
-                println!("Quote: {quote:#?}");
+                println!(
+                    "Quote: id={}, state={}, amount={}, expiry={}",
+                    quote.id,
+                    quote.state,
+                    quote.amount.map_or("none".to_string(), |a| a.to_string()),
+                    quote.expiry
+                );
 
                 println!("Please pay: {}", quote.request);
 

+ 1 - 1
crates/cdk-cli/src/sub_commands/mint_info.rs

@@ -19,7 +19,7 @@ pub async fn mint_info(proxy: Option<Url>, sub_command_args: &MintInfoSubcommand
 
     let info = client.get_mint_info().await?;
 
-    println!("{info:#?}");
+    println!("{}", serde_json::to_string_pretty(&info)?);
 
     Ok(())
 }

+ 9 - 0
crates/cdk-cln/src/lib.rs

@@ -57,6 +57,15 @@ pub struct Cln {
     kv_store: DynKVStore,
 }
 
+impl std::fmt::Debug for Cln {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("Cln")
+            .field("rpc_socket", &self.rpc_socket)
+            .field("fee_reserve", &self.fee_reserve)
+            .finish_non_exhaustive()
+    }
+}
+
 impl Cln {
     /// Create new [`Cln`]
     pub async fn new(

+ 5 - 0
crates/cdk-common/src/common.rs

@@ -66,6 +66,11 @@ impl Melted {
     }
 
     /// Total amount melted
+    ///
+    /// # Panics
+    ///
+    /// Panics if the sum of `amount` and `fee_paid` overflows. This should not
+    /// happen as the fee is validated when calculated.
     pub fn total_amount(&self) -> Amount {
         self.amount
             .checked_add(self.fee_paid)

+ 1 - 1
crates/cdk-common/src/database/mint/test/mod.rs

@@ -2,7 +2,7 @@
 //!
 //! This set is generic and checks the default and expected behaviour for a mint database
 //! implementation
-#![allow(clippy::unwrap_used)]
+#![allow(clippy::unwrap_used, clippy::missing_panics_doc)]
 use std::str::FromStr;
 use std::sync::atomic::{AtomicU64, Ordering};
 use std::time::{SystemTime, UNIX_EPOCH};

+ 1 - 1
crates/cdk-common/src/database/wallet/test/mod.rs

@@ -3,7 +3,7 @@
 //! This module contains generic tests for wallet database implementations.
 //! These tests can be used to verify any wallet database implementation
 //! by using the `wallet_db_test!` macro.
-#![allow(clippy::unwrap_used)]
+#![allow(clippy::unwrap_used, clippy::missing_panics_doc)]
 
 use std::collections::{BTreeMap, HashMap};
 use std::str::FromStr;

+ 6 - 0
crates/cdk-common/src/error.rs

@@ -316,6 +316,12 @@ pub enum Error {
     /// KV Store invalid key or namespace
     #[error("Invalid KV store key or namespace: {0}")]
     KVStoreInvalidKey(String),
+    /// Invalid response from mint
+    #[error("Invalid mint response: {0}")]
+    InvalidMintResponse(String),
+    /// Subscription error
+    #[error("Subscription error: {0}")]
+    SubscriptionError(String),
     /// Custom Error
     #[error("`{0}`")]
     Custom(String),

+ 1 - 0
crates/cdk-common/src/mint.rs

@@ -274,6 +274,7 @@ impl Saga {
 }
 
 /// Operation
+#[derive(Debug)]
 pub struct Operation {
     id: Uuid,
     kind: OperationKind,

+ 1 - 1
crates/cdk-common/src/payment.rs

@@ -521,7 +521,7 @@ impl TryFrom<Value> for SettingsResponse {
 /// This wrapper implements the Decorator pattern to collect metrics on all
 /// MintPayment trait methods. It wraps any existing MintPayment implementation
 /// and automatically records timing and operation metrics.
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 #[cfg(feature = "prometheus")]
 pub struct MetricsMintPayment<T> {
     inner: T,

+ 1 - 0
crates/cdk-common/src/pub_sub/pubsub.rs

@@ -30,6 +30,7 @@ pub type TopicTree<T> = Arc<
 >;
 
 /// Manager
+#[allow(missing_debug_implementations)]
 pub struct Pubsub<S>
 where
     S: Spec + 'static,

+ 4 - 0
crates/cdk-common/src/pub_sub/remote_consumer.rs

@@ -38,6 +38,7 @@ type ActiveSubscriptions<S> =
 type CacheEvent<S> = HashMap<<<S as Spec>::Event as Event>::Topic, <S as Spec>::Event>;
 
 /// Subscription consumer
+#[allow(missing_debug_implementations)]
 pub struct Consumer<T>
 where
     T: Transport + 'static,
@@ -58,6 +59,7 @@ where
 }
 
 /// Remote consumer
+#[allow(missing_debug_implementations)]
 pub struct RemoteActiveConsumer<T>
 where
     T: Transport + 'static,
@@ -106,6 +108,7 @@ where
 
 /// Struct to relay events from Poll and Streams from the external subscription to the local
 /// subscribers
+#[allow(missing_debug_implementations)]
 pub struct InternalRelay<S>
 where
     S: Spec + 'static,
@@ -398,6 +401,7 @@ where
 pub type SubscribeMessage<S> = (<S as Spec>::SubscriptionId, <S as Spec>::Topic);
 
 /// Messages sent from the [`Consumer`] to the [`Transport`] background loop.
+#[allow(missing_debug_implementations)]
 pub enum StreamCtrl<S>
 where
     S: Spec + 'static,

+ 1 - 0
crates/cdk-common/src/pub_sub/subscriber.rs

@@ -24,6 +24,7 @@ pub trait SubscriptionRequest {
 }
 
 /// Active Subscription
+#[allow(missing_debug_implementations)]
 pub struct ActiveSubscription<S>
 where
     S: Spec + 'static,

+ 4 - 0
crates/cdk-common/src/task.rs

@@ -9,6 +9,10 @@ use tokio::task::JoinHandle;
 static GLOBAL_RUNTIME: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
 
 /// Spawns a new asynchronous task returning nothing
+///
+/// # Panics
+///
+/// Panics if the global Tokio runtime cannot be created when no runtime exists on the current thread.
 #[cfg(not(target_arch = "wasm32"))]
 pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
 where

+ 5 - 1
crates/cdk-fake-wallet/src/lib.rs

@@ -331,7 +331,7 @@ impl SecondaryRepaymentQueue {
 }
 
 /// Fake Wallet
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct FakeWallet {
     fee_reserve: FeeReserve,
     sender: tokio::sync::mpsc::Sender<WaitPaymentResponse>,
@@ -822,6 +822,10 @@ impl MintPayment for FakeWallet {
 }
 
 /// Create fake invoice
+///
+/// # Panics
+///
+/// Panics if the hardcoded secret key or payment hash bytes are invalid.
 #[instrument]
 pub fn create_fake_invoice(amount_msat: u64, description: String) -> Bolt11Invoice {
     let private_key = SecretKey::from_slice(

+ 1 - 0
crates/cdk-ffi/src/lib.rs

@@ -3,6 +3,7 @@
 //! UniFFI bindings for the CDK Wallet and related types.
 
 #![warn(clippy::unused_async)]
+#![allow(missing_debug_implementations)]
 
 pub mod database;
 pub mod error;

+ 4 - 0
crates/cdk-ffi/src/multi_mint_wallet.rs

@@ -181,6 +181,10 @@ impl MultiMintWallet {
     }
 
     /// Remove mint from MultiMintWallet
+    ///
+    /// # Panics
+    ///
+    /// Panics if the hardcoded fallback URL is invalid (should never happen).
     pub async fn remove_mint(&self, mint_url: MintUrl) {
         let url_str = mint_url.url.clone();
         let cdk_mint_url: cdk::mint_url::MintUrl = mint_url.try_into().unwrap_or_else(|_| {

+ 1 - 1
crates/cdk-ffi/src/wallet.rs

@@ -494,7 +494,7 @@ impl Wallet {
     ) -> Result<std::sync::Arc<ActiveSubscription>, FfiError> {
         let cdk_params: cdk::nuts::nut17::Params<Arc<String>> = params.clone().into();
         let sub_id = cdk_params.id.to_string();
-        let active_sub = self.inner.subscribe(cdk_params).await;
+        let active_sub = self.inner.subscribe(cdk_params).await?;
         Ok(std::sync::Arc::new(ActiveSubscription::new(
             active_sub, sub_id,
         )))

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

@@ -157,7 +157,8 @@ async fn test_websocket_connection() {
         .subscribe(WalletSubscription::Bolt11MintQuoteState(vec![mint_quote
             .id
             .clone()]))
-        .await;
+        .await
+        .expect("failed to subscribe");
 
     // First check we get the unpaid state
     let msg = timeout(Duration::from_secs(10), subscription.recv())

+ 9 - 0
crates/cdk-ldk-node/src/lib.rs

@@ -47,6 +47,15 @@ pub struct CdkLdkNode {
     web_addr: Option<SocketAddr>,
 }
 
+impl std::fmt::Debug for CdkLdkNode {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("CdkLdkNode")
+            .field("fee_reserve", &self.fee_reserve)
+            .field("web_addr", &self.web_addr)
+            .finish_non_exhaustive()
+    }
+}
+
 /// Configuration for connecting to Bitcoin RPC
 ///
 /// Contains the necessary connection parameters for Bitcoin Core RPC interface.

+ 8 - 0
crates/cdk-lnbits/src/lib.rs

@@ -37,6 +37,14 @@ pub struct LNbits {
     settings: SettingsResponse,
 }
 
+impl std::fmt::Debug for LNbits {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("LNbits")
+            .field("fee_reserve", &self.fee_reserve)
+            .finish_non_exhaustive()
+    }
+}
+
 impl LNbits {
     /// Create new [`LNbits`] wallet
     #[allow(clippy::too_many_arguments)]

+ 8 - 0
crates/cdk-lnd/src/lib.rs

@@ -62,6 +62,14 @@ pub struct Lnd {
     unit: CurrencyUnit,
 }
 
+impl std::fmt::Debug for Lnd {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("Lnd")
+            .field("fee_reserve", &self.fee_reserve)
+            .finish_non_exhaustive()
+    }
+}
+
 impl Lnd {
     /// Maximum number of attempts at a partial payment
     pub const MAX_ROUTE_RETRIES: usize = 50;

+ 6 - 3
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/rotate_next_keyset.rs

@@ -10,7 +10,7 @@ use crate::RotateNextKeysetRequest;
 ///
 /// This command instructs the mint to rotate to a new keyset, which generates new keys
 /// for signing tokens of the specified unit type.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct RotateNextKeysetCommand {
     /// The unit type for the keyset (e.g., "sat")
     #[arg(short, long)]
@@ -56,8 +56,11 @@ pub async fn rotate_next_keyset(
     let response = response.into_inner();
 
     println!(
-        "Rotated to new keyset {} for unit {} with amounts {:?} and fee of {}",
-        response.id, response.unit, response.amounts, response.input_fee_ppk
+        "Rotated to new keyset {} for unit {} with amounts {} and fee of {}",
+        response.id,
+        response.unit,
+        serde_json::to_string(&response.amounts)?,
+        response.input_fee_ppk
     );
 
     Ok(())

+ 2 - 2
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_contact.rs

@@ -10,7 +10,7 @@ use crate::UpdateContactRequest;
 ///
 /// This command adds a new contact method with associated information to the mint.
 /// Contact methods allow users to reach the mint operators through various channels.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct AddContactCommand {
     /// The contact method type (e.g., "email", "twitter", "telegram")
     method: String,
@@ -43,7 +43,7 @@ pub async fn add_contact(
 ///
 /// This command removes an existing contact method and its associated information
 /// from the mint's available contact methods.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct RemoveContactCommand {
     /// The contact method type to remove (e.g., "email", "twitter", "telegram")
     method: String,

+ 1 - 1
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_icon_url.rs

@@ -10,7 +10,7 @@ use crate::UpdateIconUrlRequest;
 ///
 /// This command sets a new icon URL for the mint, which is used to visually
 /// identify the mint in wallet applications and other client interfaces.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct UpdateIconUrlCommand {
     /// The URL to the mint's icon image
     name: String,

+ 1 - 1
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_long_description.rs

@@ -10,7 +10,7 @@ use crate::UpdateDescriptionRequest;
 ///
 /// This command sets a new long description for the mint, which provides detailed
 /// information about the mint's purpose, operation, and policies.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct UpdateLongDescriptionCommand {
     /// The new long description text for the mint
     description: String,

+ 1 - 1
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_motd.rs

@@ -10,7 +10,7 @@ use crate::UpdateMotdRequest;
 ///
 /// This command sets a new message of the day (MOTD) for the mint, which can be used
 /// to communicate important announcements, updates, or status information to users.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct UpdateMotdCommand {
     /// The new message of the day text
     motd: String,

+ 1 - 1
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_name.rs

@@ -10,7 +10,7 @@ use crate::UpdateNameRequest;
 ///
 /// This command sets a new display name for the mint, which is used to identify
 /// the mint in wallet applications and other client interfaces.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct UpdateNameCommand {
     /// The new name for the mint
     name: String,

+ 1 - 1
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_nut04.rs

@@ -11,7 +11,7 @@ use crate::{MintMethodOptions, UpdateNut04Request};
 /// NUT-04 defines how tokens are minted in exchange for external payments. This command
 /// allows configuring the available token units, payment methods, amounts, and other settings
 /// for the minting process.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct UpdateNut04Command {
     /// The token unit type (e.g., "sat")
     #[arg(short, long)]

+ 1 - 1
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_nut04_quote.rs

@@ -10,7 +10,7 @@ use crate::UpdateNut04QuoteRequest;
 ///
 /// NUT-04 quotes represent pending mint operations. This command allows updating
 /// the state of a quote (e.g., marking it as paid) to process the minting of tokens.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct UpdateNut04QuoteCommand {
     /// The ID of the quote to update
     quote_id: String,

+ 1 - 1
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_nut05.rs

@@ -11,7 +11,7 @@ use crate::{MeltMethodOptions, UpdateNut05Request};
 /// NUT-05 defines how tokens are melted (redeemed) in exchange for external payments.
 /// This command allows configuring the available token units, payment methods, amounts,
 /// and other settings for the melting process.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct UpdateNut05Command {
     /// The token unit type (e.g., "sat")
     #[arg(short, long)]

+ 1 - 1
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_short_description.rs

@@ -11,7 +11,7 @@ use crate::UpdateDescriptionRequest;
 /// This command sets a new short description for the mint, which provides a brief
 /// summary of the mint's purpose or characteristics. The short description is typically
 /// displayed in wallets and client interfaces.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct UpdateShortDescriptionCommand {
     /// The new short description text for the mint
     description: String,

+ 2 - 2
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_ttl.rs

@@ -11,7 +11,7 @@ use crate::{GetQuoteTtlRequest, UpdateQuoteTtlRequest};
 /// This command configures how long mint and melt quotes remain valid before
 /// automatically expiring. Quote TTL settings help manage pending operations and
 /// resource usage on the mint.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct UpdateQuoteTtlCommand {
     /// The TTL (in seconds) for mint quotes
     #[arg(long)]
@@ -44,7 +44,7 @@ pub async fn update_quote_ttl(
 /// Command to get the current time-to-live (TTL) settings for quotes
 ///
 /// This command retrieves the current TTL settings for mint and melt quotes.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct GetQuoteTtlCommand {}
 
 /// Executes the get_quote_ttl command against the mint server

+ 2 - 2
crates/cdk-mint-rpc/src/mint_rpc_cli/subcommands/update_urls.rs

@@ -11,7 +11,7 @@ use crate::UpdateUrlRequest;
 /// This command adds a new URL to the mint's list of available endpoints.
 /// Multiple URLs allow clients to access the mint through different endpoints,
 /// providing redundancy and flexibility.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct AddUrlCommand {
     /// The URL to add to the mint's endpoints
     url: String,
@@ -41,7 +41,7 @@ pub async fn add_url(
 ///
 /// This command removes an existing URL from the mint's list of available endpoints.
 /// This can be used to retire endpoints that are no longer in use or available.
-#[derive(Args)]
+#[derive(Args, Debug)]
 pub struct RemoveUrlCommand {
     /// The URL to remove from the mint's endpoints
     url: String,

+ 1 - 0
crates/cdk-mint-rpc/src/proto/server.rs

@@ -42,6 +42,7 @@ pub enum Error {
 
 /// CDK Mint RPC Server
 #[derive(Clone)]
+#[allow(missing_debug_implementations)]
 pub struct MintRPCServer {
     socket_addr: SocketAddr,
     mint: Arc<Mint>,

+ 1 - 1
crates/cdk-mintd/src/cli.rs

@@ -2,7 +2,7 @@ use std::path::PathBuf;
 
 use clap::Parser;
 
-#[derive(Parser)]
+#[derive(Debug, Parser)]
 #[command(about = "A cashu mint written in rust", author = env!("CARGO_PKG_AUTHORS"), version = env!("CARGO_PKG_VERSION"))]
 pub struct CLIArgs {
     #[arg(

+ 4 - 1
crates/cdk-mintd/src/setup.rs

@@ -354,7 +354,10 @@ impl LnBackendSetup for config::LdkNode {
             Some(cdk_ldk_node::CdkLdkNode::default_web_addr())
         };
 
-        println!("webserver: {:?}", webserver_addr);
+        println!(
+            "webserver: {}",
+            webserver_addr.map_or("none".to_string(), |a| a.to_string())
+        );
 
         ldk_node.set_web_addr(webserver_addr);
 

+ 7 - 0
crates/cdk-payment-processor/src/proto/client.rs

@@ -29,6 +29,13 @@ pub struct PaymentProcessorClient {
     cancel_incoming_payment_listener: CancellationToken,
 }
 
+impl std::fmt::Debug for PaymentProcessorClient {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("PaymentProcessorClient")
+            .finish_non_exhaustive()
+    }
+}
+
 impl PaymentProcessorClient {
     /// Payment Processor
     pub async fn new(addr: &str, port: u16, tls_dir: Option<PathBuf>) -> anyhow::Result<Self> {

+ 8 - 0
crates/cdk-payment-processor/src/proto/server.rs

@@ -33,6 +33,14 @@ pub struct PaymentProcessorServer {
     handle: Option<Arc<JoinHandle<anyhow::Result<()>>>>,
 }
 
+impl std::fmt::Debug for PaymentProcessorServer {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("PaymentProcessorServer")
+            .field("socket_addr", &self.socket_addr)
+            .finish_non_exhaustive()
+    }
+}
+
 impl PaymentProcessorServer {
     /// Create new [`PaymentProcessorServer`]
     pub fn new(

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

@@ -134,6 +134,7 @@ pub struct WalletRedbDatabase {
 }
 
 /// Redb Wallet Transaction
+#[allow(missing_debug_implementations)]
 pub struct RedbWalletTransaction {
     write_txn: Option<redb::WriteTransaction>,
 }

+ 1 - 1
crates/cdk-signatory/src/bin/cli/mod.rs

@@ -138,7 +138,7 @@ pub async fn cli_main() -> Result<()> {
                     #[cfg(feature = "sqlcipher")]
                     let db = {
                         match args.password {
-                            Some(pass) => MintSqliteDatabase::new((&sql_path, pass)).await?,
+                            Some(pass) => MintSqliteDatabase::new((sql_path.clone(), pass)).await?,
                             None => bail!("Missing database password"),
                         }
                     };

+ 5 - 0
crates/cdk-signatory/src/db_signatory.rs

@@ -22,6 +22,7 @@ use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet, Signatory
 ///
 /// The private keys and the all key-related data is stored in memory, in the same process, but it
 /// is not accessible from the outside.
+#[allow(missing_debug_implementations)]
 pub struct DbSignatory {
     keysets: RwLock<HashMap<Id, (MintKeySetInfo, MintKeySet)>>,
     active_keysets: RwLock<HashMap<CurrencyUnit, Id>>,
@@ -34,6 +35,10 @@ pub struct DbSignatory {
 
 impl DbSignatory {
     /// Creates a new MemorySignatory instance
+    ///
+    /// # Panics
+    ///
+    /// Panics if the seed produces an invalid master key (should never happen with valid entropy).
     pub async fn new(
         localstore: Arc<dyn database::MintKeysDatabase<Err = database::Error> + Send + Sync>,
         seed: &[u8],

+ 1 - 0
crates/cdk-signatory/src/embedded.rs

@@ -30,6 +30,7 @@ enum Request {
 /// This implements the actor model, ensuring the Signatory and their private key is moved from the
 /// main thread to their own tokio task, and communicates with the main program by passing messages,
 /// an extra layer of security to move the keys to another layer.
+#[allow(missing_debug_implementations)]
 pub struct Service {
     pipeline: mpsc::Sender<Request>,
     runner: Option<JoinHandle<()>>,

+ 0 - 2
crates/cdk-signatory/src/lib.rs

@@ -5,8 +5,6 @@
 //!
 //! Even if it is embedded in the same process, the keys are not accessible from the outside of this
 //! module, all communication is done through the Signatory trait and the signatory manager.
-#![deny(missing_docs)]
-#![deny(warnings)]
 
 #[cfg(feature = "grpc")]
 mod proto;

+ 1 - 0
crates/cdk-signatory/src/proto/client.rs

@@ -8,6 +8,7 @@ use crate::proto::signatory_client::SignatoryClient;
 use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet, SignatoryKeysets};
 
 /// A client for the Signatory service.
+#[allow(missing_debug_implementations)]
 pub struct SignatoryRpcClient {
     client: SignatoryClient<tonic::transport::Channel>,
     url: String,

+ 5 - 0
crates/cdk-signatory/src/proto/server.rs

@@ -56,11 +56,13 @@ where
     ) -> Result<Response<proto::BlindSignResponse>, Status> {
         let metadata = request.metadata();
         let signatory = self.load_signatory(metadata).await?;
+
         let blinded_messages = request.into_inner().blinded_messages;
         let mut converted_messages = Vec::with_capacity(blinded_messages.len());
         for msg in blinded_messages {
             converted_messages.push(msg.try_into()?);
         }
+
         let result = match signatory.blind_sign(converted_messages).await {
             Ok(blind_signatures) => proto::BlindSignResponse {
                 sigs: Some(proto::BlindSignatures {
@@ -87,11 +89,14 @@ where
     ) -> Result<Response<proto::BooleanResponse>, Status> {
         let metadata = request.metadata();
         let signatory = self.load_signatory(metadata).await?;
+
         let proofs = request.into_inner().proof;
+
         let mut converted_proofs = Vec::with_capacity(proofs.len());
         for p in proofs {
             converted_proofs.push(p.try_into()?);
         }
+
         let result = match signatory.verify_proofs(converted_proofs).await {
             Ok(()) => proto::BooleanResponse {
                 success: true,

+ 1 - 0
crates/cdk-sql-common/src/database.rs

@@ -167,6 +167,7 @@ where
 }
 
 /// Generic transaction handler for SQLite
+#[allow(missing_debug_implementations)]
 pub struct GenericTransactionHandler<W>(PhantomData<W>);
 
 #[async_trait::async_trait]

+ 1 - 0
crates/cdk-sql-common/src/mint/mod.rs

@@ -49,6 +49,7 @@ where
 }
 
 /// SQL Transaction Writer
+#[allow(missing_debug_implementations)]
 pub struct SQLTransaction<RM>
 where
     RM: DatabasePool + 'static,

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

@@ -44,6 +44,7 @@ where
 }
 
 /// SQL Transaction Writer
+#[allow(missing_debug_implementations)]
 pub struct SQLWalletTransaction<RM>
 where
     RM: DatabasePool + 'static,

+ 1 - 0
crates/cdk-sqlite/src/async_sqlite.rs

@@ -69,6 +69,7 @@ fn to_sqlite_error(err: SqliteError) -> Error {
 }
 
 /// SQLite trasanction handler
+#[allow(missing_debug_implementations)]
 pub struct SQLiteTransactionHandler;
 
 #[async_trait::async_trait]

+ 9 - 0
crates/cdk/src/mint/builder.rs

@@ -39,6 +39,15 @@ pub struct MintBuilder {
     custom_paths: HashMap<CurrencyUnit, DerivationPath>,
 }
 
+impl std::fmt::Debug for MintBuilder {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("MintBuilder")
+            .field("mint_info", &self.mint_info)
+            .field("supported_units", &self.supported_units)
+            .finish_non_exhaustive()
+    }
+}
+
 impl MintBuilder {
     /// New [`MintBuilder`]
     pub fn new(localstore: DynMintDatabase) -> MintBuilder {

+ 9 - 1
crates/cdk/src/mint/mod.rs

@@ -79,6 +79,12 @@ pub struct Mint {
     task_state: Arc<Mutex<TaskState>>,
 }
 
+impl std::fmt::Debug for Mint {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("Mint").finish_non_exhaustive()
+    }
+}
+
 /// State for managing background tasks
 #[derive(Default)]
 struct TaskState {
@@ -931,7 +937,9 @@ impl Mint {
                 .get_blind_signatures(&blinded_message)
                 .await?;
 
-            assert_eq!(blinded_signatures.len(), output_len);
+            if blinded_signatures.len() != output_len {
+                return Err(Error::Internal);
+            }
 
             for (blinded_message, blinded_signature) in
                 request.outputs.into_iter().zip(blinded_signatures)

+ 2 - 0
crates/cdk/src/mint/subscription.rs

@@ -22,6 +22,7 @@ use crate::event::MintEvent;
 
 /// Mint subtopics
 #[derive(Clone)]
+#[allow(missing_debug_implementations)]
 pub struct MintPubSubSpec {
     db: DynMintDatabase,
     payment_processors: Arc<HashMap<PaymentProcessorKey, DynMintPayment>>,
@@ -175,6 +176,7 @@ impl Spec for MintPubSubSpec {
 }
 
 /// PubsubManager
+#[allow(missing_debug_implementations)]
 pub struct PubSubManager(Pubsub<MintPubSubSpec>);
 
 impl PubSubManager {

+ 2 - 8
crates/cdk/src/mint/verification.rs

@@ -94,10 +94,7 @@ impl Mint {
             return Err(Error::MultipleUnits);
         }
 
-        Ok(keyset_units
-            .into_iter()
-            .next()
-            .expect("Length is check above"))
+        keyset_units.into_iter().next().ok_or(Error::Internal)
     }
 
     /// Verify input keyset
@@ -135,10 +132,7 @@ impl Mint {
             return Err(Error::MultipleUnits);
         }
 
-        Ok(keyset_units
-            .into_iter()
-            .next()
-            .expect("Length is check above"))
+        keyset_units.into_iter().next().ok_or(Error::Internal)
     }
 
     /// Verifies that the outputs have not already been signed

+ 7 - 1
crates/cdk/src/wallet/auth/auth_wallet.rs

@@ -416,7 +416,13 @@ impl AuthWallet {
 
         // Verify the signature DLEQ is valid
         {
-            assert!(mint_res.signatures.len() == premint_secrets.secrets.len());
+            if mint_res.signatures.len() != premint_secrets.secrets.len() {
+                return Err(Error::InvalidMintResponse(format!(
+                    "mint auth signatures ({}) does not match secrets sent ({})",
+                    mint_res.signatures.len(),
+                    premint_secrets.secrets.len()
+                )));
+            }
             for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
                 let keys = self.load_keyset_keys(sig.keyset_id).await?;
                 let key = keys.amount_key(sig.amount).ok_or(Error::AmountKey)?;

+ 24 - 4
crates/cdk/src/wallet/builder.rs

@@ -34,6 +34,16 @@ pub struct WalletBuilder {
     metadata_caches: HashMap<MintUrl, Arc<MintMetadataCache>>,
 }
 
+impl std::fmt::Debug for WalletBuilder {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("WalletBuilder")
+            .field("mint_url", &self.mint_url)
+            .field("unit", &self.unit)
+            .field("target_proof_count", &self.target_proof_count)
+            .finish_non_exhaustive()
+    }
+}
+
 impl Default for WalletBuilder {
     fn default() -> Self {
         Self {
@@ -153,10 +163,20 @@ impl WalletBuilder {
     }
 
     /// Set auth CAT (Clear Auth Token)
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if `mint_url` or `localstore` have not been set on the builder.
     #[cfg(feature = "auth")]
-    pub fn set_auth_cat(mut self, cat: String) -> Self {
-        let mint_url = self.mint_url.clone().expect("Mint URL required");
-        let localstore = self.localstore.clone().expect("Localstore required");
+    pub fn set_auth_cat(mut self, cat: String) -> Result<Self, Error> {
+        let mint_url = self
+            .mint_url
+            .clone()
+            .ok_or_else(|| Error::Custom("Mint URL required".to_string()))?;
+        let localstore = self
+            .localstore
+            .clone()
+            .ok_or_else(|| Error::Custom("Localstore required".to_string()))?;
 
         let metadata_cache = self.metadata_cache.clone().unwrap_or_else(|| {
             // Check if we already have a cache for this mint in the HashMap
@@ -176,7 +196,7 @@ impl WalletBuilder {
             HashMap::new(),
             None,
         ));
-        self
+        Ok(self)
     }
 
     /// Build the wallet

+ 12 - 3
crates/cdk/src/wallet/mod.rs

@@ -209,10 +209,13 @@ impl Wallet {
     }
 
     /// Subscribe to events
-    pub async fn subscribe<T: Into<WalletParams>>(&self, query: T) -> ActiveSubscription {
+    pub async fn subscribe<T: Into<WalletParams>>(
+        &self,
+        query: T,
+    ) -> Result<ActiveSubscription, Error> {
         self.subscription
             .subscribe(self.mint_url.clone(), query.into())
-            .expect("FIXME")
+            .map_err(|e| Error::SubscriptionError(e.to_string()))
     }
 
     /// Fee required to redeem proof set
@@ -508,7 +511,13 @@ impl Wallet {
 
                 // the response outputs and premint secrets should be the same after filtering
                 // blinded messages the mint did not have signatures for
-                assert_eq!(response.outputs.len(), premint_secrets.len());
+                if response.outputs.len() != premint_secrets.len() {
+                    return Err(Error::InvalidMintResponse(format!(
+                        "restore response outputs ({}) does not match premint secrets ({})",
+                        response.outputs.len(),
+                        premint_secrets.len()
+                    )));
+                }
 
                 let proofs = construct_proofs(
                     response.signatures,

+ 9 - 1
crates/cdk/src/wallet/multi_mint_wallet.rs

@@ -188,6 +188,14 @@ pub struct MultiMintWallet {
     shared_tor_transport: Option<TorAsync>,
 }
 
+impl std::fmt::Debug for MultiMintWallet {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("MultiMintWallet")
+            .field("unit", &self.unit)
+            .finish_non_exhaustive()
+    }
+}
+
 impl MultiMintWallet {
     /// Create a new [MultiMintWallet] for a specific currency unit
     pub async fn new(
@@ -985,7 +993,7 @@ impl MultiMintWallet {
             .subscribe(super::WalletSubscription::Bolt11MintQuoteState(vec![
                 final_mint_quote.id.clone(),
             ]))
-            .await;
+            .await?;
 
         // Step 2: Melt from source wallet using the final melt quote
         let melted = source_wallet.melt(&final_melt_quote.id).await?;

+ 13 - 1
crates/cdk/src/wallet/streams/payment.rs

@@ -21,6 +21,7 @@ type SubscribeReceived = (Option<MintEvent<String>>, Vec<ActiveSubscription>);
 type PaymentValue = (String, Option<Amount>);
 
 /// PaymentWaiter
+#[allow(missing_debug_implementations)]
 pub struct PaymentStream<'a> {
     wallet: Option<(&'a Wallet, Vec<WalletSubscription>)>,
     is_finalized: bool,
@@ -59,7 +60,18 @@ impl<'a> PaymentStream<'a> {
     fn poll_init_subscription(&mut self, cx: &mut std::task::Context<'_>) -> Option<()> {
         if let Some((wallet, filters)) = self.wallet.take() {
             self.subscriber_future = Some(Box::pin(async move {
-                join_all(filters.into_iter().map(|w| wallet.subscribe(w))).await
+                let results = join_all(filters.into_iter().map(|w| wallet.subscribe(w))).await;
+                // Collect successful subscriptions, log errors
+                results
+                    .into_iter()
+                    .filter_map(|r| match r {
+                        Ok(sub) => Some(sub),
+                        Err(e) => {
+                            tracing::warn!("Failed to create subscription: {}", e);
+                            None
+                        }
+                    })
+                    .collect::<Vec<_>>()
             }));
         }
 

+ 2 - 0
crates/cdk/src/wallet/streams/proof.rs

@@ -18,6 +18,7 @@ use super::{RecvFuture, WaitableEvent};
 use crate::Wallet;
 
 /// Proofs for many mint quotes, as they are minted, in streams
+#[allow(missing_debug_implementations)]
 pub struct MultipleMintQuoteProofStream<'a> {
     payment_stream: PaymentStream<'a>,
     wallet: &'a Wallet,
@@ -141,6 +142,7 @@ impl Stream for MultipleMintQuoteProofStream<'_> {
 }
 
 /// Proofs for a single mint quote
+#[allow(missing_debug_implementations)]
 pub struct SingleMintQuoteProofStream<'a>(MultipleMintQuoteProofStream<'a>);
 
 impl<'a> SingleMintQuoteProofStream<'a> {

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

@@ -106,7 +106,7 @@ impl SubscriptionManager {
 }
 
 /// MintSubTopics
-#[derive(Clone, Default)]
+#[derive(Clone, Default, Debug)]
 pub struct MintSubTopics {}
 
 #[async_trait::async_trait]