Explorar el Código

Introduce `cashu` to host the shared types, traits and other common code (#519)

---------

Co-authored-by: thesimplekid <tsk@thesimplekid.com>
C hace 2 meses
padre
commit
8fe0982c6d
Se han modificado 93 ficheros con 1343 adiciones y 965 borrados
  1. 13 4
      .github/workflows/ci.yml
  2. 1 0
      .gitignore
  3. 206 18
      Cargo.lock
  4. 41 0
      crates/cashu/Cargo.toml
  5. 0 0
      crates/cashu/src/amount.rs
  6. 0 0
      crates/cashu/src/dhke.rs
  7. 18 0
      crates/cashu/src/lib.rs
  8. 46 4
      crates/cashu/src/mint.rs
  9. 0 0
      crates/cashu/src/mint_url.rs
  10. 6 5
      crates/cashu/src/nuts/mod.rs
  11. 18 2
      crates/cashu/src/nuts/nut00/mod.rs
  12. 6 8
      crates/cashu/src/nuts/nut00/token.rs
  13. 0 0
      crates/cashu/src/nuts/nut01/mod.rs
  14. 0 0
      crates/cashu/src/nuts/nut01/public_key.rs
  15. 0 0
      crates/cashu/src/nuts/nut01/secret_key.rs
  16. 2 4
      crates/cashu/src/nuts/nut02.rs
  17. 4 1
      crates/cashu/src/nuts/nut03.rs
  18. 0 0
      crates/cashu/src/nuts/nut04.rs
  19. 0 0
      crates/cashu/src/nuts/nut05.rs
  20. 0 3
      crates/cashu/src/nuts/nut06.rs
  21. 0 0
      crates/cashu/src/nuts/nut07.rs
  22. 0 0
      crates/cashu/src/nuts/nut08.rs
  23. 0 0
      crates/cashu/src/nuts/nut09.rs
  24. 0 0
      crates/cashu/src/nuts/nut10.rs
  25. 4 10
      crates/cashu/src/nuts/nut11/mod.rs
  26. 0 0
      crates/cashu/src/nuts/nut11/serde_p2pk_witness.rs
  27. 0 0
      crates/cashu/src/nuts/nut12.rs
  28. 0 0
      crates/cashu/src/nuts/nut13.rs
  29. 0 0
      crates/cashu/src/nuts/nut14/mod.rs
  30. 0 0
      crates/cashu/src/nuts/nut14/serde_htlc_witness.rs
  31. 0 0
      crates/cashu/src/nuts/nut15.rs
  32. 9 59
      crates/cashu/src/nuts/nut17/mod.rs
  33. 39 69
      crates/cashu/src/nuts/nut17/ws.rs
  34. 9 11
      crates/cashu/src/nuts/nut18.rs
  35. 0 0
      crates/cashu/src/nuts/nut19.rs
  36. 0 0
      crates/cashu/src/nuts/nut20.rs
  37. 0 0
      crates/cashu/src/secret.rs
  38. 0 23
      crates/cashu/src/util/hex.rs
  39. 16 6
      crates/cashu/src/util/mod.rs
  40. 24 0
      crates/cashu/src/wallet.rs
  41. 7 5
      crates/cdk-axum/src/ws/mod.rs
  42. 4 2
      crates/cdk-axum/src/ws/subscribe.rs
  43. 1 1
      crates/cdk-axum/src/ws/unsubscribe.rs
  44. 1 1
      crates/cdk-cli/src/sub_commands/burn.rs
  45. 2 1
      crates/cdk-cli/src/sub_commands/melt.rs
  46. 1 1
      crates/cdk-cli/src/sub_commands/mint.rs
  47. 2 1
      crates/cdk-cli/src/sub_commands/receive.rs
  48. 1 1
      crates/cdk-cli/src/sub_commands/restore.rs
  49. 1 2
      crates/cdk-cli/src/sub_commands/send.rs
  50. 1 1
      crates/cdk-cli/src/sub_commands/update_mint_url.rs
  51. 43 0
      crates/cdk-common/Cargo.toml
  52. 0 0
      crates/cdk-common/src/common.rs
  53. 130 0
      crates/cdk-common/src/database/mint.rs
  54. 34 0
      crates/cdk-common/src/database/mod.rs
  55. 120 0
      crates/cdk-common/src/database/wallet.rs
  56. 9 10
      crates/cdk-common/src/error.rs
  57. 28 0
      crates/cdk-common/src/lib.rs
  58. 0 0
      crates/cdk-common/src/lightning.rs
  59. 0 0
      crates/cdk-common/src/pub_sub/index.rs
  60. 77 0
      crates/cdk-common/src/pub_sub/mod.rs
  61. 74 0
      crates/cdk-common/src/subscription.rs
  62. 52 0
      crates/cdk-common/src/ws.rs
  63. 9 6
      crates/cdk-integration-tests/tests/mint.rs
  64. 8 5
      crates/cdk-redb/Cargo.toml
  65. 7 7
      crates/cdk-redb/src/error.rs
  66. 2 3
      crates/cdk-redb/src/migrations.rs
  67. 3 4
      crates/cdk-redb/src/mint/migrations.rs
  68. 7 8
      crates/cdk-redb/src/mint/mod.rs
  69. 1 1
      crates/cdk-redb/src/wallet/migrations.rs
  70. 9 10
      crates/cdk-redb/src/wallet/mod.rs
  71. 15 10
      crates/cdk-sqlite/Cargo.toml
  72. 9 9
      crates/cdk-sqlite/src/mint/error.rs
  73. 11 12
      crates/cdk-sqlite/src/mint/mod.rs
  74. 9 9
      crates/cdk-sqlite/src/wallet/error.rs
  75. 11 12
      crates/cdk-sqlite/src/wallet/mod.rs
  76. 9 7
      crates/cdk/Cargo.toml
  77. 4 3
      crates/cdk/src/cdk_database/mint_memory.rs
  78. 2 281
      crates/cdk/src/cdk_database/mod.rs
  79. 1 2
      crates/cdk/src/cdk_database/wallet_memory.rs
  80. 1 2
      crates/cdk/src/fees.rs
  81. 10 16
      crates/cdk/src/lib.rs
  82. 23 25
      crates/cdk/src/mint/builder.rs
  83. 1 1
      crates/cdk/src/mint/melt.rs
  84. 10 49
      crates/cdk/src/mint/mod.rs
  85. 34 24
      crates/cdk/src/mint/subscription/manager.rs
  86. 12 0
      crates/cdk/src/mint/subscription/mod.rs
  87. 5 4
      crates/cdk/src/mint/subscription/on_subscription.rs
  88. 3 60
      crates/cdk/src/pub_sub.rs
  89. 66 110
      crates/cdk/src/wallet/client.rs
  90. 7 5
      crates/cdk/src/wallet/mod.rs
  91. 1 22
      crates/cdk/src/wallet/multi_mint_wallet.rs
  92. 1 1
      crates/cdk/src/wallet/subscription/mod.rs
  93. 2 4
      crates/cdk/src/wallet/subscription/ws.rs

+ 13 - 4
.github/workflows/ci.yml

@@ -49,6 +49,16 @@ jobs:
       matrix:
         build-args:
           [
+            -p cashu,
+            -p cashu --no-default-features,
+            -p cashu --no-default-features --features wallet,
+            -p cashu --no-default-features --features mint,
+            -p cashu --no-default-features --features "mint swagger",
+            -p cdk-common,
+            -p cdk-common --no-default-features,
+            -p cdk-common --no-default-features --features wallet,
+            -p cdk-common --no-default-features --features mint,
+            -p cdk-common --no-default-features --features "mint swagger",
             -p cdk,
             -p cdk --no-default-features,
             -p cdk --no-default-features --features wallet,
@@ -154,10 +164,9 @@ jobs:
       matrix:
         build-args:
           [
-            -p cdk,
-            -p cdk --no-default-features,
-            -p cdk --no-default-features --features wallet,
-            -p cdk --no-default-features --features mint,
+            -p cashu --no-default-features --features "wallet mint",
+            -p cdk-common --no-default-features --features "wallet mint",
+            -p cdk --no-default-features --features "mint mint",
             -p cdk-axum,
             -p cdk-axum --no-default-features --features redis,
             -p cdk-strike,

+ 1 - 0
.gitignore

@@ -1,4 +1,5 @@
 **/target
+.DS_Store
 .direnv
 .vscode/
 .idea/

+ 206 - 18
Cargo.lock

@@ -683,6 +683,28 @@ dependencies = [
 ]
 
 [[package]]
+name = "cashu"
+version = "0.6.0"
+dependencies = [
+ "bip39",
+ "bitcoin 0.32.5",
+ "cbor-diag",
+ "ciborium",
+ "instant",
+ "lightning-invoice",
+ "once_cell",
+ "rand",
+ "serde",
+ "serde_json",
+ "serde_with",
+ "thiserror 2.0.11",
+ "tracing",
+ "url",
+ "utoipa",
+ "uuid",
+]
+
+[[package]]
 name = "cast"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -718,9 +740,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.2.7"
+version = "1.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
+checksum = "ad0cf6e91fde44c773c6ee7ec6bba798504641a8bc2eb7e37a04ffbf4dfaa55a"
 dependencies = [
  "jobserver",
  "libc",
@@ -738,13 +760,12 @@ dependencies = [
  "bip39",
  "bitcoin 0.32.5",
  "cbor-diag",
+ "cdk-common",
  "ciborium",
  "criterion",
  "futures",
  "getrandom",
- "instant",
  "lightning-invoice",
- "once_cell",
  "rand",
  "regex",
  "reqwest",
@@ -821,6 +842,31 @@ dependencies = [
 ]
 
 [[package]]
+name = "cdk-common"
+version = "0.6.0"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "bip39",
+ "bitcoin 0.32.5",
+ "cashu",
+ "cbor-diag",
+ "ciborium",
+ "futures",
+ "instant",
+ "lightning-invoice",
+ "rand",
+ "serde",
+ "serde_json",
+ "serde_with",
+ "thiserror 2.0.11",
+ "tracing",
+ "url",
+ "utoipa",
+ "uuid",
+]
+
+[[package]]
 name = "cdk-fake-wallet"
 version = "0.6.0"
 dependencies = [
@@ -959,7 +1005,7 @@ name = "cdk-redb"
 version = "0.6.0"
 dependencies = [
  "async-trait",
- "cdk",
+ "cdk-common",
  "lightning-invoice",
  "redb",
  "serde",
@@ -990,7 +1036,7 @@ version = "0.6.0"
 dependencies = [
  "async-trait",
  "bitcoin 0.32.5",
- "cdk",
+ "cdk-common",
  "lightning-invoice",
  "serde_json",
  "sqlx",
@@ -1503,6 +1549,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
+name = "erased-serde"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d"
+dependencies = [
+ "serde",
+ "typeid",
+]
+
+[[package]]
 name = "errno"
 version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2547,9 +2603,12 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.22"
+version = "0.4.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "3d6ea2a48c204030ee31a7d7fc72c93294c92fe87ecb1789881c9543516e1a0d"
+dependencies = [
+ "value-bag",
+]
 
 [[package]]
 name = "lru"
@@ -2994,7 +3053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
 dependencies = [
  "memchr",
- "thiserror 2.0.10",
+ "thiserror 2.0.11",
  "ucd-trie",
 ]
 
@@ -3203,9 +3262,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.92"
+version = "1.0.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
 dependencies = [
  "unicode-ident",
 ]
@@ -3303,7 +3362,7 @@ dependencies = [
  "rustc-hash",
  "rustls 0.23.21",
  "socket2 0.5.8",
- "thiserror 2.0.10",
+ "thiserror 2.0.11",
  "tokio",
  "tracing",
 ]
@@ -3322,7 +3381,7 @@ dependencies = [
  "rustls 0.23.21",
  "rustls-pki-types",
  "slab",
- "thiserror 2.0.10",
+ "thiserror 2.0.11",
  "tinyvec",
  "tracing",
  "web-time",
@@ -4018,6 +4077,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "serde_fmt"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
 name = "serde_json"
 version = "1.0.135"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4358,6 +4426,84 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
+name = "sval"
+version = "2.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6dc0f9830c49db20e73273ffae9b5240f63c42e515af1da1fceefb69fceafd8"
+
+[[package]]
+name = "sval_buffer"
+version = "2.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "429922f7ad43c0ef8fd7309e14d750e38899e32eb7e8da656ea169dd28ee212f"
+dependencies = [
+ "sval",
+ "sval_ref",
+]
+
+[[package]]
+name = "sval_dynamic"
+version = "2.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68f16ff5d839396c11a30019b659b0976348f3803db0626f736764c473b50ff4"
+dependencies = [
+ "sval",
+]
+
+[[package]]
+name = "sval_fmt"
+version = "2.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c01c27a80b6151b0557f9ccbe89c11db571dc5f68113690c1e028d7e974bae94"
+dependencies = [
+ "itoa",
+ "ryu",
+ "sval",
+]
+
+[[package]]
+name = "sval_json"
+version = "2.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0deef63c70da622b2a8069d8600cf4b05396459e665862e7bdb290fd6cf3f155"
+dependencies = [
+ "itoa",
+ "ryu",
+ "sval",
+]
+
+[[package]]
+name = "sval_nested"
+version = "2.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a39ce5976ae1feb814c35d290cf7cf8cd4f045782fe1548d6bc32e21f6156e9f"
+dependencies = [
+ "sval",
+ "sval_buffer",
+ "sval_ref",
+]
+
+[[package]]
+name = "sval_ref"
+version = "2.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb7c6ee3751795a728bc9316a092023529ffea1783499afbc5c66f5fabebb1fa"
+dependencies = [
+ "sval",
+]
+
+[[package]]
+name = "sval_serde"
+version = "2.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a5572d0321b68109a343634e3a5d576bf131b82180c6c442dee06349dfc652a"
+dependencies = [
+ "serde",
+ "sval",
+ "sval_nested",
+]
+
+[[package]]
 name = "syn"
 version = "1.0.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4436,11 +4582,11 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "2.0.10"
+version = "2.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3"
+checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
 dependencies = [
- "thiserror-impl 2.0.10",
+ "thiserror-impl 2.0.11",
 ]
 
 [[package]]
@@ -4456,9 +4602,9 @@ dependencies = [
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.10"
+version = "2.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb"
+checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -4946,6 +5092,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "typeid"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
+
+[[package]]
 name = "typenum"
 version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5117,6 +5269,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
 
 [[package]]
+name = "value-bag"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2"
+dependencies = [
+ "value-bag-serde1",
+ "value-bag-sval2",
+]
+
+[[package]]
+name = "value-bag-serde1"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bb773bd36fd59c7ca6e336c94454d9c66386416734817927ac93d81cb3c5b0b"
+dependencies = [
+ "erased-serde",
+ "serde",
+ "serde_fmt",
+]
+
+[[package]]
+name = "value-bag-sval2"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53a916a702cac43a88694c97657d449775667bcd14b70419441d05b7fea4a83a"
+dependencies = [
+ "sval",
+ "sval_buffer",
+ "sval_dynamic",
+ "sval_fmt",
+ "sval_json",
+ "sval_ref",
+ "sval_serde",
+]
+
+[[package]]
 name = "vcpkg"
 version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"

+ 41 - 0
crates/cashu/Cargo.toml

@@ -0,0 +1,41 @@
+[package]
+name = "cashu"
+version = "0.6.0"
+edition = "2021"
+description = "Cashu shared types and crypto utilities, used as the foundation for the CDK and their crates"
+rust-version = "1.63.0"                                                                                      # MSRV
+
+[features]
+default = ["mint", "wallet"]
+swagger = ["dep:utoipa"]
+mint = ["dep:uuid"]
+wallet = []
+bench = []
+
+[dependencies]
+uuid = { version = "1", features = ["v4", "serde"], optional = true }
+bitcoin = { version = "0.32.2", features = [
+    "base64",
+    "serde",
+    "rand",
+    "rand-std",
+] }
+cbor-diag = "0.1.12"
+ciborium = { version = "0.2.2", default-features = false, features = ["std"] }
+once_cell = "1.20.2"
+serde = { version = "1", features = ["derive"] }
+lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }
+thiserror = "2"
+tracing = "0.1"
+url = "2.3"
+utoipa = { version = "4", optional = true }
+serde_json = "1"
+serde_with = "3"
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] }
+
+[dev-dependencies]
+rand = "0.8.5"
+bip39 = "2.0"
+uuid = { version = "1", features = ["v4", "serde"] }

+ 0 - 0
crates/cdk/src/amount.rs → crates/cashu/src/amount.rs


+ 0 - 0
crates/cdk/src/dhke.rs → crates/cashu/src/dhke.rs


+ 18 - 0
crates/cashu/src/lib.rs

@@ -0,0 +1,18 @@
+//! CDK common types and traits
+//!
+pub mod amount;
+pub mod dhke;
+#[cfg(feature = "mint")]
+pub mod mint;
+pub mod mint_url;
+pub mod nuts;
+pub mod secret;
+pub mod util;
+#[cfg(feature = "wallet")]
+pub mod wallet;
+
+pub use lightning_invoice::{self, Bolt11Invoice};
+
+pub use self::amount::Amount;
+pub use self::nuts::*;
+pub use self::util::SECP256K1;

+ 46 - 4
crates/cdk/src/mint/types.rs → crates/cashu/src/mint.rs

@@ -1,12 +1,12 @@
-//! Mint Types
+//! Mint types
 
+use bitcoin::bip32::DerivationPath;
 use serde::{Deserialize, Serialize};
 use uuid::Uuid;
 
-use super::{CurrencyUnit, PublicKey};
 use crate::mint_url::MintUrl;
 use crate::nuts::{MeltQuoteState, MintQuoteState};
-use crate::Amount;
+use crate::{Amount, CurrencyUnit, Id, KeySetInfo, PublicKey};
 
 /// Mint Quote Info
 #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
@@ -58,7 +58,7 @@ impl MintQuote {
     }
 }
 
-/// Melt Quote Info
+// Melt Quote Info
 #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MeltQuote {
     /// Quote id
@@ -112,3 +112,45 @@ impl MeltQuote {
         }
     }
 }
+
+/// Mint Keyset Info
+#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
+pub struct MintKeySetInfo {
+    /// Keyset [`Id`]
+    pub id: Id,
+    /// Keyset [`CurrencyUnit`]
+    pub unit: CurrencyUnit,
+    /// Keyset active or inactive
+    /// Mint will only issue new [`BlindSignature`] on active keysets
+    pub active: bool,
+    /// Starting unix time Keyset is valid from
+    pub valid_from: u64,
+    /// When the Keyset is valid to
+    /// This is not shown to the wallet and can only be used internally
+    pub valid_to: Option<u64>,
+    /// [`DerivationPath`] keyset
+    pub derivation_path: DerivationPath,
+    /// DerivationPath index of Keyset
+    pub derivation_path_index: Option<u32>,
+    /// Max order of keyset
+    pub max_order: u8,
+    /// Input Fee ppk
+    #[serde(default = "default_fee")]
+    pub input_fee_ppk: u64,
+}
+
+/// Default fee
+pub fn default_fee() -> u64 {
+    0
+}
+
+impl From<MintKeySetInfo> for KeySetInfo {
+    fn from(keyset_info: MintKeySetInfo) -> Self {
+        Self {
+            id: keyset_info.id,
+            unit: keyset_info.unit,
+            active: keyset_info.active,
+            input_fee_ppk: keyset_info.input_fee_ppk,
+        }
+    }
+}

+ 0 - 0
crates/cdk/src/mint_url.rs → crates/cashu/src/mint_url.rs


+ 6 - 5
crates/cdk/src/nuts/mod.rs → crates/cashu/src/nuts/mod.rs

@@ -15,6 +15,7 @@ pub mod nut09;
 pub mod nut10;
 pub mod nut11;
 pub mod nut12;
+#[cfg(feature = "wallet")]
 pub mod nut13;
 pub mod nut14;
 pub mod nut15;
@@ -24,9 +25,11 @@ pub mod nut19;
 pub mod nut20;
 
 pub use nut00::{
-    BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, PreMint, PreMintSecrets, Proof,
-    Proofs, ProofsMethods, Token, TokenV3, TokenV4, Witness,
+    BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, Proof, Proofs, ProofsMethods,
+    Token, TokenV3, TokenV4, Witness,
 };
+#[cfg(feature = "wallet")]
+pub use nut00::{PreMint, PreMintSecrets};
 pub use nut01::{Keys, KeysResponse, PublicKey, SecretKey};
 #[cfg(feature = "mint")]
 pub use nut02::MintKeySet;
@@ -50,7 +53,5 @@ pub use nut11::{Conditions, P2PKWitness, SigFlag, SpendingConditions};
 pub use nut12::{BlindSignatureDleq, ProofDleq};
 pub use nut14::HTLCWitness;
 pub use nut15::{Mpp, MppMethodSettings, Settings as NUT15Settings};
-#[cfg(feature = "mint")]
-pub use nut17::PubSubManager;
-pub use nut17::{NotificationPayload, SupportedSettings as Nut17SupportedSettings};
+pub use nut17::NotificationPayload;
 pub use nut18::{PaymentRequest, PaymentRequestPayload, Transport};

+ 18 - 2
crates/cdk/src/nuts/nut00/mod.rs → crates/cashu/src/nuts/nut00/mod.rs

@@ -11,11 +11,18 @@ use std::string::FromUtf8Error;
 use serde::{de, Deserialize, Deserializer, Serialize};
 use thiserror::Error;
 
+#[cfg(feature = "wallet")]
 use super::nut10;
+#[cfg(feature = "wallet")]
 use super::nut11::SpendingConditions;
+#[cfg(feature = "wallet")]
 use crate::amount::SplitTarget;
-use crate::dhke::{blind_message, hash_to_curve};
-use crate::nuts::nut01::{PublicKey, SecretKey};
+#[cfg(feature = "wallet")]
+use crate::dhke::blind_message;
+use crate::dhke::hash_to_curve;
+use crate::nuts::nut01::PublicKey;
+#[cfg(feature = "wallet")]
+use crate::nuts::nut01::SecretKey;
 use crate::nuts::nut11::{serde_p2pk_witness, P2PKWitness};
 use crate::nuts::nut12::BlindSignatureDleq;
 use crate::nuts::nut14::{serde_htlc_witness, HTLCWitness};
@@ -491,6 +498,7 @@ impl<'de> Deserialize<'de> for PaymentMethod {
 }
 
 /// PreMint
+#[cfg(feature = "wallet")]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
 pub struct PreMint {
     /// Blinded message
@@ -503,12 +511,14 @@ pub struct PreMint {
     pub amount: Amount,
 }
 
+#[cfg(feature = "wallet")]
 impl Ord for PreMint {
     fn cmp(&self, other: &Self) -> std::cmp::Ordering {
         self.amount.cmp(&other.amount)
     }
 }
 
+#[cfg(feature = "wallet")]
 impl PartialOrd for PreMint {
     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
         Some(self.cmp(other))
@@ -516,6 +526,7 @@ impl PartialOrd for PreMint {
 }
 
 /// Premint Secrets
+#[cfg(feature = "wallet")]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
 pub struct PreMintSecrets {
     /// Secrets
@@ -524,6 +535,7 @@ pub struct PreMintSecrets {
     pub keyset_id: Id,
 }
 
+#[cfg(feature = "wallet")]
 impl PreMintSecrets {
     /// Create new [`PreMintSecrets`]
     pub fn new(keyset_id: Id) -> Self {
@@ -712,6 +724,7 @@ impl PreMintSecrets {
 }
 
 // Implement Iterator for PreMintSecrets
+#[cfg(feature = "wallet")]
 impl Iterator for PreMintSecrets {
     type Item = PreMint;
 
@@ -721,12 +734,14 @@ impl Iterator for PreMintSecrets {
     }
 }
 
+#[cfg(feature = "wallet")]
 impl Ord for PreMintSecrets {
     fn cmp(&self, other: &Self) -> Ordering {
         self.secrets.cmp(&other.secrets)
     }
 }
 
+#[cfg(feature = "wallet")]
 impl PartialOrd for PreMintSecrets {
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
         Some(self.cmp(other))
@@ -753,6 +768,7 @@ mod tests {
     }
 
     #[test]
+    #[cfg(feature = "wallet")]
     fn test_blank_blinded_messages() {
         let b = PreMintSecrets::blank(
             Id::from_str("009a1f293253e41e").unwrap(),

+ 6 - 8
crates/cdk/src/nuts/nut00/token.rs → crates/cashu/src/nuts/nut00/token.rs

@@ -527,11 +527,11 @@ mod tests {
     }
 
     #[test]
-    fn test_token_v4_multi_keyset() -> anyhow::Result<()> {
+    fn test_token_v4_multi_keyset() {
         let token_str_multi_keysets = "cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA==";
 
         let token = Token::from_str(token_str_multi_keysets).unwrap();
-        let amount = token.value()?;
+        let amount = token.value().expect("valid amount");
 
         assert_eq!(amount, Amount::from(4));
 
@@ -552,21 +552,19 @@ mod tests {
                 assert_eq!("http://localhost:3338", &mint_url.to_string());
             }
             _ => {
-                anyhow::bail!("Token should be a v4 token")
+                panic!("Token should be a v4 token")
             }
         }
-
-        Ok(())
     }
 
     #[test]
-    fn test_tokenv4_from_tokenv3() -> anyhow::Result<()> {
+    fn test_tokenv4_from_tokenv3() {
         let token_v3_str = "cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6IjQwNzkxNWJjMjEyYmU2MWE3N2UzZTZkMmFlYjRjNzI3OTgwYmRhNTFjZDA2YTZhZmMyOWUyODYxNzY4YTc4MzciLCJDIjoiMDJiYzkwOTc5OTdkODFhZmIyY2M3MzQ2YjVlNDM0NWE5MzQ2YmQyYTUwNmViNzk1ODU5OGE3MmYwY2Y4NTE2M2VhIn0seyJhbW91bnQiOjgsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmUiLCJDIjoiMDI5ZThlNTA1MGI4OTBhN2Q2YzA5NjhkYjE2YmMxZDVkNWZhMDQwZWExZGUyODRmNmVjNjlkNjEyOTlmNjcxMDU5In1dfV0sInVuaXQiOiJzYXQiLCJtZW1vIjoiVGhhbmsgeW91LiJ9";
-        let token_v3 = TokenV3::from_str(token_v3_str)?;
+        let token_v3 =
+            TokenV3::from_str(token_v3_str).expect("TokenV3 should be created from string");
         let token_v4 = TokenV4::try_from(token_v3).expect("TokenV3 should be converted to TokenV4");
         let token_v4_expected = "cashuBpGFtd2h0dHBzOi8vODMzMy5zcGFjZTozMzM4YXVjc2F0YWRqVGhhbmsgeW91LmF0gaJhaUgAmh8pMlPkHmFwgqRhYQJhc3hANDA3OTE1YmMyMTJiZTYxYTc3ZTNlNmQyYWViNGM3Mjc5ODBiZGE1MWNkMDZhNmFmYzI5ZTI4NjE3NjhhNzgzN2FjWCECvJCXmX2Br7LMc0a15DRak0a9KlBut5WFmKcvDPhRY-phZPakYWEIYXN4QGZlMTUxMDkzMTRlNjFkNzc1NmIwZjhlZTBmMjNhNjI0YWNhYTNmNGUwNDJmNjE0MzNjNzI4YzcwNTdiOTMxYmVhY1ghAp6OUFC4kKfWwJaNsWvB1dX6BA6h3ihPbsadYSmfZxBZYWT2";
         assert_eq!(token_v4.to_string(), token_v4_expected);
-        Ok(())
     }
 
     #[test]

+ 0 - 0
crates/cdk/src/nuts/nut01/mod.rs → crates/cashu/src/nuts/nut01/mod.rs


+ 0 - 0
crates/cdk/src/nuts/nut01/public_key.rs → crates/cashu/src/nuts/nut01/public_key.rs


+ 0 - 0
crates/cdk/src/nuts/nut01/secret_key.rs → crates/cashu/src/nuts/nut01/secret_key.rs


+ 2 - 4
crates/cdk/src/nuts/nut02.rs → crates/cashu/src/nuts/nut02.rs

@@ -9,9 +9,7 @@ use std::array::TryFromSliceError;
 use std::collections::BTreeMap;
 
 #[cfg(feature = "mint")]
-use bitcoin::bip32::DerivationPath;
-#[cfg(feature = "mint")]
-use bitcoin::bip32::{ChildNumber, Xpriv};
+use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv};
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::hashes::Hash;
 #[cfg(feature = "mint")]
@@ -268,8 +266,8 @@ fn default_input_fee_ppk() -> u64 {
     0
 }
 
-/// MintKeyset
 #[cfg(feature = "mint")]
+/// MintKeyset
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MintKeySet {
     /// Keyset [`Id`]

+ 4 - 1
crates/cdk/src/nuts/nut03.rs → crates/cashu/src/nuts/nut03.rs

@@ -5,7 +5,9 @@
 use serde::{Deserialize, Serialize};
 use thiserror::Error;
 
-use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs};
+#[cfg(feature = "wallet")]
+use super::nut00::PreMintSecrets;
+use super::nut00::{BlindSignature, BlindedMessage, Proofs};
 use crate::Amount;
 
 /// NUT03 Error
@@ -20,6 +22,7 @@ pub enum Error {
 }
 
 /// Preswap information
+#[cfg(feature = "wallet")]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
 pub struct PreSwap {
     /// Preswap mint secrets

+ 0 - 0
crates/cdk/src/nuts/nut04.rs → crates/cashu/src/nuts/nut04.rs


+ 0 - 0
crates/cdk/src/nuts/nut05.rs → crates/cashu/src/nuts/nut05.rs


+ 0 - 3
crates/cdk/src/nuts/nut06.rs → crates/cashu/src/nuts/nut06.rs

@@ -5,7 +5,6 @@
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
 
 use super::nut01::PublicKey;
-#[cfg(feature = "mint")]
 use super::nut17::SupportedMethods;
 use super::nut19::CachedEndpoint;
 use super::{nut04, nut05, nut15, nut19, MppMethodSettings};
@@ -241,7 +240,6 @@ pub struct Nuts {
     /// NUT17 Settings
     #[serde(default)]
     #[serde(rename = "17")]
-    #[cfg(feature = "mint")]
     pub nut17: super::nut17::SupportedSettings,
     /// NUT19 Settings
     #[serde(default)]
@@ -342,7 +340,6 @@ impl Nuts {
     }
 
     /// Nut17 settings
-    #[cfg(feature = "mint")]
     pub fn nut17(self, supported: Vec<SupportedMethods>) -> Self {
         Self {
             nut17: super::nut17::SupportedSettings { supported },

+ 0 - 0
crates/cdk/src/nuts/nut07.rs → crates/cashu/src/nuts/nut07.rs


+ 0 - 0
crates/cdk/src/nuts/nut08.rs → crates/cashu/src/nuts/nut08.rs


+ 0 - 0
crates/cdk/src/nuts/nut09.rs → crates/cashu/src/nuts/nut09.rs


+ 0 - 0
crates/cdk/src/nuts/nut10.rs → crates/cashu/src/nuts/nut10.rs


+ 4 - 10
crates/cdk/src/nuts/nut11/mod.rs → crates/cashu/src/nuts/nut11/mod.rs

@@ -2,9 +2,7 @@
 //!
 //! <https://github.com/cashubtc/nuts/blob/main/11.md>
 
-use std::collections::HashMap;
-#[cfg(feature = "mint")]
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet};
 use std::str::FromStr;
 use std::{fmt, vec};
 
@@ -18,9 +16,7 @@ use thiserror::Error;
 
 use super::nut00::Witness;
 use super::nut01::PublicKey;
-#[cfg(feature = "mint")]
-use super::Proofs;
-use super::{Kind, Nut10Secret, Proof, SecretKey};
+use super::{Kind, Nut10Secret, Proof, Proofs, SecretKey};
 use crate::nuts::nut00::BlindedMessage;
 use crate::secret::Secret;
 use crate::util::{hex, unix_time};
@@ -616,10 +612,9 @@ impl FromStr for SigFlag {
     }
 }
 
-#[cfg(feature = "mint")]
 /// Get the signature flag that should be enforced for a set of proofs and the
 /// public keys that signatures are valid for
-pub(crate) fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag {
+pub fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag {
     let mut sig_flag = SigFlag::SigInputs;
     let mut pubkeys = HashSet::new();
     let mut sigs_required = 1;
@@ -658,10 +653,9 @@ pub(crate) fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag {
     }
 }
 
-#[cfg(feature = "mint")]
 /// Enforce Sigflag info
 #[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) struct EnforceSigFlag {
+pub struct EnforceSigFlag {
     /// Sigflag required for proofs
     pub sig_flag: SigFlag,
     /// Pubkeys that can sign for proofs

+ 0 - 0
crates/cdk/src/nuts/nut11/serde_p2pk_witness.rs → crates/cashu/src/nuts/nut11/serde_p2pk_witness.rs


+ 0 - 0
crates/cdk/src/nuts/nut12.rs → crates/cashu/src/nuts/nut12.rs


+ 0 - 0
crates/cdk/src/nuts/nut13.rs → crates/cashu/src/nuts/nut13.rs


+ 0 - 0
crates/cdk/src/nuts/nut14/mod.rs → crates/cashu/src/nuts/nut14/mod.rs


+ 0 - 0
crates/cdk/src/nuts/nut14/serde_htlc_witness.rs → crates/cashu/src/nuts/nut14/serde_htlc_witness.rs


+ 0 - 0
crates/cdk/src/nuts/nut15.rs → crates/cashu/src/nuts/nut15.rs


+ 9 - 59
crates/cdk/src/nuts/nut17/mod.rs → crates/cashu/src/nuts/nut17/mod.rs

@@ -1,38 +1,28 @@
 //! Specific Subscription for the cdk crate
-use std::str::FromStr;
-
 use serde::de::DeserializeOwned;
 use serde::{Deserialize, Serialize};
+#[cfg(feature = "mint")]
 use uuid::Uuid;
 
+#[cfg(feature = "mint")]
 use super::PublicKey;
 use crate::nuts::{
     CurrencyUnit, MeltQuoteBolt11Response, MintQuoteBolt11Response, PaymentMethod, ProofState,
 };
-use crate::pub_sub::{Index, Indexable, SubscriptionGlobalId};
-
-#[cfg(feature = "mint")]
-mod manager;
-#[cfg(feature = "mint")]
-mod on_subscription;
-#[cfg(feature = "mint")]
-pub use manager::PubSubManager;
-#[cfg(feature = "mint")]
-pub use on_subscription::OnSubscription;
 
-pub use crate::pub_sub::SubId;
 pub mod ws;
 
 /// Subscription Parameter according to the standard
 #[derive(Debug, Clone, Serialize, Eq, PartialEq, Hash, Deserialize)]
-pub struct Params {
+#[serde(bound = "I: DeserializeOwned + Serialize")]
+pub struct Params<I> {
     /// Kind
     pub kind: Kind,
     /// Filters
     pub filters: Vec<String>,
     /// Subscription Id
     #[serde(rename = "subId")]
-    pub id: SubId,
+    pub id: I,
 }
 
 /// Check state Settings
@@ -109,6 +99,7 @@ impl<T> From<MintQuoteBolt11Response<T>> for NotificationPayload<T> {
     }
 }
 
+#[cfg(feature = "mint")]
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
 /// A parsed notification
 pub enum Notification {
@@ -120,24 +111,6 @@ pub enum Notification {
     MintQuoteBolt11(Uuid),
 }
 
-impl Indexable for NotificationPayload<Uuid> {
-    type Type = Notification;
-
-    fn to_indexes(&self) -> Vec<Index<Self::Type>> {
-        match self {
-            NotificationPayload::ProofState(proof_state) => {
-                vec![Index::from(Notification::ProofState(proof_state.y))]
-            }
-            NotificationPayload::MeltQuoteBolt11Response(melt_quote) => {
-                vec![Index::from(Notification::MeltQuoteBolt11(melt_quote.quote))]
-            }
-            NotificationPayload::MintQuoteBolt11Response(mint_quote) => {
-                vec![Index::from(Notification::MintQuoteBolt11(mint_quote.quote))]
-            }
-        }
-    }
-}
-
 /// Kind
 #[derive(Debug, Clone, Copy, Eq, Ord, PartialOrd, PartialEq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
@@ -150,8 +123,8 @@ pub enum Kind {
     ProofState,
 }
 
-impl AsRef<SubId> for Params {
-    fn as_ref(&self) -> &SubId {
+impl<I> AsRef<I> for Params<I> {
+    fn as_ref(&self) -> &I {
         &self.id
     }
 }
@@ -159,6 +132,7 @@ impl AsRef<SubId> for Params {
 /// Parsing error
 #[derive(thiserror::Error, Debug)]
 pub enum Error {
+    #[cfg(feature = "mint")]
     #[error("Uuid Error: {0}")]
     /// Uuid Error
     Uuid(#[from] uuid::Error),
@@ -167,27 +141,3 @@ pub enum Error {
     /// PublicKey Error
     PublicKey(#[from] crate::nuts::nut01::Error),
 }
-
-impl TryFrom<Params> for Vec<Index<Notification>> {
-    type Error = Error;
-
-    fn try_from(val: Params) -> Result<Self, Self::Error> {
-        let sub_id: SubscriptionGlobalId = Default::default();
-        val.filters
-            .into_iter()
-            .map(|filter| {
-                let idx = match val.kind {
-                    Kind::Bolt11MeltQuote => {
-                        Notification::MeltQuoteBolt11(Uuid::from_str(&filter)?)
-                    }
-                    Kind::Bolt11MintQuote => {
-                        Notification::MintQuoteBolt11(Uuid::from_str(&filter)?)
-                    }
-                    Kind::ProofState => Notification::ProofState(PublicKey::from_str(&filter)?),
-                };
-
-                Ok(Index::from((idx, val.id.clone(), sub_id)))
-            })
-            .collect::<Result<_, _>>()
-    }
-}

+ 39 - 69
crates/cdk/src/nuts/nut17/ws.rs → crates/cashu/src/nuts/nut17/ws.rs

@@ -2,31 +2,32 @@
 
 use serde::de::DeserializeOwned;
 use serde::{Deserialize, Serialize};
-use uuid::Uuid;
 
-use super::{NotificationPayload, Params, SubId};
+use super::{NotificationPayload, Params};
 
 /// JSON RPC version
 pub const JSON_RPC_VERSION: &str = "2.0";
 
 /// The response to a subscription request
 #[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct WsSubscribeResponse {
+#[serde(bound = "I: Serialize + DeserializeOwned")]
+pub struct WsSubscribeResponse<I> {
     /// Status
     pub status: String,
     /// Subscription ID
     #[serde(rename = "subId")]
-    pub sub_id: SubId,
+    pub sub_id: I,
 }
 
 /// The response to an unsubscription request
 #[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct WsUnsubscribeResponse {
+#[serde(bound = "I: Serialize + DeserializeOwned")]
+pub struct WsUnsubscribeResponse<I> {
     /// Status
     pub status: String,
     /// Subscription ID
     #[serde(rename = "subId")]
-    pub sub_id: SubId,
+    pub sub_id: I,
 }
 
 /// The notification
@@ -34,87 +35,74 @@ pub struct WsUnsubscribeResponse {
 /// This is the notification that is sent to the client when an event matches a
 /// subscription
 #[derive(Debug, Clone, Serialize, Deserialize)]
-#[serde(bound = "T: Serialize + DeserializeOwned")]
-pub struct NotificationInner<T> {
+#[serde(bound = "T: Serialize + DeserializeOwned, I: Serialize + DeserializeOwned")]
+pub struct NotificationInner<T, I> {
     /// The subscription ID
     #[serde(rename = "subId")]
-    pub sub_id: SubId,
+    pub sub_id: I,
 
     /// The notification payload
     pub payload: NotificationPayload<T>,
 }
 
-impl From<NotificationInner<Uuid>> for NotificationInner<String> {
-    fn from(value: NotificationInner<Uuid>) -> Self {
-        NotificationInner {
-            sub_id: value.sub_id,
-            payload: match value.payload {
-                NotificationPayload::ProofState(pk) => NotificationPayload::ProofState(pk),
-                NotificationPayload::MeltQuoteBolt11Response(quote) => {
-                    NotificationPayload::MeltQuoteBolt11Response(quote.to_string_id())
-                }
-                NotificationPayload::MintQuoteBolt11Response(quote) => {
-                    NotificationPayload::MintQuoteBolt11Response(quote.to_string_id())
-                }
-            },
-        }
-    }
-}
-
 /// Responses from the web socket server
 #[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(bound = "I: Serialize + DeserializeOwned")]
 #[serde(untagged)]
-pub enum WsResponseResult {
+pub enum WsResponseResult<I> {
     /// A response to a subscription request
-    Subscribe(WsSubscribeResponse),
+    Subscribe(WsSubscribeResponse<I>),
     /// Unsubscribe
-    Unsubscribe(WsUnsubscribeResponse),
+    Unsubscribe(WsUnsubscribeResponse<I>),
 }
 
-impl From<WsSubscribeResponse> for WsResponseResult {
-    fn from(response: WsSubscribeResponse) -> Self {
+impl<I> From<WsSubscribeResponse<I>> for WsResponseResult<I> {
+    fn from(response: WsSubscribeResponse<I>) -> Self {
         WsResponseResult::Subscribe(response)
     }
 }
 
-impl From<WsUnsubscribeResponse> for WsResponseResult {
-    fn from(response: WsUnsubscribeResponse) -> Self {
+impl<I> From<WsUnsubscribeResponse<I>> for WsResponseResult<I> {
+    fn from(response: WsUnsubscribeResponse<I>) -> Self {
         WsResponseResult::Unsubscribe(response)
     }
 }
 
 /// The request to unsubscribe
 #[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct WsUnsubscribeRequest {
+#[serde(bound = "I: Serialize + DeserializeOwned")]
+pub struct WsUnsubscribeRequest<I> {
     /// Subscription ID
     #[serde(rename = "subId")]
-    pub sub_id: SubId,
+    pub sub_id: I,
 }
 
 /// The inner method of the websocket request
 #[derive(Debug, Clone, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case", tag = "method", content = "params")]
-pub enum WsMethodRequest {
+#[serde(bound = "I: Serialize + DeserializeOwned")]
+pub enum WsMethodRequest<I> {
     /// Subscribe method
-    Subscribe(Params),
+    Subscribe(Params<I>),
     /// Unsubscribe method
-    Unsubscribe(WsUnsubscribeRequest),
+    Unsubscribe(WsUnsubscribeRequest<I>),
 }
 
 /// Websocket request
 #[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct WsRequest {
+#[serde(bound = "I: Serialize + DeserializeOwned")]
+pub struct WsRequest<I> {
     /// JSON RPC version
     pub jsonrpc: String,
     /// The method body
     #[serde(flatten)]
-    pub method: WsMethodRequest,
+    pub method: WsMethodRequest<I>,
     /// The request ID
     pub id: usize,
 }
 
-impl From<(WsMethodRequest, usize)> for WsRequest {
-    fn from((method, id): (WsMethodRequest, usize)) -> Self {
+impl<I> From<(WsMethodRequest<I>, usize)> for WsRequest<I> {
+    fn from((method, id): (WsMethodRequest<I>, usize)) -> Self {
         WsRequest {
             jsonrpc: JSON_RPC_VERSION.to_owned(),
             method,
@@ -145,11 +133,12 @@ pub struct WsErrorBody {
 
 /// Websocket response
 #[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct WsResponse {
+#[serde(bound = "I: Serialize + DeserializeOwned")]
+pub struct WsResponse<I> {
     /// JSON RPC version
     pub jsonrpc: String,
     /// The result
-    pub result: WsResponseResult,
+    pub result: WsResponseResult<I>,
     /// The request ID
     pub id: usize,
 }
@@ -167,18 +156,19 @@ pub struct WsErrorResponse {
 
 /// Message from the server to the client
 #[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(bound = "I: Serialize + DeserializeOwned")]
 #[serde(untagged)]
-pub enum WsMessageOrResponse {
+pub enum WsMessageOrResponse<I> {
     /// A response to a request
-    Response(WsResponse),
+    Response(WsResponse<I>),
     /// An error response
     ErrorResponse(WsErrorResponse),
     /// A notification
-    Notification(WsNotification<NotificationInner<String>>),
+    Notification(WsNotification<NotificationInner<String, I>>),
 }
 
-impl From<(usize, Result<WsResponseResult, WsErrorBody>)> for WsMessageOrResponse {
-    fn from((id, result): (usize, Result<WsResponseResult, WsErrorBody>)) -> Self {
+impl<I> From<(usize, Result<WsResponseResult<I>, WsErrorBody>)> for WsMessageOrResponse<I> {
+    fn from((id, result): (usize, Result<WsResponseResult<I>, WsErrorBody>)) -> Self {
         match result {
             Ok(result) => WsMessageOrResponse::Response(WsResponse {
                 jsonrpc: JSON_RPC_VERSION.to_owned(),
@@ -193,23 +183,3 @@ impl From<(usize, Result<WsResponseResult, WsErrorBody>)> for WsMessageOrRespons
         }
     }
 }
-
-impl From<NotificationInner<Uuid>> for WsMessageOrResponse {
-    fn from(notification: NotificationInner<Uuid>) -> Self {
-        WsMessageOrResponse::Notification(WsNotification {
-            jsonrpc: JSON_RPC_VERSION.to_owned(),
-            method: "subscribe".to_string(),
-            params: notification.into(),
-        })
-    }
-}
-
-impl From<NotificationInner<String>> for WsMessageOrResponse {
-    fn from(notification: NotificationInner<String>) -> Self {
-        WsMessageOrResponse::Notification(WsNotification {
-            jsonrpc: JSON_RPC_VERSION.to_owned(),
-            method: "subscribe".to_string(),
-            params: notification,
-        })
-    }
-}

+ 9 - 11
crates/cdk/src/nuts/nut18.rs → crates/cashu/src/nuts/nut18.rs

@@ -147,15 +147,15 @@ mod tests {
     const PAYMENT_REQUEST: &str = "creqApWF0gaNhdGVub3N0cmFheKlucHJvZmlsZTFxeTI4d3VtbjhnaGo3dW45ZDNzaGp0bnl2OWtoMnVld2Q5aHN6OW1od2RlbjV0ZTB3ZmprY2N0ZTljdXJ4dmVuOWVlaHFjdHJ2NWhzenJ0aHdkZW41dGUwZGVoaHh0bnZkYWtxcWd5ZGFxeTdjdXJrNDM5eWtwdGt5c3Y3dWRoZGh1NjhzdWNtMjk1YWtxZWZkZWhrZjBkNDk1Y3d1bmw1YWeBgmFuYjE3YWloYjdhOTAxNzZhYQphdWNzYXRhbYF4Imh0dHBzOi8vbm9mZWVzLnRlc3RudXQuY2FzaHUuc3BhY2U=";
 
     #[test]
-    fn test_decode_payment_req() -> anyhow::Result<()> {
-        let req = PaymentRequest::from_str(PAYMENT_REQUEST)?;
+    fn test_decode_payment_req() {
+        let req = PaymentRequest::from_str(PAYMENT_REQUEST).expect("valid payment request");
 
         assert_eq!(&req.payment_id.unwrap(), "b7a90176");
         assert_eq!(req.amount.unwrap(), 10.into());
         assert_eq!(req.unit.clone().unwrap(), CurrencyUnit::Sat);
         assert_eq!(
             req.mints.unwrap(),
-            vec![MintUrl::from_str("https://nofees.testnut.cashu.space")?]
+            vec![MintUrl::from_str("https://nofees.testnut.cashu.space").expect("valid mint url")]
         );
         assert_eq!(req.unit.unwrap(), CurrencyUnit::Sat);
 
@@ -164,12 +164,10 @@ mod tests {
         let expected_transport = Transport {_type: TransportType::Nostr, target: "nprofile1qy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsz9mhwden5te0wfjkccte9curxven9eehqctrv5hszrthwden5te0dehhxtnvdakqqgydaqy7curk439ykptkysv7udhdhu68sucm295akqefdehkf0d495cwunl5".to_string(), tags: Some(vec![vec!["n".to_string(), "17".to_string()]])};
 
         assert_eq!(transport, &expected_transport);
-
-        Ok(())
     }
 
     #[test]
-    fn test_roundtrip_payment_req() -> anyhow::Result<()> {
+    fn test_roundtrip_payment_req() {
         let transport = Transport {_type: TransportType::Nostr, target: "nprofile1qy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsz9mhwden5te0wfjkccte9curxven9eehqctrv5hszrthwden5te0dehhxtnvdakqqgydaqy7curk439ykptkysv7udhdhu68sucm295akqefdehkf0d495cwunl5".to_string(), tags: Some(vec![vec!["n".to_string(), "17".to_string()]])};
 
         let request = PaymentRequest {
@@ -177,27 +175,27 @@ mod tests {
             amount: Some(10.into()),
             unit: Some(CurrencyUnit::Sat),
             single_use: None,
-            mints: Some(vec!["https://nofees.testnut.cashu.space".parse()?]),
+            mints: Some(vec!["https://nofees.testnut.cashu.space"
+                .parse()
+                .expect("valid mint url")]),
             description: None,
             transports: vec![transport.clone()],
         };
 
         let request_str = request.to_string();
 
-        let req = PaymentRequest::from_str(&request_str)?;
+        let req = PaymentRequest::from_str(&request_str).expect("valid payment request");
 
         assert_eq!(&req.payment_id.unwrap(), "b7a90176");
         assert_eq!(req.amount.unwrap(), 10.into());
         assert_eq!(req.unit.clone().unwrap(), CurrencyUnit::Sat);
         assert_eq!(
             req.mints.unwrap(),
-            vec![MintUrl::from_str("https://nofees.testnut.cashu.space")?]
+            vec![MintUrl::from_str("https://nofees.testnut.cashu.space").expect("valid mint url")]
         );
         assert_eq!(req.unit.unwrap(), CurrencyUnit::Sat);
 
         let t = req.transports.first().unwrap();
         assert_eq!(&transport, t);
-
-        Ok(())
     }
 }

+ 0 - 0
crates/cdk/src/nuts/nut19.rs → crates/cashu/src/nuts/nut19.rs


+ 0 - 0
crates/cdk/src/nuts/nut20.rs → crates/cashu/src/nuts/nut20.rs


+ 0 - 0
crates/cdk/src/secret.rs → crates/cashu/src/secret.rs


+ 0 - 23
crates/cdk/src/util/hex.rs → crates/cashu/src/util/hex.rs

@@ -122,26 +122,3 @@ mod tests {
         );
     }
 }
-
-#[cfg(feature = "bench")]
-mod benches {
-    use super::*;
-    use crate::test::{black_box, Bencher};
-
-    const EVENT_JSON: &str = r#"{"content":"uRuvYr585B80L6rSJiHocw==?iv=oh6LVqdsYYol3JfFnXTbPA==","created_at":1640839235,"id":"2be17aa3031bdcb006f0fce80c146dea9c1c0268b0af2398bb673365c6444d45","kind":4,"pubkey":"f86c44a2de95d9149b51c6a29afeabba264c18e2fa7c49de93424a0c56947785","sig":"a5d9290ef9659083c490b303eb7ee41356d8778ff19f2f91776c8dc4443388a64ffcf336e61af4c25c05ac3ae952d1ced889ed655b67790891222aaa15b99fdd","tags":[["p","13adc511de7e1cfcf1c6b7f6365fb5a03442d7bcacf565ea57fa7770912c023d"]]}"#;
-
-    #[bench]
-    pub fn hex_encode(bh: &mut Bencher) {
-        bh.iter(|| {
-            black_box(encode(EVENT_JSON));
-        });
-    }
-
-    #[bench]
-    pub fn hex_decode(bh: &mut Bencher) {
-        let h = "7b22636f6e74656e74223a227552757659723538354238304c3672534a69486f63773d3d3f69763d6f68364c5671647359596f6c334a66466e58546250413d3d222c22637265617465645f6174223a313634303833393233352c226964223a2232626531376161333033316264636230303666306663653830633134366465613963316330323638623061663233393862623637333336356336343434643435222c226b696e64223a342c227075626b6579223a2266383663343461326465393564393134396235316336613239616665616262613236346331386532666137633439646539333432346130633536393437373835222c22736967223a226135643932393065663936353930383363343930623330336562376565343133353664383737386666313966326639313737366338646334343433333838613634666663663333366536316166346332356330356163336165393532643163656438383965643635356236373739303839313232326161613135623939666464222c2274616773223a5b5b2270222c2231336164633531316465376531636663663163366237663633363566623561303334343264376263616366353635656135376661373737303931326330323364225d5d7d";
-        bh.iter(|| {
-            black_box(decode(h)).unwrap();
-        });
-    }
-}

+ 16 - 6
crates/cdk/src/util/mod.rs → crates/cashu/src/util/mod.rs

@@ -1,17 +1,15 @@
-//! Util
-
 #[cfg(not(target_arch = "wasm32"))]
 use std::time::{SystemTime, UNIX_EPOCH};
 
-use anyhow::Result;
 use bitcoin::secp256k1::{rand, All, Secp256k1};
-#[cfg(target_arch = "wasm32")]
-use instant::SystemTime;
 use once_cell::sync::Lazy;
 
 pub mod hex;
 
 #[cfg(target_arch = "wasm32")]
+use instant::SystemTime;
+
+#[cfg(target_arch = "wasm32")]
 const UNIX_EPOCH: SystemTime = SystemTime::UNIX_EPOCH;
 
 /// Secp256k1 global context
@@ -30,10 +28,22 @@ pub fn unix_time() -> u64 {
         .as_secs()
 }
 
+#[derive(Debug, thiserror::Error)]
+/// Error type for serialization
+pub enum CborError {
+    /// CBOR serialization error
+    #[error("CBOR serialization error")]
+    Cbor(#[from] ciborium::ser::Error<std::io::Error>),
+
+    /// CBOR diagnostic notation error
+    #[error("CBOR diagnostic notation error: {0}")]
+    CborDiag(#[from] cbor_diag::Error),
+}
+
 /// Serializes a struct to the CBOR diagnostic notation.
 ///
 /// See <https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation>
-pub fn serialize_to_cbor_diag<T: serde::Serialize>(data: &T) -> Result<String> {
+pub fn serialize_to_cbor_diag<T: serde::Serialize>(data: &T) -> Result<String, CborError> {
     let mut cbor_buffer = Vec::new();
     ciborium::ser::into_writer(data, &mut cbor_buffer)?;
 

+ 24 - 0
crates/cdk/src/wallet/types.rs → crates/cashu/src/wallet.rs

@@ -1,11 +1,35 @@
 //! Wallet Types
 
+use std::fmt;
+
 use serde::{Deserialize, Serialize};
 
 use crate::mint_url::MintUrl;
 use crate::nuts::{CurrencyUnit, MeltQuoteState, MintQuoteState, SecretKey};
 use crate::Amount;
 
+/// Wallet Key
+#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
+pub struct WalletKey {
+    /// Mint Url
+    pub mint_url: MintUrl,
+    /// Currency Unit
+    pub unit: CurrencyUnit,
+}
+
+impl fmt::Display for WalletKey {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "mint_url: {}, unit: {}", self.mint_url, self.unit,)
+    }
+}
+
+impl WalletKey {
+    /// Create new [`WalletKey`]
+    pub fn new(mint_url: MintUrl, unit: CurrencyUnit) -> Self {
+        Self { mint_url, unit }
+    }
+}
+
 /// Mint Quote Info
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MintQuote {

+ 7 - 5
crates/cdk-axum/src/ws/mod.rs

@@ -1,10 +1,12 @@
 use std::collections::HashMap;
 
 use axum::extract::ws::{Message, WebSocket};
-use cdk::nuts::nut17::ws::{
-    NotificationInner, WsErrorBody, WsMessageOrResponse, WsMethodRequest, WsRequest,
+use cdk::nuts::nut17::NotificationPayload;
+use cdk::pub_sub::SubId;
+use cdk::ws::{
+    notification_to_ws_message, NotificationInner, WsErrorBody, WsMessageOrResponse,
+    WsMethodRequest, WsRequest,
 };
-use cdk::nuts::nut17::{NotificationPayload, SubId};
 use futures::StreamExt;
 use tokio::sync::mpsc;
 use uuid::Uuid;
@@ -61,10 +63,10 @@ pub async fn main_websocket(mut socket: WebSocket, state: MintState) {
                     // unsubscribed from the subscription manager, just ignore it.
                     continue;
                 }
-                let notification: WsMessageOrResponse= NotificationInner {
+                let notification = notification_to_ws_message(NotificationInner {
                     sub_id,
                     payload,
-                }.into();
+                });
                 let message = match serde_json::to_string(&notification) {
                     Ok(message) => message,
                     Err(err) => {

+ 4 - 2
crates/cdk-axum/src/ws/subscribe.rs

@@ -1,5 +1,5 @@
-use cdk::nuts::nut17::ws::{WsResponseResult, WsSubscribeResponse};
-use cdk::nuts::nut17::Params;
+use cdk::subscription::{IndexableParams, Params};
+use cdk::ws::{WsResponseResult, WsSubscribeResponse};
 
 use super::{WsContext, WsError};
 
@@ -15,6 +15,8 @@ pub(crate) async fn handle(
         return Err(WsError::InvalidParams);
     }
 
+    let params: IndexableParams = params.into();
+
     let mut subscription = context
         .state
         .mint

+ 1 - 1
crates/cdk-axum/src/ws/unsubscribe.rs

@@ -1,4 +1,4 @@
-use cdk::nuts::nut17::ws::{WsResponseResult, WsUnsubscribeRequest, WsUnsubscribeResponse};
+use cdk::ws::{WsResponseResult, WsUnsubscribeRequest, WsUnsubscribeResponse};
 
 use super::{WsContext, WsError};
 

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

@@ -3,7 +3,7 @@ use std::str::FromStr;
 use anyhow::Result;
 use cdk::mint_url::MintUrl;
 use cdk::nuts::CurrencyUnit;
-use cdk::wallet::multi_mint_wallet::WalletKey;
+use cdk::wallet::types::WalletKey;
 use cdk::wallet::MultiMintWallet;
 use cdk::Amount;
 use clap::Args;

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

@@ -5,7 +5,8 @@ use std::str::FromStr;
 use anyhow::{bail, Result};
 use cdk::amount::MSAT_IN_SAT;
 use cdk::nuts::{CurrencyUnit, MeltOptions};
-use cdk::wallet::multi_mint_wallet::{MultiMintWallet, WalletKey};
+use cdk::wallet::multi_mint_wallet::MultiMintWallet;
+use cdk::wallet::types::WalletKey;
 use cdk::Bolt11Invoice;
 use clap::Args;
 

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

@@ -7,7 +7,7 @@ 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::multi_mint_wallet::WalletKey;
+use cdk::wallet::types::WalletKey;
 use cdk::wallet::{MultiMintWallet, Wallet, WalletSubscription};
 use cdk::Amount;
 use clap::Args;

+ 2 - 1
crates/cdk-cli/src/sub_commands/receive.rs

@@ -6,7 +6,8 @@ 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, WalletKey};
+use cdk::wallet::multi_mint_wallet::MultiMintWallet;
+use cdk::wallet::types::WalletKey;
 use cdk::wallet::Wallet;
 use cdk::Amount;
 use clap::Args;

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

@@ -5,7 +5,7 @@ use anyhow::Result;
 use cdk::cdk_database::{Error, WalletDatabase};
 use cdk::mint_url::MintUrl;
 use cdk::nuts::CurrencyUnit;
-use cdk::wallet::multi_mint_wallet::WalletKey;
+use cdk::wallet::types::WalletKey;
 use cdk::wallet::{MultiMintWallet, Wallet};
 use clap::Args;
 

+ 1 - 2
crates/cdk-cli/src/sub_commands/send.rs

@@ -5,8 +5,7 @@ use std::str::FromStr;
 use anyhow::{bail, Result};
 use cdk::amount::SplitTarget;
 use cdk::nuts::{Conditions, CurrencyUnit, PublicKey, SpendingConditions};
-use cdk::wallet::multi_mint_wallet::WalletKey;
-use cdk::wallet::types::SendKind;
+use cdk::wallet::types::{SendKind, WalletKey};
 use cdk::wallet::MultiMintWallet;
 use cdk::Amount;
 use clap::Args;

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

@@ -1,7 +1,7 @@
 use anyhow::{anyhow, Result};
 use cdk::mint_url::MintUrl;
 use cdk::nuts::CurrencyUnit;
-use cdk::wallet::multi_mint_wallet::WalletKey;
+use cdk::wallet::types::WalletKey;
 use cdk::wallet::MultiMintWallet;
 use clap::Args;
 

+ 43 - 0
crates/cdk-common/Cargo.toml

@@ -0,0 +1,43 @@
+[package]
+name = "cdk-common"
+version = "0.6.0"
+edition = "2021"
+description = "CDK common types and traits"
+rust-version = "1.63.0"                     # MSRV
+
+[features]
+default = ["mint", "wallet"]
+swagger = ["dep:utoipa", "cashu/swagger"]
+bench = []
+wallet = ["cashu/wallet"]
+mint = ["cashu/mint", "dep:uuid"]
+
+[dependencies]
+async-trait = "0.1"
+bitcoin = { version = "0.32.2", features = [
+    "base64",
+    "serde",
+    "rand",
+    "rand-std",
+] }
+cashu = { path = "../cashu", default-features = false, version = "0.6.0" }
+cbor-diag = "0.1.12"
+ciborium = { version = "0.2.2", default-features = false, features = ["std"] }
+serde = { version = "1", features = ["derive"] }
+lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }
+thiserror = "2"
+tracing = "0.1"
+url = "2.3"
+uuid = { version = "1", features = ["v4", "serde"], optional = true }
+utoipa = { version = "4", optional = true }
+futures = "0.3.31"
+anyhow = "1.0"
+serde_json = "1"
+serde_with = "3"
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] }
+
+[dev-dependencies]
+rand = "0.8.5"
+bip39 = "2.0"

+ 0 - 0
crates/cdk/src/types.rs → crates/cdk-common/src/common.rs


+ 130 - 0
crates/cdk-common/src/database/mint.rs

@@ -0,0 +1,130 @@
+//! CDK Database
+
+use std::collections::HashMap;
+
+use async_trait::async_trait;
+use uuid::Uuid;
+
+use super::Error;
+use crate::common::LnKey;
+use crate::mint::{self, MintKeySetInfo, MintQuote as MintMintQuote};
+use crate::nuts::{
+    BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, Proof,
+    Proofs, PublicKey, State,
+};
+
+/// Mint Database trait
+#[async_trait]
+pub trait Database {
+    /// Mint Database Error
+    type Err: Into<Error> + From<Error>;
+
+    /// Add Active Keyset
+    async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err>;
+    /// Get Active Keyset
+    async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result<Option<Id>, Self::Err>;
+    /// Get all Active Keyset
+    async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, Self::Err>;
+
+    /// Add [`MintMintQuote`]
+    async fn add_mint_quote(&self, quote: MintMintQuote) -> Result<(), Self::Err>;
+    /// Get [`MintMintQuote`]
+    async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintMintQuote>, Self::Err>;
+    /// Update state of [`MintMintQuote`]
+    async fn update_mint_quote_state(
+        &self,
+        quote_id: &Uuid,
+        state: MintQuoteState,
+    ) -> Result<MintQuoteState, Self::Err>;
+    /// Get all [`MintMintQuote`]s
+    async fn get_mint_quote_by_request(
+        &self,
+        request: &str,
+    ) -> Result<Option<MintMintQuote>, Self::Err>;
+    /// Get all [`MintMintQuote`]s
+    async fn get_mint_quote_by_request_lookup_id(
+        &self,
+        request_lookup_id: &str,
+    ) -> Result<Option<MintMintQuote>, Self::Err>;
+    /// Get Mint Quotes
+    async fn get_mint_quotes(&self) -> Result<Vec<MintMintQuote>, Self::Err>;
+    /// Remove [`MintMintQuote`]
+    async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>;
+
+    /// Add [`mint::MeltQuote`]
+    async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err>;
+    /// Get [`mint::MeltQuote`]
+    async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err>;
+    /// Update [`mint::MeltQuote`] state
+    async fn update_melt_quote_state(
+        &self,
+        quote_id: &Uuid,
+        state: MeltQuoteState,
+    ) -> Result<MeltQuoteState, Self::Err>;
+    /// Get all [`mint::MeltQuote`]s
+    async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, Self::Err>;
+    /// Remove [`mint::MeltQuote`]
+    async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>;
+
+    /// Add melt request
+    async fn add_melt_request(
+        &self,
+        melt_request: MeltBolt11Request<Uuid>,
+        ln_key: LnKey,
+    ) -> Result<(), Self::Err>;
+    /// Get melt request
+    async fn get_melt_request(
+        &self,
+        quote_id: &Uuid,
+    ) -> Result<Option<(MeltBolt11Request<Uuid>, LnKey)>, Self::Err>;
+
+    /// Add [`MintKeySetInfo`]
+    async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err>;
+    /// Get [`MintKeySetInfo`]
+    async fn get_keyset_info(&self, id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err>;
+    /// Get [`MintKeySetInfo`]s
+    async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err>;
+
+    /// Add spent [`Proofs`]
+    async fn add_proofs(&self, proof: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err>;
+    /// Get [`Proofs`] by ys
+    async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err>;
+    /// Get ys by quote id
+    async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err>;
+    /// Get [`Proofs`] state
+    async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err>;
+    /// Get [`Proofs`] state
+    async fn update_proofs_states(
+        &self,
+        ys: &[PublicKey],
+        proofs_state: State,
+    ) -> Result<Vec<Option<State>>, Self::Err>;
+    /// Get [`Proofs`] by state
+    async fn get_proofs_by_keyset_id(
+        &self,
+        keyset_id: &Id,
+    ) -> Result<(Proofs, Vec<Option<State>>), Self::Err>;
+
+    /// Add [`BlindSignature`]
+    async fn add_blind_signatures(
+        &self,
+        blinded_messages: &[PublicKey],
+        blind_signatures: &[BlindSignature],
+        quote_id: Option<Uuid>,
+    ) -> Result<(), Self::Err>;
+    /// Get [`BlindSignature`]s
+    async fn get_blind_signatures(
+        &self,
+        blinded_messages: &[PublicKey],
+    ) -> Result<Vec<Option<BlindSignature>>, Self::Err>;
+    /// Get [`BlindSignature`]s for keyset_id
+    async fn get_blind_signatures_for_keyset(
+        &self,
+        keyset_id: &Id,
+    ) -> Result<Vec<BlindSignature>, Self::Err>;
+    /// Get [`BlindSignature`]s for quote
+    async fn get_blind_signatures_for_quote(
+        &self,
+        quote_id: &Uuid,
+    ) -> Result<Vec<BlindSignature>, Self::Err>;
+}

+ 34 - 0
crates/cdk-common/src/database/mod.rs

@@ -0,0 +1,34 @@
+//! CDK Database
+
+#[cfg(feature = "mint")]
+mod mint;
+#[cfg(feature = "wallet")]
+mod wallet;
+
+#[cfg(feature = "mint")]
+pub use mint::Database as MintDatabase;
+#[cfg(feature = "wallet")]
+pub use wallet::Database as WalletDatabase;
+
+/// CDK_database error
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+    /// Database Error
+    #[error(transparent)]
+    Database(Box<dyn std::error::Error + Send + Sync>),
+    /// DHKE error
+    #[error(transparent)]
+    DHKE(#[from] crate::dhke::Error),
+    /// NUT00 Error
+    #[error(transparent)]
+    NUT00(#[from] crate::nuts::nut00::Error),
+    /// NUT02 Error
+    #[error(transparent)]
+    NUT02(#[from] crate::nuts::nut02::Error),
+    /// Serde Error
+    #[error(transparent)]
+    Serde(#[from] serde_json::Error),
+    /// Unknown Quote
+    #[error("Unknown Quote")]
+    UnknownQuote,
+}

+ 120 - 0
crates/cdk-common/src/database/wallet.rs

@@ -0,0 +1,120 @@
+//! CDK Database
+
+use std::collections::HashMap;
+use std::fmt::Debug;
+
+use async_trait::async_trait;
+
+use super::Error;
+use crate::common::ProofInfo;
+use crate::mint_url::MintUrl;
+use crate::nuts::{
+    CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State,
+};
+use crate::wallet;
+use crate::wallet::MintQuote as WalletMintQuote;
+
+/// Wallet Database trait
+#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
+#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
+pub trait Database: Debug {
+    /// Wallet Database Error
+    type Err: Into<Error> + From<Error>;
+
+    /// Add Mint to storage
+    async fn add_mint(
+        &self,
+        mint_url: MintUrl,
+        mint_info: Option<MintInfo>,
+    ) -> Result<(), Self::Err>;
+    /// Remove Mint from storage
+    async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), Self::Err>;
+    /// Get mint from storage
+    async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, Self::Err>;
+    /// Get all mints from storage
+    async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, Self::Err>;
+    /// Update mint url
+    async fn update_mint_url(
+        &self,
+        old_mint_url: MintUrl,
+        new_mint_url: MintUrl,
+    ) -> Result<(), Self::Err>;
+
+    /// Add mint keyset to storage
+    async fn add_mint_keysets(
+        &self,
+        mint_url: MintUrl,
+        keysets: Vec<KeySetInfo>,
+    ) -> Result<(), Self::Err>;
+    /// Get mint keysets for mint url
+    async fn get_mint_keysets(
+        &self,
+        mint_url: MintUrl,
+    ) -> Result<Option<Vec<KeySetInfo>>, Self::Err>;
+    /// Get mint keyset by id
+    async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result<Option<KeySetInfo>, Self::Err>;
+
+    /// Add mint quote to storage
+    async fn add_mint_quote(&self, quote: WalletMintQuote) -> Result<(), Self::Err>;
+    /// Get mint quote from storage
+    async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<WalletMintQuote>, Self::Err>;
+    /// Get mint quotes from storage
+    async fn get_mint_quotes(&self) -> Result<Vec<WalletMintQuote>, Self::Err>;
+    /// Remove mint quote from storage
+    async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err>;
+
+    /// Add melt quote to storage
+    async fn add_melt_quote(&self, quote: wallet::MeltQuote) -> Result<(), Self::Err>;
+    /// Get melt quote from storage
+    async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<wallet::MeltQuote>, Self::Err>;
+    /// Remove melt quote from storage
+    async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err>;
+
+    /// Add [`Keys`] to storage
+    async fn add_keys(&self, keys: Keys) -> Result<(), Self::Err>;
+    /// Get [`Keys`] from storage
+    async fn get_keys(&self, id: &Id) -> Result<Option<Keys>, Self::Err>;
+    /// Remove [`Keys`] from storage
+    async fn remove_keys(&self, id: &Id) -> Result<(), Self::Err>;
+
+    /// Update the proofs in storage by adding new proofs or removing proofs by
+    /// their Y value.
+    async fn update_proofs(
+        &self,
+        added: Vec<ProofInfo>,
+        removed_ys: Vec<PublicKey>,
+    ) -> Result<(), Self::Err>;
+    /// Set proofs as pending in storage. Proofs are identified by their Y
+    /// value.
+    async fn set_pending_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
+    /// Reserve proofs in storage. Proofs are identified by their Y value.
+    async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
+    /// Set proofs as unspent in storage. Proofs are identified by their Y
+    /// value.
+    async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
+    /// Get proofs from storage
+    async fn get_proofs(
+        &self,
+        mint_url: Option<MintUrl>,
+        unit: Option<CurrencyUnit>,
+        state: Option<Vec<State>>,
+        spending_conditions: Option<Vec<SpendingConditions>>,
+    ) -> Result<Vec<ProofInfo>, Self::Err>;
+
+    /// Increment Keyset counter
+    async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err>;
+    /// Get current Keyset counter
+    async fn get_keyset_counter(&self, keyset_id: &Id) -> Result<Option<u32>, Self::Err>;
+
+    /// Get when nostr key was last checked
+    async fn get_nostr_last_checked(
+        &self,
+        verifying_key: &PublicKey,
+    ) -> Result<Option<u32>, Self::Err>;
+    /// Update last checked time
+    async fn add_nostr_last_checked(
+        &self,
+        verifying_key: PublicKey,
+        last_checked: u32,
+    ) -> Result<(), Self::Err>;
+}

+ 9 - 10
crates/cdk/src/error.rs → crates/cdk-common/src/error.rs

@@ -9,7 +9,7 @@ use thiserror::Error;
 use crate::nuts::Id;
 use crate::util::hex;
 #[cfg(feature = "wallet")]
-use crate::wallet::multi_mint_wallet::WalletKey;
+use crate::wallet::WalletKey;
 use crate::Amount;
 
 /// CDK Error
@@ -128,8 +128,8 @@ pub enum Error {
     #[error("Incorrect wallet: `{0}`")]
     IncorrectWallet(String),
     /// Unknown Wallet
-    #[cfg(feature = "wallet")]
     #[error("Unknown wallet: `{0}`")]
+    #[cfg(feature = "wallet")]
     UnknownWallet(WalletKey),
     /// Max Fee Ecxeded
     #[error("Max fee exceeded")]
@@ -197,10 +197,9 @@ pub enum Error {
     /// From hex error
     #[error(transparent)]
     HexError(#[from] hex::Error),
-    #[cfg(feature = "wallet")]
-    /// From hex error
-    #[error(transparent)]
-    ReqwestError(#[from] reqwest::Error),
+    /// Http transport error
+    #[error("Http transport error: {0}")]
+    HttpError(String),
 
     // Crate error conversions
     /// Cashu Url Error
@@ -238,6 +237,7 @@ pub enum Error {
     NUT12(#[from] crate::nuts::nut12::Error),
     /// NUT13 Error
     #[error(transparent)]
+    #[cfg(feature = "wallet")]
     NUT13(#[from] crate::nuts::nut13::Error),
     /// NUT14 Error
     #[error(transparent)]
@@ -249,13 +249,12 @@ pub enum Error {
     #[error(transparent)]
     NUT20(#[from] crate::nuts::nut20::Error),
     /// Database Error
-    #[cfg(any(feature = "wallet", feature = "mint"))]
     #[error(transparent)]
-    Database(#[from] crate::cdk_database::Error),
+    Database(#[from] crate::database::Error),
     /// Lightning Error
-    #[cfg(feature = "mint")]
     #[error(transparent)]
-    Lightning(#[from] crate::cdk_lightning::Error),
+    #[cfg(feature = "mint")]
+    Lightning(#[from] crate::lightning::Error),
 }
 
 /// CDK Error Response

+ 28 - 0
crates/cdk-common/src/lib.rs

@@ -0,0 +1,28 @@
+//! Cashu shared types and functions.
+//!
+//! This crate is the base foundation to build things that can interact with the CDK (Cashu
+//! Development Kit) and their internal crates.
+//!
+//! This is meant to contain the shared types, traits and common functions that are used across the
+//! internal crates.
+
+pub mod common;
+pub mod database;
+pub mod error;
+#[cfg(feature = "mint")]
+pub mod lightning;
+pub mod pub_sub;
+#[cfg(feature = "mint")]
+pub mod subscription;
+pub mod ws;
+
+// re-exporting external crates
+pub use bitcoin;
+pub use cashu::amount::{self, Amount};
+pub use cashu::lightning_invoice::{self, Bolt11Invoice};
+#[cfg(feature = "mint")]
+pub use cashu::mint;
+pub use cashu::nuts::{self, *};
+#[cfg(feature = "wallet")]
+pub use cashu::wallet;
+pub use cashu::{dhke, mint_url, secret, util, SECP256K1};

+ 0 - 0
crates/cdk/src/cdk_lightning/mod.rs → crates/cdk-common/src/lightning.rs


+ 0 - 0
crates/cdk/src/pub_sub/index.rs → crates/cdk-common/src/pub_sub/index.rs


+ 77 - 0
crates/cdk-common/src/pub_sub/mod.rs

@@ -0,0 +1,77 @@
+//! Publish–subscribe pattern.
+//!
+//! This is a generic implementation for
+//! [NUT-17(https://github.com/cashubtc/nuts/blob/main/17.md) with a type
+//! agnostic Publish-subscribe manager.
+//!
+//! The manager has a method for subscribers to subscribe to events with a
+//! generic type that must be converted to a vector of indexes.
+//!
+//! Events are also generic that should implement the `Indexable` trait.
+use std::fmt::Debug;
+use std::ops::Deref;
+use std::str::FromStr;
+
+use serde::{Deserialize, Serialize};
+
+pub mod index;
+
+/// Default size of the remove channel
+pub const DEFAULT_REMOVE_SIZE: usize = 10_000;
+
+/// Default channel size for subscription buffering
+pub const DEFAULT_CHANNEL_SIZE: usize = 10;
+
+#[async_trait::async_trait]
+/// On New Subscription trait
+///
+/// This trait is optional and it is used to notify the application when a new
+/// subscription is created. This is useful when the application needs to send
+/// the initial state to the subscriber upon subscription
+pub trait OnNewSubscription {
+    /// Index type
+    type Index;
+    /// Subscription event type
+    type Event;
+
+    /// Called when a new subscription is created
+    async fn on_new_subscription(
+        &self,
+        request: &[&Self::Index],
+    ) -> Result<Vec<Self::Event>, String>;
+}
+
+/// Subscription Id wrapper
+///
+/// This is the place to add some sane default (like a max length) to the
+/// subscription ID
+#[derive(Debug, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
+pub struct SubId(String);
+
+impl From<&str> for SubId {
+    fn from(s: &str) -> Self {
+        Self(s.to_string())
+    }
+}
+
+impl From<String> for SubId {
+    fn from(s: String) -> Self {
+        Self(s)
+    }
+}
+
+impl FromStr for SubId {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(Self(s.to_string()))
+    }
+}
+
+impl Deref for SubId {
+    type Target = String;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}

+ 74 - 0
crates/cdk-common/src/subscription.rs

@@ -0,0 +1,74 @@
+//! Subscription types and traits
+use std::str::FromStr;
+
+use cashu::nut17::{self, Error, Kind, Notification};
+use cashu::{NotificationPayload, PublicKey};
+use serde::{Deserialize, Serialize};
+use uuid::Uuid;
+
+use crate::pub_sub::index::{Index, Indexable, SubscriptionGlobalId};
+use crate::pub_sub::SubId;
+
+/// Subscription parameters.
+///
+/// This is a concrete type alias for `nut17::Params<SubId>`.
+pub type Params = nut17::Params<SubId>;
+
+/// Wrapper around `nut17::Params` to implement `Indexable` for `Notification`.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct IndexableParams(Params);
+
+impl From<Params> for IndexableParams {
+    fn from(params: Params) -> Self {
+        Self(params)
+    }
+}
+
+impl TryFrom<IndexableParams> for Vec<Index<Notification>> {
+    type Error = Error;
+    fn try_from(params: IndexableParams) -> Result<Self, Self::Error> {
+        let sub_id: SubscriptionGlobalId = Default::default();
+        let params = params.0;
+        params
+            .filters
+            .into_iter()
+            .map(|filter| {
+                let idx = match params.kind {
+                    Kind::Bolt11MeltQuote => {
+                        Notification::MeltQuoteBolt11(Uuid::from_str(&filter)?)
+                    }
+                    Kind::Bolt11MintQuote => {
+                        Notification::MintQuoteBolt11(Uuid::from_str(&filter)?)
+                    }
+                    Kind::ProofState => Notification::ProofState(PublicKey::from_str(&filter)?),
+                };
+
+                Ok(Index::from((idx, params.id.clone(), sub_id)))
+            })
+            .collect::<Result<_, _>>()
+    }
+}
+
+impl AsRef<SubId> for IndexableParams {
+    fn as_ref(&self) -> &SubId {
+        &self.0.id
+    }
+}
+
+impl Indexable for NotificationPayload<Uuid> {
+    type Type = Notification;
+
+    fn to_indexes(&self) -> Vec<Index<Self::Type>> {
+        match self {
+            NotificationPayload::ProofState(proof_state) => {
+                vec![Index::from(Notification::ProofState(proof_state.y))]
+            }
+            NotificationPayload::MeltQuoteBolt11Response(melt_quote) => {
+                vec![Index::from(Notification::MeltQuoteBolt11(melt_quote.quote))]
+            }
+            NotificationPayload::MintQuoteBolt11Response(mint_quote) => {
+                vec![Index::from(Notification::MintQuoteBolt11(mint_quote.quote))]
+            }
+        }
+    }
+}

+ 52 - 0
crates/cdk-common/src/ws.rs

@@ -0,0 +1,52 @@
+//! Websocket types and functions for the CDK.
+//!
+//! This module extends the `cashu` crate with types and functions for the CDK, using the correct
+//! expected ID types.
+#[cfg(feature = "mint")]
+use cashu::nut17::ws::JSON_RPC_VERSION;
+use cashu::nut17::{self};
+#[cfg(feature = "mint")]
+use cashu::NotificationPayload;
+#[cfg(feature = "mint")]
+use uuid::Uuid;
+
+use crate::pub_sub::SubId;
+
+pub type WsUnsubscribeRequest = nut17::ws::WsUnsubscribeRequest<SubId>;
+pub type WsNotification = nut17::ws::WsNotification<SubId>;
+pub type WsSubscribeResponse = nut17::ws::WsSubscribeResponse<SubId>;
+pub type WsResponseResult = nut17::ws::WsResponseResult<SubId>;
+pub type WsUnsubscribeResponse = nut17::ws::WsUnsubscribeResponse<SubId>;
+pub type WsRequest = nut17::ws::WsRequest<SubId>;
+pub type WsResponse = nut17::ws::WsResponse<SubId>;
+pub type WsMethodRequest = nut17::ws::WsMethodRequest<SubId>;
+pub type WsErrorBody = nut17::ws::WsErrorBody;
+pub type WsMessageOrResponse = nut17::ws::WsMessageOrResponse<SubId>;
+pub type NotificationInner<T> = nut17::ws::NotificationInner<T, SubId>;
+
+#[cfg(feature = "mint")]
+pub fn notification_uuid_to_notification_string(
+    notification: NotificationInner<Uuid>,
+) -> NotificationInner<String> {
+    nut17::ws::NotificationInner {
+        sub_id: notification.sub_id,
+        payload: match notification.payload {
+            NotificationPayload::ProofState(pk) => NotificationPayload::ProofState(pk),
+            NotificationPayload::MeltQuoteBolt11Response(quote) => {
+                NotificationPayload::MeltQuoteBolt11Response(quote.to_string_id())
+            }
+            NotificationPayload::MintQuoteBolt11Response(quote) => {
+                NotificationPayload::MintQuoteBolt11Response(quote.to_string_id())
+            }
+        },
+    }
+}
+
+#[cfg(feature = "mint")]
+pub fn notification_to_ws_message(notification: NotificationInner<Uuid>) -> WsMessageOrResponse {
+    nut17::ws::WsMessageOrResponse::Notification(nut17::ws::WsNotification {
+        jsonrpc: JSON_RPC_VERSION.to_owned(),
+        method: "subscribe".to_string(),
+        params: notification_uuid_to_notification_string(notification),
+    })
+}

+ 9 - 6
crates/cdk-integration-tests/tests/mint.rs

@@ -11,11 +11,11 @@ use cdk::cdk_database::mint_memory::MintMemoryDatabase;
 use cdk::dhke::construct_proofs;
 use cdk::mint::MintQuote;
 use cdk::nuts::nut00::ProofsMethods;
-use cdk::nuts::nut17::Params;
 use cdk::nuts::{
     CurrencyUnit, Id, MintBolt11Request, MintInfo, NotificationPayload, Nuts, PreMintSecrets,
     ProofState, Proofs, SecretKey, SpendingConditions, State, SwapRequest,
 };
+use cdk::subscription::{IndexableParams, Params};
 use cdk::types::QuoteTTL;
 use cdk::util::unix_time;
 use cdk::Mint;
@@ -232,11 +232,14 @@ pub async fn test_p2pk_swap() -> Result<()> {
 
     let mut listener = mint
         .pubsub_manager
-        .try_subscribe(Params {
-            kind: cdk::nuts::nut17::Kind::ProofState,
-            filters: public_keys_to_listen.clone(),
-            id: "test".into(),
-        })
+        .try_subscribe::<IndexableParams>(
+            Params {
+                kind: cdk::nuts::nut17::Kind::ProofState,
+                filters: public_keys_to_listen.clone(),
+                id: "test".into(),
+            }
+            .into(),
+        )
         .await
         .expect("valid subscription");
 

+ 8 - 5
crates/cdk-redb/Cargo.toml

@@ -7,20 +7,23 @@ description = "Redb storage backend for CDK"
 license = "MIT"
 homepage = "https://github.com/cashubtc/cdk"
 repository = "https://github.com/cashubtc/cdk.git"
-rust-version = "1.66.0" # MSRV
+rust-version = "1.66.0"                            # MSRV
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [features]
 default = ["mint", "wallet"]
-mint = ["cdk/mint"]
-wallet = ["cdk/wallet"]
+mint = []
+wallet = []
 
 [dependencies]
 async-trait = "0.1"
-cdk = { path = "../cdk", version = "0.6.0", default-features = false }
+cdk-common = { path = "../cdk-common", version = "0.6.0" }
 redb = "2.1.0"
 thiserror = "1"
-tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] }
+tracing = { version = "0.1", default-features = false, features = [
+    "attributes",
+    "log",
+] }
 serde = { version = "1", default-features = false, features = ["derive"] }
 serde_json = "1"
 lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }

+ 7 - 7
crates/cdk-redb/src/error.rs

@@ -33,22 +33,22 @@ pub enum Error {
     ParseInt(#[from] ParseIntError),
     /// CDK Database Error
     #[error(transparent)]
-    CDKDatabase(#[from] cdk::cdk_database::Error),
+    CDKDatabase(#[from] cdk_common::database::Error),
     /// CDK Mint Url Error
     #[error(transparent)]
-    CDKMintUrl(#[from] cdk::mint_url::Error),
+    CDKMintUrl(#[from] cdk_common::mint_url::Error),
     /// CDK Error
     #[error(transparent)]
-    CDK(#[from] cdk::error::Error),
+    CDK(#[from] cdk_common::error::Error),
     /// NUT00 Error
     #[error(transparent)]
-    CDKNUT00(#[from] cdk::nuts::nut00::Error),
+    CDKNUT00(#[from] cdk_common::nuts::nut00::Error),
     /// NUT02 Error
     #[error(transparent)]
-    CDKNUT02(#[from] cdk::nuts::nut02::Error),
+    CDKNUT02(#[from] cdk_common::nuts::nut02::Error),
     /// DHKE Error
     #[error(transparent)]
-    DHKE(#[from] cdk::dhke::Error),
+    DHKE(#[from] cdk_common::dhke::Error),
     /// Unknown Mint Info
     #[error("Unknown mint info")]
     UnknownMintInfo,
@@ -60,7 +60,7 @@ pub enum Error {
     UnknownDatabaseVersion,
 }
 
-impl From<Error> for cdk::cdk_database::Error {
+impl From<Error> for cdk_common::database::Error {
     fn from(e: Error) -> Self {
         Self::Database(Box::new(e))
     }

+ 2 - 3
crates/cdk-redb/src/migrations.rs

@@ -1,9 +1,8 @@
 use std::collections::HashMap;
 use std::sync::Arc;
 
-use cdk::mint_url::MintUrl;
-use cdk::nuts::{CurrencyUnit, MeltQuoteState, MintQuoteState};
-use cdk::Amount;
+use cdk_common::mint_url::MintUrl;
+use cdk_common::{Amount, CurrencyUnit, MeltQuoteState, MintQuoteState};
 use redb::{Database, ReadableTable, TableDefinition};
 use serde::{Deserialize, Serialize};
 

+ 3 - 4
crates/cdk-redb/src/mint/migrations.rs

@@ -3,10 +3,9 @@ use std::collections::HashMap;
 use std::str::FromStr;
 use std::sync::Arc;
 
-use cdk::mint::MintQuote;
-use cdk::mint_url::MintUrl;
-use cdk::nuts::{CurrencyUnit, MintQuoteState, Proof, State};
-use cdk::Amount;
+use cdk_common::mint::MintQuote;
+use cdk_common::mint_url::MintUrl;
+use cdk_common::{Amount, CurrencyUnit, MintQuoteState, Proof, State};
 use lightning_invoice::Bolt11Invoice;
 use redb::{
     Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition,

+ 7 - 8
crates/cdk-redb/src/mint/mod.rs

@@ -7,16 +7,15 @@ use std::str::FromStr;
 use std::sync::Arc;
 
 use async_trait::async_trait;
-use cdk::cdk_database::MintDatabase;
-use cdk::dhke::hash_to_curve;
-use cdk::mint::{MintKeySetInfo, MintQuote};
-use cdk::nuts::nut00::ProofsMethods;
-use cdk::nuts::{
+use cdk_common::common::LnKey;
+use cdk_common::database::{self, MintDatabase};
+use cdk_common::dhke::hash_to_curve;
+use cdk_common::mint::{self, MintKeySetInfo, MintQuote};
+use cdk_common::nut00::ProofsMethods;
+use cdk_common::{
     BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, Proof,
     Proofs, PublicKey, State,
 };
-use cdk::types::LnKey;
-use cdk::{cdk_database, mint};
 use migrations::{migrate_01_to_02, migrate_04_to_05};
 use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
 use uuid::Uuid;
@@ -162,7 +161,7 @@ impl MintRedbDatabase {
 
 #[async_trait]
 impl MintDatabase for MintRedbDatabase {
-    type Err = cdk_database::Error;
+    type Err = database::Error;
 
     async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err> {
         let write_txn = self.db.begin_write().map_err(Error::from)?;

+ 1 - 1
crates/cdk-redb/src/wallet/migrations.rs

@@ -3,7 +3,7 @@ use std::ops::Deref;
 use std::str::FromStr;
 use std::sync::Arc;
 
-use cdk::mint_url::MintUrl;
+use cdk_common::mint_url::MintUrl;
 use redb::{
     Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition,
 };

+ 9 - 10
crates/cdk-redb/src/wallet/mod.rs

@@ -7,15 +7,14 @@ use std::str::FromStr;
 use std::sync::Arc;
 
 use async_trait::async_trait;
-use cdk::cdk_database::WalletDatabase;
-use cdk::mint_url::MintUrl;
-use cdk::nuts::{
-    CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State,
+use cdk_common::common::ProofInfo;
+use cdk_common::database::WalletDatabase;
+use cdk_common::mint_url::MintUrl;
+use cdk_common::util::unix_time;
+use cdk_common::wallet::{self, MintQuote};
+use cdk_common::{
+    database, CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State,
 };
-use cdk::types::ProofInfo;
-use cdk::util::unix_time;
-use cdk::wallet::MintQuote;
-use cdk::{cdk_database, wallet};
 use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
 use tracing::instrument;
 
@@ -153,7 +152,7 @@ impl WalletRedbDatabase {
         &self,
         ys: Vec<PublicKey>,
         state: State,
-    ) -> Result<(), cdk_database::Error> {
+    ) -> Result<(), database::Error> {
         let read_txn = self.db.begin_read().map_err(Error::from)?;
         let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
 
@@ -192,7 +191,7 @@ impl WalletRedbDatabase {
 
 #[async_trait]
 impl WalletDatabase for WalletRedbDatabase {
-    type Err = cdk_database::Error;
+    type Err = database::Error;
 
     #[instrument(skip(self))]
     async fn add_mint(

+ 15 - 10
crates/cdk-sqlite/Cargo.toml

@@ -7,26 +7,31 @@ description = "SQLite storage backend for CDK"
 license = "MIT"
 homepage = "https://github.com/cashubtc/cdk"
 repository = "https://github.com/cashubtc/cdk.git"
-rust-version = "1.66.0" # MSRV
+rust-version = "1.66.0"                            # MSRV
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [features]
 default = ["mint", "wallet"]
-mint = ["cdk/mint"]
-wallet = ["cdk/wallet"]
+mint = []
+wallet = []
 
 [dependencies]
 async-trait = "0.1"
-cdk = { path = "../cdk", version = "0.6.0", default-features = false }
+cdk-common = { path = "../cdk-common", version = "0.6.0" }
 bitcoin = { version = "0.32.2", default-features = false }
-sqlx = { version = "0.6.3", default-features = false, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "uuid"] }
-thiserror = "1"
-tokio = { version = "1", features = [
-    "time",
+sqlx = { version = "0.6.3", default-features = false, features = [
+    "runtime-tokio-rustls",
+    "sqlite",
     "macros",
-    "sync",
+    "migrate",
+    "uuid",
+] }
+thiserror = "1"
+tokio = { version = "1", features = ["time", "macros", "sync"] }
+tracing = { version = "0.1", default-features = false, features = [
+    "attributes",
+    "log",
 ] }
-tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] }
 serde_json = "1"
 lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }
 uuid = { version = "1", features = ["v4", "serde"] }

+ 9 - 9
crates/cdk-sqlite/src/mint/error.rs

@@ -10,31 +10,31 @@ pub enum Error {
     SQLX(#[from] sqlx::Error),
     /// NUT00 Error
     #[error(transparent)]
-    CDKNUT00(#[from] cdk::nuts::nut00::Error),
+    CDKNUT00(#[from] cdk_common::nuts::nut00::Error),
     /// NUT01 Error
     #[error(transparent)]
-    CDKNUT01(#[from] cdk::nuts::nut01::Error),
+    CDKNUT01(#[from] cdk_common::nuts::nut01::Error),
     /// NUT02 Error
     #[error(transparent)]
-    CDKNUT02(#[from] cdk::nuts::nut02::Error),
+    CDKNUT02(#[from] cdk_common::nuts::nut02::Error),
     /// NUT04 Error
     #[error(transparent)]
-    CDKNUT04(#[from] cdk::nuts::nut04::Error),
+    CDKNUT04(#[from] cdk_common::nuts::nut04::Error),
     /// NUT05 Error
     #[error(transparent)]
-    CDKNUT05(#[from] cdk::nuts::nut05::Error),
+    CDKNUT05(#[from] cdk_common::nuts::nut05::Error),
     /// NUT07 Error
     #[error(transparent)]
-    CDKNUT07(#[from] cdk::nuts::nut07::Error),
+    CDKNUT07(#[from] cdk_common::nuts::nut07::Error),
     /// Secret Error
     #[error(transparent)]
-    CDKSECRET(#[from] cdk::secret::Error),
+    CDKSECRET(#[from] cdk_common::secret::Error),
     /// BIP32 Error
     #[error(transparent)]
     BIP32(#[from] bitcoin::bip32::Error),
     /// Mint Url Error
     #[error(transparent)]
-    MintUrl(#[from] cdk::mint_url::Error),
+    MintUrl(#[from] cdk_common::mint_url::Error),
     /// Could Not Initialize Database
     #[error("Could not initialize database")]
     CouldNotInitialize,
@@ -46,7 +46,7 @@ pub enum Error {
     Serde(#[from] serde_json::Error),
 }
 
-impl From<Error> for cdk::cdk_database::Error {
+impl From<Error> for cdk_common::database::Error {
     fn from(e: Error) -> Self {
         Self::Database(Box::new(e))
     }

+ 11 - 12
crates/cdk-sqlite/src/mint/mod.rs

@@ -7,18 +7,17 @@ use std::time::Duration;
 
 use async_trait::async_trait;
 use bitcoin::bip32::DerivationPath;
-use cdk::cdk_database::{self, MintDatabase};
-use cdk::mint::{MintKeySetInfo, MintQuote};
-use cdk::mint_url::MintUrl;
-use cdk::nuts::nut00::ProofsMethods;
-use cdk::nuts::nut05::QuoteState;
-use cdk::nuts::{
-    BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState,
-    MintQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SecretKey, State,
+use cdk_common::common::LnKey;
+use cdk_common::database::{self, MintDatabase};
+use cdk_common::mint::{self, MintKeySetInfo, MintQuote};
+use cdk_common::mint_url::MintUrl;
+use cdk_common::nut00::ProofsMethods;
+use cdk_common::nut05::QuoteState;
+use cdk_common::secret::Secret;
+use cdk_common::{
+    Amount, BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltBolt11Request,
+    MeltQuoteState, MintQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SecretKey, State,
 };
-use cdk::secret::Secret;
-use cdk::types::LnKey;
-use cdk::{mint, Amount};
 use error::Error;
 use lightning_invoice::Bolt11Invoice;
 use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions, SqliteRow};
@@ -63,7 +62,7 @@ impl MintSqliteDatabase {
 
 #[async_trait]
 impl MintDatabase for MintSqliteDatabase {
-    type Err = cdk_database::Error;
+    type Err = database::Error;
 
     async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err> {
         let mut transaction = self.pool.begin().await.map_err(Error::from)?;

+ 9 - 9
crates/cdk-sqlite/src/wallet/error.rs

@@ -13,28 +13,28 @@ pub enum Error {
     Serde(#[from] serde_json::Error),
     /// NUT00 Error
     #[error(transparent)]
-    CDKNUT00(#[from] cdk::nuts::nut00::Error),
+    CDKNUT00(#[from] cdk_common::nuts::nut00::Error),
     /// NUT01 Error
     #[error(transparent)]
-    CDKNUT01(#[from] cdk::nuts::nut01::Error),
+    CDKNUT01(#[from] cdk_common::nuts::nut01::Error),
     /// NUT02 Error
     #[error(transparent)]
-    CDKNUT02(#[from] cdk::nuts::nut02::Error),
+    CDKNUT02(#[from] cdk_common::nuts::nut02::Error),
     /// NUT04 Error
     #[error(transparent)]
-    CDKNUT04(#[from] cdk::nuts::nut04::Error),
+    CDKNUT04(#[from] cdk_common::nuts::nut04::Error),
     /// NUT05 Error
     #[error(transparent)]
-    CDKNUT05(#[from] cdk::nuts::nut05::Error),
+    CDKNUT05(#[from] cdk_common::nuts::nut05::Error),
     /// NUT07 Error
     #[error(transparent)]
-    CDKNUT07(#[from] cdk::nuts::nut07::Error),
+    CDKNUT07(#[from] cdk_common::nuts::nut07::Error),
     /// Secret Error
     #[error(transparent)]
-    CDKSECRET(#[from] cdk::secret::Error),
+    CDKSECRET(#[from] cdk_common::secret::Error),
     /// Mint Url
     #[error(transparent)]
-    MintUrl(#[from] cdk::mint_url::Error),
+    MintUrl(#[from] cdk_common::mint_url::Error),
     /// BIP32 Error
     #[error(transparent)]
     BIP32(#[from] bitcoin::bip32::Error),
@@ -46,7 +46,7 @@ pub enum Error {
     InvalidDbPath,
 }
 
-impl From<Error> for cdk::cdk_database::Error {
+impl From<Error> for cdk_common::database::Error {
     fn from(e: Error) -> Self {
         Self::Database(Box::new(e))
     }

+ 11 - 12
crates/cdk-sqlite/src/wallet/mod.rs

@@ -5,17 +5,16 @@ use std::path::Path;
 use std::str::FromStr;
 
 use async_trait::async_trait;
-use cdk::amount::Amount;
-use cdk::cdk_database::{self, WalletDatabase};
-use cdk::mint_url::MintUrl;
-use cdk::nuts::{
-    CurrencyUnit, Id, KeySetInfo, Keys, MeltQuoteState, MintInfo, MintQuoteState, Proof, PublicKey,
-    SecretKey, SpendingConditions, State,
+use cdk_common::common::ProofInfo;
+use cdk_common::database::WalletDatabase;
+use cdk_common::mint_url::MintUrl;
+use cdk_common::nuts::{MeltQuoteState, MintQuoteState};
+use cdk_common::secret::Secret;
+use cdk_common::wallet::{self, MintQuote};
+use cdk_common::{
+    database, Amount, CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, Proof, PublicKey, SecretKey,
+    SpendingConditions, State,
 };
-use cdk::secret::Secret;
-use cdk::types::ProofInfo;
-use cdk::wallet;
-use cdk::wallet::MintQuote;
 use error::Error;
 use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqliteRow};
 use sqlx::{ConnectOptions, Row};
@@ -54,7 +53,7 @@ impl WalletSqliteDatabase {
             .expect("Could not run migrations");
     }
 
-    async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), cdk_database::Error> {
+    async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), database::Error> {
         sqlx::query(
             r#"
     UPDATE proof
@@ -74,7 +73,7 @@ impl WalletSqliteDatabase {
 
 #[async_trait]
 impl WalletDatabase for WalletSqliteDatabase {
-    type Err = cdk_database::Error;
+    type Err = database::Error;
 
     #[instrument(skip(self, mint_info))]
     async fn add_mint(

+ 9 - 7
crates/cdk/Cargo.toml

@@ -12,15 +12,17 @@ license = "MIT"
 
 [features]
 default = ["mint", "wallet"]
-mint = ["dep:futures"]
+mint = ["dep:futures", "cdk-common/mint"]
 # We do not commit to a MSRV with swagger enabled
-swagger = ["mint", "dep:utoipa"]
-wallet = ["dep:reqwest"]
+swagger = ["mint", "dep:utoipa", "cdk-common/swagger"]
+wallet = ["dep:reqwest", "cdk-common/wallet"]
 bench = []
 http_subscription = []
 
 
 [dependencies]
+cdk-common = { path = "../cdk-common", version = "0.6.0" }
+cbor-diag = "0.1.12"
 arc-swap = "1.7.1"
 async-trait = "0.1"
 anyhow = { version = "1.0.43", features = ["backtrace"] }
@@ -31,16 +33,17 @@ bitcoin = { version = "0.32.2", features = [
     "rand-std",
 ] }
 ciborium = { version = "0.2.2", default-features = false, features = ["std"] }
-cbor-diag = "0.1.12"
 lightning-invoice = { version = "0.32.0", features = ["serde", "std"] }
-once_cell = "1.19"
 regex = "1"
 reqwest = { version = "0.12", default-features = false, features = [
     "json",
     "rustls-tls",
     "rustls-tls-native-roots",
     "socks",
-    "zstd", "brotli", "gzip", "deflate"
+    "zstd",
+    "brotli",
+    "gzip",
+    "deflate",
 ], optional = true }
 serde = { version = "1", default-features = false, features = ["derive"] }
 serde_json = "1"
@@ -77,7 +80,6 @@ tokio-tungstenite = { version = "0.19.0", features = [
 [target.'cfg(target_arch = "wasm32")'.dependencies]
 tokio = { version = "1.21", features = ["rt", "macros", "sync", "time"] }
 getrandom = { version = "0.2", features = ["js"] }
-instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] }
 
 [[example]]
 name = "mint-token"

+ 4 - 3
crates/cdk/src/cdk_database/mint_memory.rs

@@ -4,13 +4,14 @@ use std::collections::HashMap;
 use std::sync::Arc;
 
 use async_trait::async_trait;
+use cdk_common::database::{Error, MintDatabase};
+use cdk_common::mint::MintKeySetInfo;
+use cdk_common::nut00::ProofsMethods;
 use tokio::sync::{Mutex, RwLock};
 use uuid::Uuid;
 
-use super::{Error, MintDatabase};
 use crate::dhke::hash_to_curve;
-use crate::mint::{self, MintKeySetInfo, MintQuote};
-use crate::nuts::nut00::ProofsMethods;
+use crate::mint::{self, MintQuote};
 use crate::nuts::nut07::State;
 use crate::nuts::{
     nut07, BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState,

+ 2 - 281
crates/cdk/src/cdk_database/mod.rs

@@ -1,290 +1,11 @@
 //! CDK Database
 
-#[cfg(any(feature = "wallet", feature = "mint"))]
-use std::collections::HashMap;
-use std::fmt::Debug;
-
-#[cfg(any(feature = "wallet", feature = "mint"))]
-use async_trait::async_trait;
-use thiserror::Error;
-#[cfg(feature = "mint")]
-use uuid::Uuid;
-
-#[cfg(feature = "mint")]
-use crate::mint;
-#[cfg(feature = "mint")]
-use crate::mint::MintKeySetInfo;
-#[cfg(feature = "mint")]
-use crate::mint::MintQuote as MintMintQuote;
-#[cfg(feature = "wallet")]
-use crate::mint_url::MintUrl;
-#[cfg(feature = "mint")]
-use crate::nuts::MeltBolt11Request;
-#[cfg(feature = "mint")]
-use crate::nuts::{BlindSignature, MeltQuoteState, MintQuoteState, Proof, Proofs};
-#[cfg(any(feature = "wallet", feature = "mint"))]
-use crate::nuts::{CurrencyUnit, Id, PublicKey, State};
-#[cfg(feature = "wallet")]
-use crate::nuts::{KeySetInfo, Keys, MintInfo, SpendingConditions};
-#[cfg(feature = "mint")]
-use crate::types::LnKey;
-#[cfg(feature = "wallet")]
-use crate::types::ProofInfo;
-#[cfg(feature = "wallet")]
-use crate::wallet;
-#[cfg(feature = "wallet")]
-use crate::wallet::MintQuote as WalletMintQuote;
-
 #[cfg(feature = "mint")]
 pub mod mint_memory;
 #[cfg(feature = "wallet")]
 pub mod wallet_memory;
 
+/// re-export types
+pub use cdk_common::database::{Error, MintDatabase, WalletDatabase};
 #[cfg(feature = "wallet")]
 pub use wallet_memory::WalletMemoryDatabase;
-
-/// CDK_database error
-#[derive(Debug, Error)]
-pub enum Error {
-    /// Database Error
-    #[error(transparent)]
-    Database(Box<dyn std::error::Error + Send + Sync>),
-    /// DHKE error
-    #[error(transparent)]
-    DHKE(#[from] crate::dhke::Error),
-    /// NUT00 Error
-    #[error(transparent)]
-    NUT00(#[from] crate::nuts::nut00::Error),
-    /// NUT02 Error
-    #[error(transparent)]
-    NUT02(#[from] crate::nuts::nut02::Error),
-    /// Serde Error
-    #[error(transparent)]
-    Serde(#[from] serde_json::Error),
-    /// Unknown Quote
-    #[error("Unknown Quote")]
-    UnknownQuote,
-}
-
-/// Wallet Database trait
-#[cfg(feature = "wallet")]
-#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
-#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
-pub trait WalletDatabase: Debug {
-    /// Wallet Database Error
-    type Err: Into<Error> + From<Error>;
-
-    /// Add Mint to storage
-    async fn add_mint(
-        &self,
-        mint_url: MintUrl,
-        mint_info: Option<MintInfo>,
-    ) -> Result<(), Self::Err>;
-    /// Remove Mint from storage
-    async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), Self::Err>;
-    /// Get mint from storage
-    async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, Self::Err>;
-    /// Get all mints from storage
-    async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, Self::Err>;
-    /// Update mint url
-    async fn update_mint_url(
-        &self,
-        old_mint_url: MintUrl,
-        new_mint_url: MintUrl,
-    ) -> Result<(), Self::Err>;
-
-    /// Add mint keyset to storage
-    async fn add_mint_keysets(
-        &self,
-        mint_url: MintUrl,
-        keysets: Vec<KeySetInfo>,
-    ) -> Result<(), Self::Err>;
-    /// Get mint keysets for mint url
-    async fn get_mint_keysets(
-        &self,
-        mint_url: MintUrl,
-    ) -> Result<Option<Vec<KeySetInfo>>, Self::Err>;
-    /// Get mint keyset by id
-    async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result<Option<KeySetInfo>, Self::Err>;
-
-    /// Add mint quote to storage
-    async fn add_mint_quote(&self, quote: WalletMintQuote) -> Result<(), Self::Err>;
-    /// Get mint quote from storage
-    async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<WalletMintQuote>, Self::Err>;
-    /// Get mint quotes from storage
-    async fn get_mint_quotes(&self) -> Result<Vec<WalletMintQuote>, Self::Err>;
-    /// Remove mint quote from storage
-    async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err>;
-
-    /// Add melt quote to storage
-    async fn add_melt_quote(&self, quote: wallet::MeltQuote) -> Result<(), Self::Err>;
-    /// Get melt quote from storage
-    async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<wallet::MeltQuote>, Self::Err>;
-    /// Remove melt quote from storage
-    async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err>;
-
-    /// Add [`Keys`] to storage
-    async fn add_keys(&self, keys: Keys) -> Result<(), Self::Err>;
-    /// Get [`Keys`] from storage
-    async fn get_keys(&self, id: &Id) -> Result<Option<Keys>, Self::Err>;
-    /// Remove [`Keys`] from storage
-    async fn remove_keys(&self, id: &Id) -> Result<(), Self::Err>;
-
-    /// Update the proofs in storage by adding new proofs or removing proofs by
-    /// their Y value.
-    async fn update_proofs(
-        &self,
-        added: Vec<ProofInfo>,
-        removed_ys: Vec<PublicKey>,
-    ) -> Result<(), Self::Err>;
-    /// Set proofs as pending in storage. Proofs are identified by their Y
-    /// value.
-    async fn set_pending_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
-    /// Reserve proofs in storage. Proofs are identified by their Y value.
-    async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
-    /// Set proofs as unspent in storage. Proofs are identified by their Y
-    /// value.
-    async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
-    /// Get proofs from storage
-    async fn get_proofs(
-        &self,
-        mint_url: Option<MintUrl>,
-        unit: Option<CurrencyUnit>,
-        state: Option<Vec<State>>,
-        spending_conditions: Option<Vec<SpendingConditions>>,
-    ) -> Result<Vec<ProofInfo>, Self::Err>;
-
-    /// Increment Keyset counter
-    async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err>;
-    /// Get current Keyset counter
-    async fn get_keyset_counter(&self, keyset_id: &Id) -> Result<Option<u32>, Self::Err>;
-
-    /// Get when nostr key was last checked
-    async fn get_nostr_last_checked(
-        &self,
-        verifying_key: &PublicKey,
-    ) -> Result<Option<u32>, Self::Err>;
-    /// Update last checked time
-    async fn add_nostr_last_checked(
-        &self,
-        verifying_key: PublicKey,
-        last_checked: u32,
-    ) -> Result<(), Self::Err>;
-}
-
-/// Mint Database trait
-#[cfg(feature = "mint")]
-#[async_trait]
-pub trait MintDatabase {
-    /// Mint Database Error
-    type Err: Into<Error> + From<Error>;
-
-    /// Add Active Keyset
-    async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err>;
-    /// Get Active Keyset
-    async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result<Option<Id>, Self::Err>;
-    /// Get all Active Keyset
-    async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, Self::Err>;
-
-    /// Add [`MintMintQuote`]
-    async fn add_mint_quote(&self, quote: MintMintQuote) -> Result<(), Self::Err>;
-    /// Get [`MintMintQuote`]
-    async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintMintQuote>, Self::Err>;
-    /// Update state of [`MintMintQuote`]
-    async fn update_mint_quote_state(
-        &self,
-        quote_id: &Uuid,
-        state: MintQuoteState,
-    ) -> Result<MintQuoteState, Self::Err>;
-    /// Get all [`MintMintQuote`]s
-    async fn get_mint_quote_by_request(
-        &self,
-        request: &str,
-    ) -> Result<Option<MintMintQuote>, Self::Err>;
-    /// Get all [`MintMintQuote`]s
-    async fn get_mint_quote_by_request_lookup_id(
-        &self,
-        request_lookup_id: &str,
-    ) -> Result<Option<MintMintQuote>, Self::Err>;
-    /// Get Mint Quotes
-    async fn get_mint_quotes(&self) -> Result<Vec<MintMintQuote>, Self::Err>;
-    /// Remove [`MintMintQuote`]
-    async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>;
-
-    /// Add [`mint::MeltQuote`]
-    async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err>;
-    /// Get [`mint::MeltQuote`]
-    async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err>;
-    /// Update [`mint::MeltQuote`] state
-    async fn update_melt_quote_state(
-        &self,
-        quote_id: &Uuid,
-        state: MeltQuoteState,
-    ) -> Result<MeltQuoteState, Self::Err>;
-    /// Get all [`mint::MeltQuote`]s
-    async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, Self::Err>;
-    /// Remove [`mint::MeltQuote`]
-    async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>;
-
-    /// Add melt request
-    async fn add_melt_request(
-        &self,
-        melt_request: MeltBolt11Request<Uuid>,
-        ln_key: LnKey,
-    ) -> Result<(), Self::Err>;
-    /// Get melt request
-    async fn get_melt_request(
-        &self,
-        quote_id: &Uuid,
-    ) -> Result<Option<(MeltBolt11Request<Uuid>, LnKey)>, Self::Err>;
-
-    /// Add [`MintKeySetInfo`]
-    async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err>;
-    /// Get [`MintKeySetInfo`]
-    async fn get_keyset_info(&self, id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err>;
-    /// Get [`MintKeySetInfo`]s
-    async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err>;
-
-    /// Add spent [`Proofs`]
-    async fn add_proofs(&self, proof: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err>;
-    /// Get [`Proofs`] by ys
-    async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err>;
-    /// Get ys by quote id
-    async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result<Vec<PublicKey>, Self::Err>;
-    /// Get [`Proofs`] state
-    async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err>;
-    /// Get [`Proofs`] state
-    async fn update_proofs_states(
-        &self,
-        ys: &[PublicKey],
-        proofs_state: State,
-    ) -> Result<Vec<Option<State>>, Self::Err>;
-    /// Get [`Proofs`] by state
-    async fn get_proofs_by_keyset_id(
-        &self,
-        keyset_id: &Id,
-    ) -> Result<(Proofs, Vec<Option<State>>), Self::Err>;
-
-    /// Add [`BlindSignature`]
-    async fn add_blind_signatures(
-        &self,
-        blinded_messages: &[PublicKey],
-        blind_signatures: &[BlindSignature],
-        quote_id: Option<Uuid>,
-    ) -> Result<(), Self::Err>;
-    /// Get [`BlindSignature`]s
-    async fn get_blind_signatures(
-        &self,
-        blinded_messages: &[PublicKey],
-    ) -> Result<Vec<Option<BlindSignature>>, Self::Err>;
-    /// Get [`BlindSignature`]s for keyset_id
-    async fn get_blind_signatures_for_keyset(
-        &self,
-        keyset_id: &Id,
-    ) -> Result<Vec<BlindSignature>, Self::Err>;
-    /// Get [`BlindSignature`]s for quote
-    async fn get_blind_signatures_for_quote(
-        &self,
-        quote_id: &Uuid,
-    ) -> Result<Vec<BlindSignature>, Self::Err>;
-}

+ 1 - 2
crates/cdk/src/cdk_database/wallet_memory.rs

@@ -4,10 +4,9 @@ use std::collections::{HashMap, HashSet};
 use std::sync::Arc;
 
 use async_trait::async_trait;
+use cdk_common::database::{Error, WalletDatabase};
 use tokio::sync::RwLock;
 
-use super::WalletDatabase;
-use crate::cdk_database::Error;
 use crate::mint_url::MintUrl;
 use crate::nuts::{
     CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State,

+ 1 - 2
crates/cdk/src/fees.rs

@@ -6,9 +6,8 @@ use std::collections::HashMap;
 
 use tracing::instrument;
 
-use crate::error::Error;
 use crate::nuts::Id;
-use crate::Amount;
+use crate::{Amount, Error};
 
 /// Fee required for proof set
 #[instrument(skip_all)]

+ 10 - 16
crates/cdk/src/lib.rs

@@ -3,33 +3,29 @@
 #![warn(missing_docs)]
 #![warn(rustdoc::bare_urls)]
 
-pub mod amount;
 #[cfg(any(feature = "wallet", feature = "mint"))]
 pub mod cdk_database;
-#[cfg(feature = "mint")]
-pub mod cdk_lightning;
-pub mod dhke;
-pub mod error;
+
 #[cfg(feature = "mint")]
 pub mod mint;
-pub mod mint_url;
-pub mod nuts;
-pub mod secret;
-pub mod types;
-pub mod util;
 #[cfg(feature = "wallet")]
 pub mod wallet;
 
 pub mod pub_sub;
 
+/// Re-export amount type
+#[doc(hidden)]
+pub use cdk_common::{
+    amount, common as types, dhke,
+    error::{self, Error},
+    lightning as cdk_lightning, lightning_invoice, mint_url, nuts, secret, subscription, util, ws,
+    Amount, Bolt11Invoice,
+};
+
 pub mod fees;
 
 #[doc(hidden)]
 pub use bitcoin::secp256k1;
-#[doc(hidden)]
-pub use error::Error;
-#[doc(hidden)]
-pub use lightning_invoice::{self, Bolt11Invoice};
 #[cfg(feature = "mint")]
 #[doc(hidden)]
 pub use mint::Mint;
@@ -38,8 +34,6 @@ pub use mint::Mint;
 pub use wallet::{Wallet, WalletSubscription};
 
 #[doc(hidden)]
-pub use self::amount::Amount;
-#[doc(hidden)]
 pub use self::util::SECP256K1;
 #[cfg(feature = "wallet")]
 #[doc(hidden)]

+ 23 - 25
crates/cdk/src/mint/builder.rs

@@ -4,12 +4,12 @@ use std::collections::HashMap;
 use std::sync::Arc;
 
 use anyhow::anyhow;
+use cdk_common::database::{self, MintDatabase};
 
 use super::nut17::SupportedMethods;
 use super::nut19::{self, CachedEndpoint};
 use super::Nuts;
 use crate::amount::Amount;
-use crate::cdk_database::{self, MintDatabase};
 use crate::cdk_lightning::{self, MintLightning};
 use crate::mint::Mint;
 use crate::nuts::{
@@ -26,7 +26,7 @@ pub struct MintBuilder {
     /// Mint Info
     mint_info: MintInfo,
     /// Mint Storage backend
-    localstore: Option<Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>>,
+    localstore: Option<Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>>,
     /// Ln backends for mint
     ln: Option<HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>>,
     seed: Option<Vec<u8>>,
@@ -57,7 +57,7 @@ impl MintBuilder {
     /// Set localstore
     pub fn with_localstore(
         mut self,
-        localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>,
+        localstore: Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>,
     ) -> MintBuilder {
         self.localstore = Some(localstore);
         self
@@ -149,28 +149,26 @@ impl MintBuilder {
             self.mint_info.nuts.nut15 = mpp;
         }
 
-        match method {
-            PaymentMethod::Bolt11 => {
-                let mint_method_settings = MintMethodSettings {
-                    method,
-                    unit: unit.clone(),
-                    min_amount: Some(limits.mint_min),
-                    max_amount: Some(limits.mint_max),
-                    description: settings.invoice_description,
-                };
-
-                self.mint_info.nuts.nut04.methods.push(mint_method_settings);
-                self.mint_info.nuts.nut04.disabled = false;
-
-                let melt_method_settings = MeltMethodSettings {
-                    method,
-                    unit,
-                    min_amount: Some(limits.melt_min),
-                    max_amount: Some(limits.melt_max),
-                };
-                self.mint_info.nuts.nut05.methods.push(melt_method_settings);
-                self.mint_info.nuts.nut05.disabled = false;
-            }
+        if method == PaymentMethod::Bolt11 {
+            let mint_method_settings = MintMethodSettings {
+                method,
+                unit: unit.clone(),
+                min_amount: Some(limits.mint_min),
+                max_amount: Some(limits.mint_max),
+                description: settings.invoice_description,
+            };
+
+            self.mint_info.nuts.nut04.methods.push(mint_method_settings);
+            self.mint_info.nuts.nut04.disabled = false;
+
+            let melt_method_settings = MeltMethodSettings {
+                method,
+                unit,
+                min_amount: Some(limits.melt_min),
+                max_amount: Some(limits.melt_max),
+            };
+            self.mint_info.nuts.nut05.methods.push(melt_method_settings);
+            self.mint_info.nuts.nut05.disabled = false;
         }
 
         ln.insert(ln_key.clone(), ln_backend);

+ 1 - 1
crates/cdk/src/mint/melt.rs

@@ -2,6 +2,7 @@ use std::collections::HashSet;
 use std::str::FromStr;
 
 use anyhow::bail;
+use cdk_common::nut00::ProofsMethods;
 use lightning_invoice::Bolt11Invoice;
 use tracing::instrument;
 use uuid::Uuid;
@@ -13,7 +14,6 @@ use super::{
 use crate::amount::to_unit;
 use crate::cdk_lightning::{MintLightning, PayInvoiceResponse};
 use crate::mint::SigFlag;
-use crate::nuts::nut00::ProofsMethods;
 use crate::nuts::nut11::{enforce_sig_flag, EnforceSigFlag};
 use crate::nuts::{Id, MeltQuoteState};
 use crate::types::LnKey;

+ 10 - 49
crates/cdk/src/mint/mod.rs

@@ -6,22 +6,24 @@ use std::sync::Arc;
 
 use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv};
 use bitcoin::secp256k1::{self, Secp256k1};
+use cdk_common::common::{LnKey, QuoteTTL};
+use cdk_common::database::{self, MintDatabase};
+use cdk_common::mint::MintKeySetInfo;
 use config::SwappableConfig;
 use futures::StreamExt;
 use serde::{Deserialize, Serialize};
+use subscription::PubSubManager;
 use tokio::sync::Notify;
 use tokio::task::JoinSet;
 use tracing::instrument;
 use uuid::Uuid;
 
-use crate::cdk_database::{self, MintDatabase};
 use crate::cdk_lightning::{self, MintLightning};
 use crate::dhke::{sign_message, verify_message};
 use crate::error::Error;
 use crate::fees::calculate_fee;
 use crate::mint_url::MintUrl;
 use crate::nuts::*;
-use crate::types::{LnKey, QuoteTTL};
 use crate::util::unix_time;
 use crate::Amount;
 
@@ -33,11 +35,11 @@ mod keysets;
 mod melt;
 mod mint_nut04;
 mod start_up_check;
+pub mod subscription;
 mod swap;
-pub mod types;
 
 pub use builder::{MintBuilder, MintMeltLimits};
-pub use types::{MeltQuote, MintQuote};
+pub use cdk_common::mint::{MeltQuote, MintQuote};
 
 /// Cashu Mint
 #[derive(Clone)]
@@ -45,7 +47,7 @@ pub struct Mint {
     /// Mint Config
     pub config: SwappableConfig,
     /// Mint Storage backend
-    pub localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>,
+    pub localstore: Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>,
     /// Ln backends for mint
     pub ln: HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>,
     /// Subscription manager
@@ -62,7 +64,7 @@ impl Mint {
         seed: &[u8],
         mint_info: MintInfo,
         quote_ttl: QuoteTTL,
-        localstore: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>,
+        localstore: Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>,
         ln: HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>,
         // Hashmap where the key is the unit and value is (input fee ppk, max_order)
         supported_units: HashMap<CurrencyUnit, (u64, u8)>,
@@ -516,47 +518,6 @@ pub struct FeeReserve {
     pub percent_fee_reserve: f32,
 }
 
-/// Mint Keyset Info
-#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MintKeySetInfo {
-    /// Keyset [`Id`]
-    pub id: Id,
-    /// Keyset [`CurrencyUnit`]
-    pub unit: CurrencyUnit,
-    /// Keyset active or inactive
-    /// Mint will only issue new [`BlindSignature`] on active keysets
-    pub active: bool,
-    /// Starting unix time Keyset is valid from
-    pub valid_from: u64,
-    /// When the Keyset is valid to
-    /// This is not shown to the wallet and can only be used internally
-    pub valid_to: Option<u64>,
-    /// [`DerivationPath`] keyset
-    pub derivation_path: DerivationPath,
-    /// DerivationPath index of Keyset
-    pub derivation_path_index: Option<u32>,
-    /// Max order of keyset
-    pub max_order: u8,
-    /// Input Fee ppk
-    #[serde(default = "default_fee")]
-    pub input_fee_ppk: u64,
-}
-
-fn default_fee() -> u64 {
-    0
-}
-
-impl From<MintKeySetInfo> for KeySetInfo {
-    fn from(keyset_info: MintKeySetInfo) -> Self {
-        Self {
-            id: keyset_info.id,
-            unit: keyset_info.unit,
-            active: keyset_info.active,
-            input_fee_ppk: keyset_info.input_fee_ppk,
-        }
-    }
-}
-
 /// Generate new [`MintKeySetInfo`] from path
 #[instrument(skip_all)]
 fn create_new_keyset<C: secp256k1::Signing>(
@@ -605,11 +566,11 @@ mod tests {
     use std::collections::HashSet;
 
     use bitcoin::Network;
+    use cdk_common::common::{LnKey, QuoteTTL};
     use secp256k1::Secp256k1;
     use uuid::Uuid;
 
     use super::*;
-    use crate::types::LnKey;
 
     #[test]
     fn mint_mod_generate_keyset_from_seed() {
@@ -697,7 +658,7 @@ mod tests {
         assert_eq!(amounts_and_pubkeys, expected_amounts_and_pubkeys);
     }
 
-    use cdk_database::mint_memory::MintMemoryDatabase;
+    use crate::cdk_database::mint_memory::MintMemoryDatabase;
 
     #[derive(Default)]
     struct MintConfig<'a> {

+ 34 - 24
crates/cdk/src/nuts/nut17/manager.rs → crates/cdk/src/mint/subscription/manager.rs

@@ -2,10 +2,12 @@
 use std::ops::Deref;
 use std::sync::Arc;
 
+use cdk_common::database::{self, MintDatabase};
+use cdk_common::nut17::Notification;
+use cdk_common::NotificationPayload;
 use uuid::Uuid;
 
-use super::{Notification, NotificationPayload, OnSubscription};
-use crate::cdk_database::{self, MintDatabase};
+use super::OnSubscription;
 use crate::nuts::{
     BlindSignature, MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response,
     MintQuoteState, ProofState,
@@ -26,8 +28,8 @@ impl Default for PubSubManager {
     }
 }
 
-impl From<Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>> for PubSubManager {
-    fn from(val: Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>) -> Self {
+impl From<Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>> for PubSubManager {
+    fn from(val: Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>) -> Self {
         PubSubManager(OnSubscription(Some(val)).into())
     }
 }
@@ -82,19 +84,21 @@ mod test {
     use tokio::time::sleep;
 
     use super::*;
-    use crate::nuts::nut17::{Kind, Params};
+    use crate::nuts::nut17::Kind;
     use crate::nuts::{PublicKey, State};
+    use crate::subscription::{IndexableParams, Params};
 
     #[tokio::test]
     async fn active_and_drop() {
         let manager = PubSubManager::default();
-        let params = Params {
+        let params: IndexableParams = Params {
             kind: Kind::ProofState,
             filters: vec![
                 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2".to_owned(),
             ],
             id: "uno".into(),
-        };
+        }
+        .into();
 
         // Although the same param is used, two subscriptions are created, that
         // is because each index is unique, thanks to `Unique`, it is the
@@ -123,25 +127,31 @@ mod test {
         let manager = PubSubManager::default();
         let mut subscriptions = [
             manager
-                .try_subscribe(Params {
-                    kind: Kind::ProofState,
-                    filters: vec![
-                        "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"
-                            .to_string(),
-                    ],
-                    id: "uno".into(),
-                })
+                .try_subscribe::<IndexableParams>(
+                    Params {
+                        kind: Kind::ProofState,
+                        filters: vec![
+                            "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"
+                                .to_string(),
+                        ],
+                        id: "uno".into(),
+                    }
+                    .into(),
+                )
                 .await
                 .expect("valid subscription"),
             manager
-                .try_subscribe(Params {
-                    kind: Kind::ProofState,
-                    filters: vec![
-                        "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"
-                            .to_string(),
-                    ],
-                    id: "dos".into(),
-                })
+                .try_subscribe::<IndexableParams>(
+                    Params {
+                        kind: Kind::ProofState,
+                        filters: vec![
+                            "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"
+                                .to_string(),
+                        ],
+                        id: "dos".into(),
+                    }
+                    .into(),
+                )
                 .await
                 .expect("valid subscription"),
         ];
@@ -182,7 +192,7 @@ mod test {
     async fn json_test() {
         let manager = PubSubManager::default();
         let mut subscription = manager
-            .try_subscribe::<Params>(
+            .try_subscribe::<IndexableParams>(
                 serde_json::from_str(r#"{"kind":"proof_state","filters":["02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"],"subId":"uno"}"#)
                     .expect("valid json"),
             )

+ 12 - 0
crates/cdk/src/mint/subscription/mod.rs

@@ -0,0 +1,12 @@
+//! Specific Subscription for the cdk crate
+
+#[cfg(feature = "mint")]
+mod manager;
+#[cfg(feature = "mint")]
+mod on_subscription;
+#[cfg(feature = "mint")]
+pub use manager::PubSubManager;
+#[cfg(feature = "mint")]
+pub use on_subscription::OnSubscription;
+
+pub use crate::pub_sub::SubId;

+ 5 - 4
crates/cdk/src/nuts/nut17/on_subscription.rs → crates/cdk/src/mint/subscription/on_subscription.rs

@@ -3,12 +3,13 @@
 //! This module contains the code that is triggered when a new subscription is created.
 use std::sync::Arc;
 
+use cdk_common::database::{self, MintDatabase};
+use cdk_common::nut17::Notification;
+use cdk_common::pub_sub::OnNewSubscription;
+use cdk_common::NotificationPayload;
 use uuid::Uuid;
 
-use super::{Notification, NotificationPayload};
-use crate::cdk_database::{self, MintDatabase};
 use crate::nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey};
-use crate::pub_sub::OnNewSubscription;
 
 #[derive(Default)]
 /// Subscription Init
@@ -17,7 +18,7 @@ use crate::pub_sub::OnNewSubscription;
 ///
 /// It is used to send the initial state of the subscription to the client.
 pub struct OnSubscription(
-    pub(crate) Option<Arc<dyn MintDatabase<Err = cdk_database::Error> + Send + Sync>>,
+    pub(crate) Option<Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>>,
 );
 
 #[async_trait::async_trait]

+ 3 - 60
crates/cdk/src/pub_sub/mod.rs → crates/cdk/src/pub_sub.rs

@@ -12,18 +12,15 @@ use std::cmp::Ordering;
 use std::collections::{BTreeMap, HashSet};
 use std::fmt::Debug;
 use std::ops::{Deref, DerefMut};
-use std::str::FromStr;
 use std::sync::atomic::{self, AtomicUsize};
 use std::sync::Arc;
 
-use serde::{Deserialize, Serialize};
+pub use cdk_common::pub_sub::index::{Index, Indexable, SubscriptionGlobalId};
+use cdk_common::pub_sub::OnNewSubscription;
+pub use cdk_common::pub_sub::SubId;
 use tokio::sync::{mpsc, RwLock};
 use tokio::task::JoinHandle;
 
-mod index;
-
-pub use index::{Index, Indexable, SubscriptionGlobalId};
-
 type IndexTree<T, I> = Arc<RwLock<BTreeMap<Index<I>, mpsc::Sender<(SubId, T)>>>>;
 
 /// Default size of the remove channel
@@ -32,25 +29,6 @@ pub const DEFAULT_REMOVE_SIZE: usize = 10_000;
 /// Default channel size for subscription buffering
 pub const DEFAULT_CHANNEL_SIZE: usize = 10;
 
-#[async_trait::async_trait]
-/// On New Subscription trait
-///
-/// This trait is optional and it is used to notify the application when a new
-/// subscription is created. This is useful when the application needs to send
-/// the initial state to the subscriber upon subscription
-pub trait OnNewSubscription {
-    /// Index type
-    type Index;
-    /// Subscription event type
-    type Event;
-
-    /// Called when a new subscription is created
-    async fn on_new_subscription(
-        &self,
-        request: &[&Self::Index],
-    ) -> Result<Vec<Self::Event>, String>;
-}
-
 /// Subscription manager
 ///
 /// This object keep track of all subscription listener and it is also
@@ -320,41 +298,6 @@ where
     }
 }
 
-/// Subscription Id wrapper
-///
-/// This is the place to add some sane default (like a max length) to the
-/// subscription ID
-#[derive(Debug, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
-pub struct SubId(String);
-
-impl From<&str> for SubId {
-    fn from(s: &str) -> Self {
-        Self(s.to_string())
-    }
-}
-
-impl From<String> for SubId {
-    fn from(s: String) -> Self {
-        Self(s)
-    }
-}
-
-impl FromStr for SubId {
-    type Err = ();
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        Ok(Self(s.to_string()))
-    }
-}
-
-impl Deref for SubId {
-    type Target = String;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
 #[cfg(test)]
 mod test {
     use tokio::sync::mpsc;

+ 66 - 110
crates/cdk/src/wallet/client.rs

@@ -3,7 +3,9 @@
 use std::fmt::Debug;
 
 use async_trait::async_trait;
-use reqwest::Client;
+use reqwest::{Client, IntoUrl};
+use serde::de::DeserializeOwned;
+use serde::Serialize;
 use tracing::instrument;
 #[cfg(not(target_arch = "wasm32"))]
 use url::Url;
@@ -18,18 +20,6 @@ use crate::nuts::{
     RestoreResponse, SwapRequest, SwapResponse,
 };
 
-macro_rules! convert_http_response {
-    ($type:ty, $data:ident) => {
-        serde_json::from_str::<$type>(&$data).map_err(|err| {
-            tracing::warn!("Http Response error: {}", err);
-            match ErrorResponse::from_json(&$data) {
-                Ok(ok) => <ErrorResponse as Into<Error>>::into(ok),
-                Err(err) => err.into(),
-            }
-        })
-    };
-}
-
 /// Http Client
 #[derive(Debug, Clone)]
 pub struct HttpClient {
@@ -46,6 +36,53 @@ impl HttpClient {
         }
     }
 
+    #[inline]
+    async fn http_get<U: IntoUrl, R: DeserializeOwned>(&self, url: U) -> Result<R, Error> {
+        let response = self
+            .inner
+            .get(url)
+            .send()
+            .await
+            .map_err(|e| Error::HttpError(e.to_string()))?
+            .text()
+            .await
+            .map_err(|e| Error::HttpError(e.to_string()))?;
+
+        serde_json::from_str::<R>(&response).map_err(|err| {
+            tracing::warn!("Http Response error: {}", err);
+            match ErrorResponse::from_json(&response) {
+                Ok(ok) => <ErrorResponse as Into<Error>>::into(ok),
+                Err(err) => err.into(),
+            }
+        })
+    }
+
+    #[inline]
+    async fn http_post<U: IntoUrl, P: Serialize + ?Sized, R: DeserializeOwned>(
+        &self,
+        url: U,
+        payload: &P,
+    ) -> Result<R, Error> {
+        let response = self
+            .inner
+            .post(url)
+            .json(&payload)
+            .send()
+            .await
+            .map_err(|e| Error::HttpError(e.to_string()))?
+            .text()
+            .await
+            .map_err(|e| Error::HttpError(e.to_string()))?;
+
+        serde_json::from_str::<R>(&response).map_err(|err| {
+            tracing::warn!("Http Response error: {}", err);
+            match ErrorResponse::from_json(&response) {
+                Ok(ok) => <ErrorResponse as Into<Error>>::into(ok),
+                Err(err) => err.into(),
+            }
+        })
+    }
+
     #[cfg(not(target_arch = "wasm32"))]
     /// Create new [`HttpClient`] with a proxy for specific TLDs.
     /// Specifying `None` for `host_matcher` will use the proxy for all
@@ -72,7 +109,8 @@ impl HttpClient {
                 None
             }))
             .danger_accept_invalid_certs(accept_invalid_certs) // Allow self-signed certs
-            .build()?;
+            .build()
+            .map_err(|e| Error::HttpError(e.to_string()))?;
 
         Ok(Self {
             inner: client,
@@ -88,9 +126,7 @@ impl MintConnector for HttpClient {
     #[instrument(skip(self), fields(mint_url = %self.mint_url))]
     async fn get_mint_keys(&self) -> Result<Vec<KeySet>, Error> {
         let url = self.mint_url.join_paths(&["v1", "keys"])?;
-        let keys = self.inner.get(url).send().await?.text().await?;
-
-        Ok(convert_http_response!(KeysResponse, keys)?.keysets)
+        Ok(self.http_get::<_, KeysResponse>(url).await?.keysets)
     }
 
     /// Get Keyset Keys [NUT-01]
@@ -99,9 +135,8 @@ impl MintConnector for HttpClient {
         let url = self
             .mint_url
             .join_paths(&["v1", "keys", &keyset_id.to_string()])?;
-        let keys = self.inner.get(url).send().await?.text().await?;
-
-        convert_http_response!(KeysResponse, keys)?
+        self.http_get::<_, KeysResponse>(url)
+            .await?
             .keysets
             .drain(0..1)
             .next()
@@ -112,9 +147,7 @@ impl MintConnector for HttpClient {
     #[instrument(skip(self), fields(mint_url = %self.mint_url))]
     async fn get_mint_keysets(&self) -> Result<KeysetResponse, Error> {
         let url = self.mint_url.join_paths(&["v1", "keysets"])?;
-        let res = self.inner.get(url).send().await?.text().await?;
-
-        convert_http_response!(KeysetResponse, res)
+        self.http_get(url).await
     }
 
     /// Mint Quote [NUT-04]
@@ -126,17 +159,7 @@ impl MintConnector for HttpClient {
         let url = self
             .mint_url
             .join_paths(&["v1", "mint", "quote", "bolt11"])?;
-
-        let res = self
-            .inner
-            .post(url)
-            .json(&request)
-            .send()
-            .await?
-            .text()
-            .await?;
-
-        convert_http_response!(MintQuoteBolt11Response<String>, res)
+        self.http_post(url, &request).await
     }
 
     /// Mint Quote status
@@ -149,9 +172,7 @@ impl MintConnector for HttpClient {
             .mint_url
             .join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?;
 
-        let res = self.inner.get(url).send().await?.text().await?;
-
-        convert_http_response!(MintQuoteBolt11Response<String>, res)
+        self.http_get(url).await
     }
 
     /// Mint Tokens [NUT-04]
@@ -161,17 +182,7 @@ impl MintConnector for HttpClient {
         request: MintBolt11Request<String>,
     ) -> Result<MintBolt11Response, Error> {
         let url = self.mint_url.join_paths(&["v1", "mint", "bolt11"])?;
-
-        let res = self
-            .inner
-            .post(url)
-            .json(&request)
-            .send()
-            .await?
-            .text()
-            .await?;
-
-        convert_http_response!(MintBolt11Response, res)
+        self.http_post(url, &request).await
     }
 
     /// Melt Quote [NUT-05]
@@ -183,17 +194,7 @@ impl MintConnector for HttpClient {
         let url = self
             .mint_url
             .join_paths(&["v1", "melt", "quote", "bolt11"])?;
-
-        let res = self
-            .inner
-            .post(url)
-            .json(&request)
-            .send()
-            .await?
-            .text()
-            .await?;
-
-        convert_http_response!(MeltQuoteBolt11Response<String>, res)
+        self.http_post(url, &request).await
     }
 
     /// Melt Quote Status
@@ -206,9 +207,7 @@ impl MintConnector for HttpClient {
             .mint_url
             .join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?;
 
-        let res = self.inner.get(url).send().await?.text().await?;
-
-        convert_http_response!(MeltQuoteBolt11Response<String>, res)
+        self.http_get(url).await
     }
 
     /// Melt [NUT-05]
@@ -219,44 +218,21 @@ impl MintConnector for HttpClient {
         request: MeltBolt11Request<String>,
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
         let url = self.mint_url.join_paths(&["v1", "melt", "bolt11"])?;
-
-        let res = self
-            .inner
-            .post(url)
-            .json(&request)
-            .send()
-            .await?
-            .text()
-            .await?;
-
-        convert_http_response!(MeltQuoteBolt11Response<String>, res)
+        self.http_post(url, &request).await
     }
 
     /// Swap Token [NUT-03]
     #[instrument(skip(self, swap_request), fields(mint_url = %self.mint_url))]
     async fn post_swap(&self, swap_request: SwapRequest) -> Result<SwapResponse, Error> {
         let url = self.mint_url.join_paths(&["v1", "swap"])?;
-
-        let res = self
-            .inner
-            .post(url)
-            .json(&swap_request)
-            .send()
-            .await?
-            .text()
-            .await?;
-
-        convert_http_response!(SwapResponse, res)
+        self.http_post(url, &swap_request).await
     }
 
     /// Get Mint Info [NUT-06]
     #[instrument(skip(self), fields(mint_url = %self.mint_url))]
     async fn get_mint_info(&self) -> Result<MintInfo, Error> {
         let url = self.mint_url.join_paths(&["v1", "info"])?;
-
-        let res = self.inner.get(url).send().await?.text().await?;
-
-        convert_http_response!(MintInfo, res)
+        self.http_get(url).await
     }
 
     /// Spendable check [NUT-07]
@@ -266,34 +242,14 @@ impl MintConnector for HttpClient {
         request: CheckStateRequest,
     ) -> Result<CheckStateResponse, Error> {
         let url = self.mint_url.join_paths(&["v1", "checkstate"])?;
-
-        let res = self
-            .inner
-            .post(url)
-            .json(&request)
-            .send()
-            .await?
-            .text()
-            .await?;
-
-        convert_http_response!(CheckStateResponse, res)
+        self.http_post(url, &request).await
     }
 
     /// Restore request [NUT-13]
     #[instrument(skip(self, request), fields(mint_url = %self.mint_url))]
     async fn post_restore(&self, request: RestoreRequest) -> Result<RestoreResponse, Error> {
         let url = self.mint_url.join_paths(&["v1", "restore"])?;
-
-        let res = self
-            .inner
-            .post(url)
-            .json(&request)
-            .send()
-            .await?
-            .text()
-            .await?;
-
-        convert_http_response!(RestoreResponse, res)
+        self.http_post(url, &request).await
     }
 }
 

+ 7 - 5
crates/cdk/src/wallet/mod.rs

@@ -6,6 +6,8 @@ use std::sync::Arc;
 
 use bitcoin::bip32::Xpriv;
 use bitcoin::Network;
+use cdk_common::database::{self, WalletDatabase};
+use cdk_common::subscription::Params;
 use client::MintConnector;
 use getrandom::getrandom;
 pub use multi_mint_wallet::MultiMintWallet;
@@ -14,13 +16,12 @@ use tracing::instrument;
 pub use types::{MeltQuote, MintQuote, SendKind};
 
 use crate::amount::SplitTarget;
-use crate::cdk_database::{self, WalletDatabase};
 use crate::dhke::construct_proofs;
 use crate::error::Error;
 use crate::fees::calculate_fee;
 use crate::mint_url::MintUrl;
 use crate::nuts::nut00::token::Token;
-use crate::nuts::nut17::{Kind, Params};
+use crate::nuts::nut17::Kind;
 use crate::nuts::{
     nut10, CurrencyUnit, Id, Keys, MintInfo, MintQuoteState, PreMintSecrets, Proof, Proofs,
     RestoreRequest, SpendingConditions, State,
@@ -39,9 +40,10 @@ mod receive;
 mod send;
 pub mod subscription;
 mod swap;
-pub mod types;
 pub mod util;
 
+pub use cdk_common::wallet as types;
+
 use crate::nuts::nut00::ProofsMethods;
 
 /// CDK Wallet
@@ -56,7 +58,7 @@ pub struct Wallet {
     /// Unit
     pub unit: CurrencyUnit,
     /// Storage backend
-    pub localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
+    pub localstore: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
     /// The targeted amount of proofs to have at each size
     pub target_proof_count: usize,
     xpriv: Xpriv,
@@ -132,7 +134,7 @@ impl Wallet {
     pub fn new(
         mint_url: &str,
         unit: CurrencyUnit,
-        localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
+        localstore: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
         seed: &[u8],
         target_proof_count: Option<usize>,
     ) -> Result<Self, Error> {

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

@@ -4,11 +4,10 @@
 //! pairs
 
 use std::collections::{BTreeMap, HashMap};
-use std::fmt;
 use std::str::FromStr;
 use std::sync::Arc;
 
-use serde::{Deserialize, Serialize};
+use cdk_common::wallet::WalletKey;
 use tokio::sync::Mutex;
 use tracing::instrument;
 
@@ -28,26 +27,6 @@ pub struct MultiMintWallet {
     pub wallets: Arc<Mutex<BTreeMap<WalletKey, Wallet>>>,
 }
 
-/// Wallet Key
-#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
-pub struct WalletKey {
-    mint_url: MintUrl,
-    unit: CurrencyUnit,
-}
-
-impl fmt::Display for WalletKey {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "mint_url: {}, unit: {}", self.mint_url, self.unit,)
-    }
-}
-
-impl WalletKey {
-    /// Create new [`WalletKey`]
-    pub fn new(mint_url: MintUrl, unit: CurrencyUnit) -> Self {
-        Self { mint_url, unit }
-    }
-}
-
 impl MultiMintWallet {
     /// New Multimint wallet
     pub fn new(wallets: Vec<Wallet>) -> Self {

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

@@ -9,12 +9,12 @@ use std::collections::HashMap;
 use std::fmt::Debug;
 use std::sync::Arc;
 
+use cdk_common::subscription::Params;
 use tokio::sync::{mpsc, RwLock};
 use tokio::task::JoinHandle;
 use tracing::error;
 
 use crate::mint_url::MintUrl;
-use crate::nuts::nut17::Params;
 use crate::pub_sub::SubId;
 use crate::wallet::client::MintConnector;
 

+ 2 - 4
crates/cdk/src/wallet/subscription/ws.rs

@@ -2,6 +2,8 @@ use std::collections::{HashMap, HashSet};
 use std::sync::atomic::AtomicUsize;
 use std::sync::Arc;
 
+use cdk_common::subscription::Params;
+use cdk_common::ws::{WsMessageOrResponse, WsMethodRequest, WsRequest, WsUnsubscribeRequest};
 use futures::{SinkExt, StreamExt};
 use tokio::sync::{mpsc, RwLock};
 use tokio_tungstenite::connect_async;
@@ -10,10 +12,6 @@ use tokio_tungstenite::tungstenite::Message;
 use super::http::http_main;
 use super::WsSubscriptionBody;
 use crate::mint_url::MintUrl;
-use crate::nuts::nut17::ws::{
-    WsMessageOrResponse, WsMethodRequest, WsRequest, WsUnsubscribeRequest,
-};
-use crate::nuts::nut17::Params;
 use crate::pub_sub::SubId;
 use crate::wallet::client::MintConnector;