Explorar o código

refactor: `nuts`

thesimplekid hai 1 ano
pai
achega
fa07c3632f
Modificáronse 18 ficheiros con 741 adicións e 627 borrados
  1. 0 1
      src/amount.rs
  2. 9 27
      src/cashu_wallet.rs
  3. 15 14
      src/client.rs
  4. 8 5
      src/dhke.rs
  5. 1 1
      src/lib.rs
  6. 32 57
      src/mint.rs
  7. 10 0
      src/nuts/mod.rs
  8. 256 0
      src/nuts/nut00.rs
  9. 111 0
      src/nuts/nut01.rs
  10. 24 103
      src/nuts/nut02.rs
  11. 15 0
      src/nuts/nut03.rs
  12. 27 0
      src/nuts/nut04.rs
  13. 51 0
      src/nuts/nut05.rs
  14. 57 0
      src/nuts/nut06.rs
  15. 20 0
      src/nuts/nut07.rs
  16. 42 0
      src/nuts/nut08.rs
  17. 61 0
      src/nuts/nut09.rs
  18. 2 419
      src/types.rs

+ 0 - 1
src/amount.rs

@@ -13,7 +13,6 @@ impl Amount {
     pub fn split(&self) -> Vec<Self> {
     pub fn split(&self) -> Vec<Self> {
         let sats = self.0.to_sat();
         let sats = self.0.to_sat();
         (0_u64..64)
         (0_u64..64)
-            .into_iter()
             .rev()
             .rev()
             .filter_map(|bit| {
             .filter_map(|bit| {
                 let part = 1 << bit;
                 let part = 1 << bit;

+ 9 - 27
src/cashu_wallet.rs

@@ -1,18 +1,14 @@
 //! Cashu Wallet
 //! Cashu Wallet
 use std::str::FromStr;
 use std::str::FromStr;
 
 
+use crate::nuts::nut00::{mint, BlindedMessages, Proofs, Token};
+use crate::nuts::nut01::Keys;
+use crate::nuts::nut03::RequestMintResponse;
+use crate::nuts::nut06::{SplitPayload, SplitRequest};
+use crate::nuts::nut08::MeltResponse;
+use crate::types::{ProofsStatus, SendProofs};
 pub use crate::Invoice;
 pub use crate::Invoice;
-use crate::{
-    client::Client,
-    dhke::construct_proofs,
-    error::Error,
-    keyset::Keys,
-    mint,
-    types::{
-        BlindedMessages, Melted, Proofs, ProofsStatus, RequestMintResponse, SendProofs,
-        SplitPayload, SplitRequest, Token,
-    },
-};
+use crate::{client::Client, dhke::construct_proofs, error::Error};
 
 
 use crate::amount::Amount;
 use crate::amount::Amount;
 
 
@@ -226,28 +222,14 @@ impl CashuWallet {
         invoice: Invoice,
         invoice: Invoice,
         proofs: Proofs,
         proofs: Proofs,
         fee_reserve: Amount,
         fee_reserve: Amount,
-    ) -> Result<Melted, Error> {
+    ) -> Result<MeltResponse, Error> {
         let change = BlindedMessages::blank(fee_reserve)?;
         let change = BlindedMessages::blank(fee_reserve)?;
         let melt_response = self
         let melt_response = self
             .client
             .client
             .melt(proofs, invoice, Some(change.blinded_messages))
             .melt(proofs, invoice, Some(change.blinded_messages))
             .await?;
             .await?;
 
 
-        let change = match melt_response.change {
-            Some(promises) => Some(construct_proofs(
-                promises,
-                change.rs,
-                change.secrets,
-                &self.mint_keys,
-            )?),
-            None => None,
-        };
-
-        Ok(Melted {
-            paid: melt_response.paid,
-            preimage: melt_response.preimage,
-            change,
-        })
+        Ok(melt_response)
     }
     }
 
 
     pub fn proofs_to_token(&self, proofs: Proofs, memo: Option<String>) -> Result<String, Error> {
     pub fn proofs_to_token(&self, proofs: Proofs, memo: Option<String>) -> Result<String, Error> {

+ 15 - 14
src/client.rs

@@ -1,22 +1,23 @@
 //! Client to connet to mint
 //! Client to connet to mint
 use std::fmt;
 use std::fmt;
 
 
+use serde::{Deserialize, Serialize};
 use serde_json::Value;
 use serde_json::Value;
 use url::Url;
 use url::Url;
 
 
 use crate::amount::Amount;
 use crate::amount::Amount;
+use crate::nuts::nut00::{BlindedMessage, BlindedMessages, Proof};
+use crate::nuts::nut01::Keys;
+use crate::nuts::nut03::RequestMintResponse;
+use crate::nuts::nut04::{MintRequest, PostMintResponse};
+use crate::nuts::nut05::{CheckFeesRequest, CheckFeesResponse};
+use crate::nuts::nut06::{SplitRequest, SplitResponse};
+use crate::nuts::nut07::{CheckSpendableRequest, CheckSpendableResponse};
+use crate::nuts::nut08::{MeltRequest, MeltResponse};
+use crate::nuts::nut09::MintInfo;
+use crate::nuts::*;
+use crate::utils;
 pub use crate::Invoice;
 pub use crate::Invoice;
-use crate::{
-    keyset::{self, Keys},
-    mint,
-    types::{
-        BlindedMessage, BlindedMessages, CheckFeesRequest, CheckFeesResponse,
-        CheckSpendableRequest, CheckSpendableResponse, MeltRequest, MeltResponse, MintInfo,
-        MintRequest, PostMintResponse, Proof, RequestMintResponse, SplitRequest, SplitResponse,
-    },
-    utils,
-};
-use serde::{Deserialize, Serialize};
 
 
 #[derive(Debug)]
 #[derive(Debug)]
 pub enum Error {
 pub enum Error {
@@ -140,11 +141,11 @@ impl Client {
     }
     }
 
 
     /// Get Keysets [NUT-02]
     /// Get Keysets [NUT-02]
-    pub async fn get_keysets(&self) -> Result<keyset::Response, Error> {
+    pub async fn get_keysets(&self) -> Result<nut02::Response, Error> {
         let url = self.mint_url.join("keysets")?;
         let url = self.mint_url.join("keysets")?;
         let res = minreq::get(url).send()?.json::<Value>()?;
         let res = minreq::get(url).send()?.json::<Value>()?;
 
 
-        let response: Result<keyset::Response, serde_json::Error> =
+        let response: Result<nut02::Response, serde_json::Error> =
             serde_json::from_value(res.clone());
             serde_json::from_value(res.clone());
 
 
         match response {
         match response {
@@ -268,7 +269,7 @@ impl Client {
     /// Spendable check [NUT-07]
     /// Spendable check [NUT-07]
     pub async fn check_spendable(
     pub async fn check_spendable(
         &self,
         &self,
-        proofs: &Vec<mint::Proof>,
+        proofs: &Vec<nut00::mint::Proof>,
     ) -> Result<CheckSpendableResponse, Error> {
     ) -> Result<CheckSpendableResponse, Error> {
         let url = self.mint_url.join("check")?;
         let url = self.mint_url.join("check")?;
         let request = CheckSpendableRequest {
         let request = CheckSpendableRequest {

+ 8 - 5
src/dhke.rs

@@ -7,9 +7,12 @@ use bitcoin_hashes::Hash;
 use k256::{ProjectivePoint, Scalar, SecretKey};
 use k256::{ProjectivePoint, Scalar, SecretKey};
 
 
 use crate::error::Error;
 use crate::error::Error;
-use crate::keyset;
-use crate::keyset::{Keys, PublicKey};
-use crate::types::{Promise, Proof, Proofs};
+use crate::nuts::nut00::BlindedSignature;
+use crate::nuts::nut00::Proof;
+use crate::nuts::nut00::Proofs;
+use crate::nuts::nut01::Keys;
+use crate::nuts::nut01::PublicKey;
+use crate::nuts::*;
 
 
 fn hash_to_curve(message: &[u8]) -> k256::PublicKey {
 fn hash_to_curve(message: &[u8]) -> k256::PublicKey {
     let mut msg_to_hash = message.to_vec();
     let mut msg_to_hash = message.to_vec();
@@ -66,8 +69,8 @@ pub fn unblind_message(
 
 
 /// Construct Proof
 /// Construct Proof
 pub fn construct_proofs(
 pub fn construct_proofs(
-    promises: Vec<Promise>,
-    rs: Vec<keyset::SecretKey>,
+    promises: Vec<BlindedSignature>,
+    rs: Vec<nut01::SecretKey>,
     secrets: Vec<String>,
     secrets: Vec<String>,
     keys: &Keys,
     keys: &Keys,
 ) -> Result<Proofs, Error> {
 ) -> Result<Proofs, Error> {

+ 1 - 1
src/lib.rs

@@ -3,8 +3,8 @@ pub mod cashu_wallet;
 pub mod client;
 pub mod client;
 pub mod dhke;
 pub mod dhke;
 pub mod error;
 pub mod error;
-pub mod keyset;
 pub mod mint;
 pub mod mint;
+pub mod nuts;
 pub mod serde_utils;
 pub mod serde_utils;
 pub mod types;
 pub mod types;
 pub mod utils;
 pub mod utils;

+ 32 - 57
src/mint.rs

@@ -1,27 +1,23 @@
 use std::collections::{HashMap, HashSet};
 use std::collections::{HashMap, HashSet};
 
 
-use serde::{Deserialize, Serialize};
-
+use crate::dhke::sign_message;
 use crate::dhke::verify_message;
 use crate::dhke::verify_message;
 use crate::error::Error;
 use crate::error::Error;
-use crate::types::{
-    self, BlindedMessage, CheckSpendableRequest, CheckSpendableResponse, MeltRequest, MeltResponse,
-    PostMintResponse, Promise, SplitRequest, SplitResponse,
-};
+use crate::nuts::nut00::BlindedMessage;
+use crate::nuts::nut00::BlindedSignature;
+use crate::nuts::nut00::Proof;
+use crate::nuts::nut06::SplitRequest;
+use crate::nuts::nut06::SplitResponse;
+use crate::nuts::nut07::CheckSpendableRequest;
+use crate::nuts::nut07::CheckSpendableResponse;
+use crate::nuts::nut08::MeltRequest;
+use crate::nuts::nut08::MeltResponse;
+use crate::nuts::*;
 use crate::Amount;
 use crate::Amount;
-use crate::{
-    dhke::sign_message,
-    keyset::{
-        self,
-        mint::{self, KeySet},
-        PublicKey,
-    },
-    types::MintRequest,
-};
 
 
 pub struct Mint {
 pub struct Mint {
-    pub active_keyset: KeySet,
-    pub inactive_keysets: HashMap<String, mint::KeySet>,
+    pub active_keyset: nut02::mint::KeySet,
+    pub inactive_keysets: HashMap<String, nut02::mint::KeySet>,
     pub spent_secrets: HashSet<String>,
     pub spent_secrets: HashSet<String>,
 }
 }
 
 
@@ -29,12 +25,12 @@ impl Mint {
     pub fn new(
     pub fn new(
         secret: &str,
         secret: &str,
         derivation_path: &str,
         derivation_path: &str,
-        inactive_keysets: HashMap<String, mint::KeySet>,
+        inactive_keysets: HashMap<String, nut02::mint::KeySet>,
         spent_secrets: HashSet<String>,
         spent_secrets: HashSet<String>,
         max_order: u8,
         max_order: u8,
     ) -> Self {
     ) -> Self {
         Self {
         Self {
-            active_keyset: keyset::mint::KeySet::generate(secret, derivation_path, max_order),
+            active_keyset: nut02::mint::KeySet::generate(secret, derivation_path, max_order),
             inactive_keysets,
             inactive_keysets,
             spent_secrets,
             spent_secrets,
         }
         }
@@ -42,23 +38,23 @@ impl Mint {
 
 
     /// Retrieve the public keys of the active keyset for distribution to
     /// Retrieve the public keys of the active keyset for distribution to
     /// wallet clients
     /// wallet clients
-    pub fn active_keyset_pubkeys(&self) -> keyset::KeySet {
-        keyset::KeySet::from(self.active_keyset.clone())
+    pub fn active_keyset_pubkeys(&self) -> nut02::KeySet {
+        nut02::KeySet::from(self.active_keyset.clone())
     }
     }
 
 
     /// Return a list of all supported keysets
     /// Return a list of all supported keysets
-    pub fn keysets(&self) -> keyset::Response {
+    pub fn keysets(&self) -> nut02::Response {
         let mut keysets: HashSet<_> = self.inactive_keysets.keys().cloned().collect();
         let mut keysets: HashSet<_> = self.inactive_keysets.keys().cloned().collect();
         keysets.insert(self.active_keyset.id.clone());
         keysets.insert(self.active_keyset.id.clone());
-        keyset::Response { keysets }
+        nut02::Response { keysets }
     }
     }
 
 
-    pub fn active_keyset(&self) -> keyset::mint::KeySet {
+    pub fn active_keyset(&self) -> nut02::mint::KeySet {
         self.active_keyset.clone()
         self.active_keyset.clone()
     }
     }
 
 
-    pub fn keyset(&self, id: &str) -> Option<keyset::KeySet> {
-        if &self.active_keyset.id == id {
+    pub fn keyset(&self, id: &str) -> Option<nut02::KeySet> {
+        if self.active_keyset.id == id {
             return Some(self.active_keyset.clone().into());
             return Some(self.active_keyset.clone().into());
         }
         }
 
 
@@ -67,20 +63,20 @@ impl Mint {
 
 
     pub fn process_mint_request(
     pub fn process_mint_request(
         &mut self,
         &mut self,
-        mint_request: MintRequest,
-    ) -> Result<PostMintResponse, Error> {
+        mint_request: nut04::MintRequest,
+    ) -> Result<nut04::PostMintResponse, Error> {
         let mut blind_signatures = Vec::with_capacity(mint_request.outputs.len());
         let mut blind_signatures = Vec::with_capacity(mint_request.outputs.len());
 
 
         for blinded_message in mint_request.outputs {
         for blinded_message in mint_request.outputs {
             blind_signatures.push(self.blind_sign(&blinded_message)?);
             blind_signatures.push(self.blind_sign(&blinded_message)?);
         }
         }
 
 
-        Ok(PostMintResponse {
+        Ok(nut04::PostMintResponse {
             promises: blind_signatures,
             promises: blind_signatures,
         })
         })
     }
     }
 
 
-    fn blind_sign(&self, blinded_message: &BlindedMessage) -> Result<Promise, Error> {
+    fn blind_sign(&self, blinded_message: &BlindedMessage) -> Result<BlindedSignature, Error> {
         let BlindedMessage { amount, b } = blinded_message;
         let BlindedMessage { amount, b } = blinded_message;
 
 
         let Some(key_pair) = self.active_keyset.keys.0.get(&amount.to_sat()) else {
         let Some(key_pair) = self.active_keyset.keys.0.get(&amount.to_sat()) else {
@@ -90,8 +86,8 @@ impl Mint {
 
 
         let c = sign_message(key_pair.secret_key.clone(), b.clone().into())?;
         let c = sign_message(key_pair.secret_key.clone(), b.clone().into())?;
 
 
-        Ok(Promise {
-            amount: amount.clone(),
+        Ok(BlindedSignature {
+            amount: *amount,
             c: c.into(),
             c: c.into(),
             id: self.active_keyset.id.clone(),
             id: self.active_keyset.id.clone(),
         })
         })
@@ -111,7 +107,7 @@ impl Mint {
         // in the outputs (blind messages). As we loop, take from those sets,
         // in the outputs (blind messages). As we loop, take from those sets,
         // target amount first.
         // target amount first.
         for output in outputs {
         for output in outputs {
-            let signed = self.blind_sign(&output)?;
+            let signed = self.blind_sign(output)?;
 
 
             // Accumulate outputs into the target (send) list
             // Accumulate outputs into the target (send) list
             if target_total + signed.amount <= amount {
             if target_total + signed.amount <= amount {
@@ -172,7 +168,7 @@ impl Mint {
         Ok(split_response)
         Ok(split_response)
     }
     }
 
 
-    fn verify_proof(&self, proof: &types::Proof) -> Result<String, Error> {
+    fn verify_proof(&self, proof: &Proof) -> Result<String, Error> {
         if self.spent_secrets.contains(&proof.secret) {
         if self.spent_secrets.contains(&proof.secret) {
             return Err(Error::TokenSpent);
             return Err(Error::TokenSpent);
         }
         }
@@ -223,7 +219,7 @@ impl Mint {
 
 
         let mut secrets = Vec::with_capacity(melt_request.proofs.len());
         let mut secrets = Vec::with_capacity(melt_request.proofs.len());
         for proof in &melt_request.proofs {
         for proof in &melt_request.proofs {
-            secrets.push(self.verify_proof(&proof)?);
+            secrets.push(self.verify_proof(proof)?);
         }
         }
 
 
         Ok(())
         Ok(())
@@ -248,7 +244,7 @@ impl Mint {
             for (i, amount) in amounts.iter().enumerate() {
             for (i, amount) in amounts.iter().enumerate() {
                 let mut message = outputs[i].clone();
                 let mut message = outputs[i].clone();
 
 
-                message.amount = amount.clone();
+                message.amount = *amount;
 
 
                 let signature = self.blind_sign(&message)?;
                 let signature = self.blind_sign(&message)?;
                 change.push(signature)
                 change.push(signature)
@@ -262,24 +258,3 @@ impl Mint {
         })
         })
     }
     }
 }
 }
-
-/// Proofs [NUT-00]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct Proof {
-    /// Amount in satoshi
-    pub amount: Option<Amount>,
-    /// Secret message
-    // #[serde(with = "crate::serde_utils::bytes_base64")]
-    pub secret: String,
-    /// Unblinded signature
-    #[serde(rename = "C")]
-    pub c: Option<PublicKey>,
-    /// `Keyset id`
-    pub id: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    /// P2SHScript that specifies the spending condition for this Proof
-    pub script: Option<String>,
-}
-
-/// List of proofs
-pub type Proofs = Vec<Proof>;

+ 10 - 0
src/nuts/mod.rs

@@ -0,0 +1,10 @@
+pub mod nut00;
+pub mod nut01;
+pub mod nut02;
+pub mod nut03;
+pub mod nut04;
+pub mod nut05;
+pub mod nut06;
+pub mod nut07;
+pub mod nut08;
+pub mod nut09;

+ 256 - 0
src/nuts/nut00.rs

@@ -0,0 +1,256 @@
+//! Notation and Models
+// https://github.com/cashubtc/nuts/blob/main/00.md
+
+use std::str::FromStr;
+
+use base64::{engine::general_purpose, Engine as _};
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+use crate::utils::generate_secret;
+use crate::Amount;
+use crate::{dhke::blind_message, error::Error, serde_utils::serde_url, utils::split_amount};
+
+use super::nut01::{self, PublicKey};
+
+/// Blinded Message [NUT-00]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct BlindedMessage {
+    /// Amount in satoshi
+    pub amount: Amount,
+    /// encrypted secret message (B_)
+    #[serde(rename = "B_")]
+    pub b: PublicKey,
+}
+
+/// Blinded Messages [NUT-00]
+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct BlindedMessages {
+    /// Blinded messages
+    pub blinded_messages: Vec<BlindedMessage>,
+    /// Secrets
+    pub secrets: Vec<String>,
+    /// Rs
+    pub rs: Vec<nut01::SecretKey>,
+    /// Amounts
+    pub amounts: Vec<Amount>,
+}
+
+impl BlindedMessages {
+    /// Outputs for speceifed amount with random secret
+    pub fn random(amount: Amount) -> Result<Self, Error> {
+        let mut blinded_messages = BlindedMessages::default();
+
+        for amount in split_amount(amount) {
+            let secret = generate_secret();
+            let (blinded, r) = blind_message(secret.as_bytes(), None)?;
+
+            let blinded_message = BlindedMessage { amount, b: blinded };
+
+            blinded_messages.secrets.push(secret);
+            blinded_messages.blinded_messages.push(blinded_message);
+            blinded_messages.rs.push(r.into());
+            blinded_messages.amounts.push(amount);
+        }
+
+        Ok(blinded_messages)
+    }
+
+    /// Blank Outputs used for NUT-08 change
+    pub fn blank(fee_reserve: Amount) -> Result<Self, Error> {
+        let mut blinded_messages = BlindedMessages::default();
+
+        let fee_reserve = bitcoin::Amount::from_sat(fee_reserve.to_sat());
+
+        let count = (fee_reserve
+            .to_float_in(bitcoin::Denomination::Satoshi)
+            .log2()
+            .ceil() as u64)
+            .max(1);
+
+        for _i in 0..count {
+            let secret = generate_secret();
+            let (blinded, r) = blind_message(secret.as_bytes(), None)?;
+
+            let blinded_message = BlindedMessage {
+                amount: Amount::ZERO,
+                b: blinded,
+            };
+
+            blinded_messages.secrets.push(secret);
+            blinded_messages.blinded_messages.push(blinded_message);
+            blinded_messages.rs.push(r.into());
+            blinded_messages.amounts.push(Amount::ZERO);
+        }
+
+        Ok(blinded_messages)
+    }
+}
+
+/// Promise (BlindedSignature) [NUT-00]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct BlindedSignature {
+    pub id: String,
+    pub amount: Amount,
+    /// blinded signature (C_) on the secret message `B_` of [BlindedMessage]
+    #[serde(rename = "C_")]
+    pub c: PublicKey,
+}
+
+/// Proofs [NUT-00]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct Proof {
+    /// Amount in satoshi
+    pub amount: Amount,
+    /// Secret message
+    // #[serde(with = "crate::serde_utils::bytes_base64")]
+    pub secret: String,
+    /// Unblinded signature
+    #[serde(rename = "C")]
+    pub c: PublicKey,
+    /// `Keyset id`
+    pub id: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    /// P2SHScript that specifies the spending condition for this Proof
+    pub script: Option<String>,
+}
+
+/// List of proofs
+pub type Proofs = Vec<Proof>;
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct MintProofs {
+    #[serde(with = "serde_url")]
+    pub mint: Url,
+    pub proofs: Proofs,
+}
+
+impl MintProofs {
+    fn new(mint_url: Url, proofs: Proofs) -> Self {
+        Self {
+            mint: mint_url,
+            proofs,
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct Token {
+    pub token: Vec<MintProofs>,
+    pub memo: Option<String>,
+}
+
+impl Token {
+    pub fn new(mint_url: Url, proofs: Proofs, memo: Option<String>) -> Self {
+        Self {
+            token: vec![MintProofs::new(mint_url, proofs)],
+            memo,
+        }
+    }
+
+    pub fn token_info(&self) -> (u64, String) {
+        let mut amount = Amount::ZERO;
+
+        for proofs in &self.token {
+            for proof in &proofs.proofs {
+                amount += proof.amount;
+            }
+        }
+
+        (amount.to_sat(), self.token[0].mint.to_string())
+    }
+}
+
+impl FromStr for Token {
+    type Err = Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if !s.starts_with("cashuA") {
+            return Err(Error::UnsupportedToken);
+        }
+
+        let s = s.replace("cashuA", "");
+        let decoded = general_purpose::STANDARD.decode(s)?;
+        let decoded_str = String::from_utf8(decoded)?;
+        println!("decode: {:?}", decoded_str);
+        let token: Token = serde_json::from_str(&decoded_str)?;
+        Ok(token)
+    }
+}
+
+impl Token {
+    pub fn convert_to_string(&self) -> Result<String, Error> {
+        let json_string = serde_json::to_string(self)?;
+        let encoded = general_purpose::STANDARD.encode(json_string);
+        Ok(format!("cashuA{}", encoded))
+    }
+}
+
+pub mod mint {
+    use serde::{Deserialize, Serialize};
+
+    use crate::amount::Amount;
+
+    use super::PublicKey;
+
+    /// Proofs [NUT-00]
+    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+    pub struct Proof {
+        /// Amount in satoshi
+        pub amount: Option<Amount>,
+        /// Secret message
+        // #[serde(with = "crate::serde_utils::bytes_base64")]
+        pub secret: String,
+        /// Unblinded signature
+        #[serde(rename = "C")]
+        pub c: Option<PublicKey>,
+        /// `Keyset id`
+        pub id: Option<String>,
+        #[serde(skip_serializing_if = "Option::is_none")]
+        /// P2SHScript that specifies the spending condition for this Proof
+        pub script: Option<String>,
+    }
+
+    /// List of proofs
+    pub type Proofs = Vec<Proof>;
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_proof_seralize() {
+        let proof = "[{\"id\":\"DSAl9nvvyfva\",\"amount\":2,\"secret\":\"EhpennC9qB3iFlW8FZ_pZw\",\"C\":\"02c020067db727d586bc3183aecf97fcb800c3f4cc4759f69c626c9db5d8f5b5d4\"},{\"id\":\"DSAl9nvvyfva\",\"amount\":8,\"secret\":\"TmS6Cv0YT5PU_5ATVKnukw\",\"C\":\"02ac910bef28cbe5d7325415d5c263026f15f9b967a079ca9779ab6e5c2db133a7\"}]";
+        let proof: Proofs = serde_json::from_str(proof).unwrap();
+
+        assert_eq!(proof[0].clone().id.unwrap(), "DSAl9nvvyfva");
+    }
+
+    #[test]
+    fn test_token_str_round_trip() {
+        let token_str = "cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJpZCI6IkRTQWw5bnZ2eWZ2YSIsImFtb3VudCI6Miwic2VjcmV0IjoiRWhwZW5uQzlxQjNpRmxXOEZaX3BadyIsIkMiOiIwMmMwMjAwNjdkYjcyN2Q1ODZiYzMxODNhZWNmOTdmY2I4MDBjM2Y0Y2M0NzU5ZjY5YzYyNmM5ZGI1ZDhmNWI1ZDQifSx7ImlkIjoiRFNBbDludnZ5ZnZhIiwiYW1vdW50Ijo4LCJzZWNyZXQiOiJUbVM2Q3YwWVQ1UFVfNUFUVktudWt3IiwiQyI6IjAyYWM5MTBiZWYyOGNiZTVkNzMyNTQxNWQ1YzI2MzAyNmYxNWY5Yjk2N2EwNzljYTk3NzlhYjZlNWMyZGIxMzNhNyJ9XX1dLCJtZW1vIjoiVGhhbmt5b3UuIn0=";
+        let token = Token::from_str(token_str).unwrap();
+
+        assert_eq!(
+            token.token[0].mint,
+            Url::from_str("https://8333.space:3338").unwrap()
+        );
+        assert_eq!(token.token[0].proofs[0].clone().id.unwrap(), "DSAl9nvvyfva");
+
+        let encoded = &token.convert_to_string().unwrap();
+
+        let token_data = Token::from_str(encoded).unwrap();
+
+        assert_eq!(token_data, token);
+    }
+
+    #[test]
+    fn test_blank_blinded_messages() {
+        let b = BlindedMessages::blank(Amount::from_sat(1000)).unwrap();
+        assert_eq!(b.blinded_messages.len(), 10);
+
+        let b = BlindedMessages::blank(Amount::from_sat(1)).unwrap();
+        assert_eq!(b.blinded_messages.len(), 1);
+    }
+}

+ 111 - 0
src/nuts/nut01.rs

@@ -0,0 +1,111 @@
+//! Mint public key exchange
+// https://github.com/cashubtc/nuts/blob/main/01.md
+
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(transparent)]
+pub struct PublicKey(#[serde(with = "crate::serde_utils::serde_public_key")] k256::PublicKey);
+
+impl From<PublicKey> for k256::PublicKey {
+    fn from(value: PublicKey) -> k256::PublicKey {
+        value.0
+    }
+}
+
+impl From<&PublicKey> for k256::PublicKey {
+    fn from(value: &PublicKey) -> k256::PublicKey {
+        value.0
+    }
+}
+
+impl From<k256::PublicKey> for PublicKey {
+    fn from(value: k256::PublicKey) -> Self {
+        Self(value)
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(transparent)]
+pub struct SecretKey(#[serde(with = "crate::serde_utils::serde_secret_key")] k256::SecretKey);
+
+impl From<SecretKey> for k256::SecretKey {
+    fn from(value: SecretKey) -> k256::SecretKey {
+        value.0
+    }
+}
+
+impl From<k256::SecretKey> for SecretKey {
+    fn from(value: k256::SecretKey) -> Self {
+        Self(value)
+    }
+}
+
+/// Mint Keys [NUT-01]
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+pub struct Keys(BTreeMap<u64, PublicKey>);
+
+impl Keys {
+    pub fn new(keys: BTreeMap<u64, PublicKey>) -> Self {
+        Self(keys)
+    }
+
+    pub fn keys(&self) -> BTreeMap<u64, PublicKey> {
+        self.0.clone()
+    }
+
+    pub fn amount_key(&self, amount: &u64) -> Option<PublicKey> {
+        self.0.get(amount).cloned()
+    }
+
+    pub fn as_hashmap(&self) -> HashMap<u64, String> {
+        self.0
+            .iter()
+            .map(|(k, v)| (k.to_owned(), hex::encode(v.0.to_sec1_bytes())))
+            .collect()
+    }
+}
+
+impl From<mint::Keys> for Keys {
+    fn from(keys: mint::Keys) -> Self {
+        Self(
+            keys.0
+                .iter()
+                .map(|(amount, keypair)| (*amount, keypair.public_key.clone()))
+                .collect(),
+        )
+    }
+}
+
+pub mod mint {
+    use std::collections::BTreeMap;
+
+    use k256::SecretKey;
+    use serde::Deserialize;
+    use serde::Serialize;
+
+    use super::PublicKey;
+    use crate::serde_utils;
+
+    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+    pub struct Keys(pub BTreeMap<u64, KeyPair>);
+
+    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+    pub struct KeyPair {
+        pub public_key: PublicKey,
+        #[serde(with = "serde_utils::serde_secret_key")]
+        pub secret_key: SecretKey,
+    }
+
+    impl KeyPair {
+        pub fn from_secret_key(secret_key: SecretKey) -> Self {
+            Self {
+                public_key: secret_key.public_key().into(),
+                secret_key,
+            }
+        }
+    }
+}

+ 24 - 103
src/keyset.rs → src/nuts/nut02.rs

@@ -1,7 +1,6 @@
-//! Keysets
+//! Keysets and keyset ID
+// https://github.com/cashubtc/nuts/blob/main/02.md
 
 
-use std::collections::BTreeMap;
-use std::collections::HashMap;
 use std::collections::HashSet;
 use std::collections::HashSet;
 
 
 use base64::{engine::general_purpose, Engine as _};
 use base64::{engine::general_purpose, Engine as _};
@@ -9,58 +8,31 @@ use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::hashes::Hash;
 use bitcoin::hashes::Hash;
 use serde::{Deserialize, Serialize};
 use serde::{Deserialize, Serialize};
 
 
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-#[serde(transparent)]
-pub struct PublicKey(#[serde(with = "crate::serde_utils::serde_public_key")] k256::PublicKey);
-
-impl From<PublicKey> for k256::PublicKey {
-    fn from(value: PublicKey) -> k256::PublicKey {
-        value.0
-    }
-}
-
-impl From<k256::PublicKey> for PublicKey {
-    fn from(value: k256::PublicKey) -> Self {
-        Self(value)
-    }
-}
+use super::nut01::Keys;
 
 
+/// Mint Keysets [NUT-02]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-#[serde(transparent)]
-pub struct SecretKey(#[serde(with = "crate::serde_utils::serde_secret_key")] k256::SecretKey);
+pub struct Response {
+    /// set of public keys that the mint generates
+    pub keysets: HashSet<String>,
+}
 
 
-impl From<SecretKey> for k256::SecretKey {
-    fn from(value: SecretKey) -> k256::SecretKey {
-        value.0
-    }
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+pub struct KeySet {
+    pub id: String,
+    pub keys: Keys,
 }
 }
 
 
-impl From<k256::SecretKey> for SecretKey {
-    fn from(value: k256::SecretKey) -> Self {
-        Self(value)
+impl From<mint::KeySet> for KeySet {
+    fn from(keyset: mint::KeySet) -> Self {
+        Self {
+            id: keyset.id,
+            keys: Keys::from(keyset.keys),
+        }
     }
     }
 }
 }
 
 
-/// Mint Keys [NUT-01]
-#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
-pub struct Keys(BTreeMap<u64, PublicKey>);
-
 impl Keys {
 impl Keys {
-    pub fn new(keys: BTreeMap<u64, PublicKey>) -> Self {
-        Self(keys)
-    }
-
-    pub fn amount_key(&self, amount: &u64) -> Option<PublicKey> {
-        self.0.get(amount).cloned()
-    }
-
-    pub fn as_hashmap(&self) -> HashMap<u64, String> {
-        self.0
-            .iter()
-            .map(|(k, v)| (k.to_owned(), hex::encode(v.0.to_sec1_bytes())))
-            .collect()
-    }
-
     pub fn id(&self) -> String {
     pub fn id(&self) -> String {
         /* 1 - sort keyset by amount
         /* 1 - sort keyset by amount
          * 2 - concatenate all (sorted) public keys to one string
          * 2 - concatenate all (sorted) public keys to one string
@@ -69,9 +41,9 @@ impl Keys {
          */
          */
 
 
         let pubkeys_concat = self
         let pubkeys_concat = self
-            .0
+            .keys()
             .values()
             .values()
-            .map(|pubkey| hex::encode(&pubkey.0.to_sec1_bytes()))
+            .map(|pubkey| hex::encode(k256::PublicKey::from(pubkey).to_sec1_bytes()))
             .collect::<Vec<String>>()
             .collect::<Vec<String>>()
             .join("");
             .join("");
 
 
@@ -81,39 +53,6 @@ impl Keys {
     }
     }
 }
 }
 
 
-/// Mint Keysets [NUT-02]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct Response {
-    /// set of public keys that the mint generates
-    pub keysets: HashSet<String>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
-pub struct KeySet {
-    pub id: String,
-    pub keys: Keys,
-}
-
-impl From<mint::Keys> for Keys {
-    fn from(keys: mint::Keys) -> Self {
-        Self(
-            keys.0
-                .iter()
-                .map(|(amount, keypair)| (*amount, keypair.public_key.clone()))
-                .collect(),
-        )
-    }
-}
-
-impl From<mint::KeySet> for KeySet {
-    fn from(keyset: mint::KeySet) -> Self {
-        Self {
-            id: keyset.id,
-            keys: Keys::from(keyset.keys),
-        }
-    }
-}
-
 pub mod mint {
 pub mod mint {
     use std::collections::BTreeMap;
     use std::collections::BTreeMap;
 
 
@@ -125,11 +64,7 @@ pub mod mint {
     use serde::Deserialize;
     use serde::Deserialize;
     use serde::Serialize;
     use serde::Serialize;
 
 
-    use super::PublicKey;
-    use crate::serde_utils;
-
-    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-    pub struct Keys(pub BTreeMap<u64, KeyPair>);
+    use crate::nuts::nut01::mint::{KeyPair, Keys};
 
 
     #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
     #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
     pub struct KeySet {
     pub struct KeySet {
@@ -184,7 +119,9 @@ pub mod mint {
 
 
             let pubkeys_concat = map
             let pubkeys_concat = map
                 .values()
                 .values()
-                .map(|keypair| hex::encode(&keypair.public_key.0.to_sec1_bytes()))
+                .map(|keypair| {
+                    hex::encode(k256::PublicKey::from(&keypair.public_key).to_sec1_bytes())
+                })
                 .collect::<Vec<String>>()
                 .collect::<Vec<String>>()
                 .join("");
                 .join("");
 
 
@@ -193,22 +130,6 @@ pub mod mint {
             hash[0..12].to_string()
             hash[0..12].to_string()
         }
         }
     }
     }
-
-    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-    pub struct KeyPair {
-        pub public_key: PublicKey,
-        #[serde(with = "serde_utils::serde_secret_key")]
-        pub secret_key: SecretKey,
-    }
-
-    impl KeyPair {
-        fn from_secret_key(secret_key: SecretKey) -> Self {
-            Self {
-                public_key: secret_key.public_key().into(),
-                secret_key,
-            }
-        }
-    }
 }
 }
 
 
 #[cfg(test)]
 #[cfg(test)]

+ 15 - 0
src/nuts/nut03.rs

@@ -0,0 +1,15 @@
+//! Request mint
+// https://github.com/cashubtc/nuts/blob/main/03.md
+
+use serde::{Deserialize, Serialize};
+
+pub use crate::Invoice;
+
+/// Mint request response [NUT-03]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct RequestMintResponse {
+    /// Bolt11 payment request
+    pub pr: Invoice,
+    /// Random hash MUST not be the hash of invoice
+    pub hash: String,
+}

+ 27 - 0
src/nuts/nut04.rs

@@ -0,0 +1,27 @@
+//! Mint Tokens
+// https://github.com/cashubtc/nuts/blob/main/04.md
+use serde::{Deserialize, Serialize};
+
+use super::nut00::{BlindedMessage, BlindedSignature};
+use crate::Amount;
+
+/// Post Mint Request [NUT-04]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct MintRequest {
+    pub outputs: Vec<BlindedMessage>,
+}
+
+impl MintRequest {
+    pub fn total_amount(&self) -> Amount {
+        self.outputs
+            .iter()
+            .map(|BlindedMessage { amount, .. }| *amount)
+            .sum()
+    }
+}
+
+/// Post Mint Response [NUT-04]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct PostMintResponse {
+    pub promises: Vec<BlindedSignature>,
+}

+ 51 - 0
src/nuts/nut05.rs

@@ -0,0 +1,51 @@
+//! Melting Tokens
+// https://github.com/cashubtc/nuts/blob/main/05.md
+
+use serde::{Deserialize, Serialize};
+
+use super::nut00::Proofs;
+use crate::amount::Amount;
+use crate::error::Error;
+use crate::Invoice;
+
+/// Check Fees Response [NUT-05]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct CheckFeesResponse {
+    /// Expected Mac Fee in satoshis    
+    pub fee: Amount,
+}
+
+/// Check Fees request [NUT-05]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct CheckFeesRequest {
+    /// Lighting Invoice
+    pub pr: Invoice,
+}
+
+/// Melt Request [NUT-05]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct MeltRequest {
+    pub proofs: Proofs,
+    /// bollt11
+    pub pr: Invoice,
+}
+
+impl MeltRequest {
+    pub fn proofs_amount(&self) -> Amount {
+        self.proofs.iter().map(|proof| proof.amount).sum()
+    }
+
+    pub fn invoice_amount(&self) -> Result<Amount, Error> {
+        match self.pr.amount_milli_satoshis() {
+            Some(value) => Ok(Amount::from_sat(value)),
+            None => Err(Error::InvoiceAmountUndefined),
+        }
+    }
+}
+
+/// Melt Response [NUT-05]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct MeltResponse {
+    pub paid: bool,
+    pub preimage: Option<String>,
+}

+ 57 - 0
src/nuts/nut06.rs

@@ -0,0 +1,57 @@
+//! Split
+// https://github.com/cashubtc/nuts/blob/main/06.md
+use serde::{Deserialize, Serialize};
+
+use crate::amount::Amount;
+use crate::nuts::nut00::{BlindedMessage, BlindedMessages, Proofs};
+
+use super::nut00::BlindedSignature;
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct SplitPayload {
+    pub keep_blinded_messages: BlindedMessages,
+    pub send_blinded_messages: BlindedMessages,
+    pub split_payload: SplitRequest,
+}
+
+/// Split Request [NUT-06]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct SplitRequest {
+    pub amount: Amount,
+    pub proofs: Proofs,
+    pub outputs: Vec<BlindedMessage>,
+}
+
+impl SplitRequest {
+    pub fn proofs_amount(&self) -> Amount {
+        self.proofs.iter().map(|proof| proof.amount).sum()
+    }
+    pub fn output_amount(&self) -> Amount {
+        self.outputs.iter().map(|proof| proof.amount).sum()
+    }
+}
+
+/// Split Response [NUT-06]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct SplitResponse {
+    /// Promises to keep
+    pub fst: Vec<BlindedSignature>,
+    /// Promises to send
+    pub snd: Vec<BlindedSignature>,
+}
+
+impl SplitResponse {
+    pub fn change_amount(&self) -> Amount {
+        self.fst
+            .iter()
+            .map(|BlindedSignature { amount, .. }| *amount)
+            .sum()
+    }
+
+    pub fn target_amount(&self) -> Amount {
+        self.snd
+            .iter()
+            .map(|BlindedSignature { amount, .. }| *amount)
+            .sum()
+    }
+}

+ 20 - 0
src/nuts/nut07.rs

@@ -0,0 +1,20 @@
+//! Spendable Check
+// https://github.com/cashubtc/nuts/blob/main/07.md
+
+use serde::{Deserialize, Serialize};
+
+use super::nut00::mint;
+
+/// Check spendabale request [NUT-07]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct CheckSpendableRequest {
+    pub proofs: mint::Proofs,
+}
+
+/// Check Spendable Response [NUT-07]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct CheckSpendableResponse {
+    /// booleans indicating whether the provided Proof is still spendable.
+    /// In same order as provided proofs
+    pub spendable: Vec<bool>,
+}

+ 42 - 0
src/nuts/nut08.rs

@@ -0,0 +1,42 @@
+//! Lightning fee return
+// https://github.com/cashubtc/nuts/blob/main/08.md
+
+use lightning_invoice::Invoice;
+use serde::{Deserialize, Serialize};
+
+use crate::{error::Error, Amount};
+
+use super::nut00::{BlindedMessage, BlindedSignature, Proofs};
+
+/// Melt Request [NUT-08]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct MeltRequest {
+    pub proofs: Proofs,
+    /// bollt11
+    pub pr: Invoice,
+    /// Blinded Message that can be used to return change [NUT-08]
+    /// Amount field of blindedMessages `SHOULD` be set to zero
+    pub outputs: Option<Vec<BlindedMessage>>,
+}
+
+impl MeltRequest {
+    pub fn proofs_amount(&self) -> Amount {
+        self.proofs.iter().map(|proof| proof.amount).sum()
+    }
+
+    pub fn invoice_amount(&self) -> Result<Amount, Error> {
+        match self.pr.amount_milli_satoshis() {
+            Some(value) => Ok(Amount::from_sat(value)),
+            None => Err(Error::InvoiceAmountUndefined),
+        }
+    }
+}
+
+/// Melt Response [NUT-08]
+/// Lightning fee return [NUT-08] if change is defined
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct MeltResponse {
+    pub paid: bool,
+    pub preimage: Option<String>,
+    pub change: Option<Vec<BlindedSignature>>,
+}

+ 61 - 0
src/nuts/nut09.rs

@@ -0,0 +1,61 @@
+//! Mint Information
+// https://github.com/cashubtc/nuts/blob/main/09.md
+
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
+
+use super::nut01::PublicKey;
+
+/// Mint Version
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct MintVersion {
+    pub name: String,
+    pub version: String,
+}
+
+impl Serialize for MintVersion {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let combined = format!("{}/{}", self.name, self.version);
+        serializer.serialize_str(&combined)
+    }
+}
+
+impl<'de> Deserialize<'de> for MintVersion {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let combined = String::deserialize(deserializer)?;
+        let parts: Vec<&str> = combined.split('/').collect();
+        if parts.len() != 2 {
+            return Err(serde::de::Error::custom("Invalid input string"));
+        }
+        Ok(MintVersion {
+            name: parts[0].to_string(),
+            version: parts[1].to_string(),
+        })
+    }
+}
+
+/// Mint Info [NIP-09]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct MintInfo {
+    /// name of the mint and should be recognizable
+    pub name: Option<String>,
+    /// hex pubkey of the mint
+    pub pubkey: Option<PublicKey>,
+    /// implementation name and the version running
+    pub version: Option<MintVersion>,
+    /// short description of the mint
+    pub description: Option<String>,
+    /// long description
+    pub description_long: Option<String>,
+    /// contact methods to reach the mint operator
+    pub contact: Vec<Vec<String>>,
+    /// shows which NUTs the mint supports
+    pub nuts: Vec<String>,
+    /// message of the day that the wallet must display to the user
+    pub motd: Option<String>,
+}

+ 2 - 419
src/types.rs

@@ -1,262 +1,8 @@
 //! Types for `cashu-crab`
 //! Types for `cashu-crab`
 
 
-use std::str::FromStr;
+use serde::{Deserialize, Serialize};
 
 
-use base64::{engine::general_purpose, Engine as _};
-use serde::{Deserialize, Deserializer, Serialize, Serializer};
-use url::Url;
-
-use crate::keyset::{self, PublicKey};
-use crate::utils::generate_secret;
-use crate::Amount;
-pub use crate::Invoice;
-use crate::{dhke::blind_message, error::Error, mint, serde_utils::serde_url, utils::split_amount};
-
-/// Blinded Message [NUT-00]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct BlindedMessage {
-    /// Amount in satoshi
-    pub amount: Amount,
-    /// encrypted secret message (B_)
-    #[serde(rename = "B_")]
-    pub b: PublicKey,
-}
-
-/// Blinded Messages [NUT-00]
-#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct BlindedMessages {
-    /// Blinded messages
-    pub blinded_messages: Vec<BlindedMessage>,
-    /// Secrets
-    pub secrets: Vec<String>,
-    /// Rs
-    pub rs: Vec<keyset::SecretKey>,
-    /// Amounts
-    pub amounts: Vec<Amount>,
-}
-
-impl BlindedMessages {
-    /// Outputs for speceifed amount with random secret
-    pub fn random(amount: Amount) -> Result<Self, Error> {
-        let mut blinded_messages = BlindedMessages::default();
-
-        for amount in split_amount(amount) {
-            let secret = generate_secret();
-            let (blinded, r) = blind_message(secret.as_bytes(), None)?;
-
-            let blinded_message = BlindedMessage { amount, b: blinded };
-
-            blinded_messages.secrets.push(secret);
-            blinded_messages.blinded_messages.push(blinded_message);
-            blinded_messages.rs.push(r.into());
-            blinded_messages.amounts.push(amount);
-        }
-
-        Ok(blinded_messages)
-    }
-
-    /// Blank Outputs used for NUT-08 change
-    pub fn blank(fee_reserve: Amount) -> Result<Self, Error> {
-        let mut blinded_messages = BlindedMessages::default();
-
-        let fee_reserve = bitcoin::Amount::from_sat(fee_reserve.to_sat());
-
-        let count = (fee_reserve
-            .to_float_in(bitcoin::Denomination::Satoshi)
-            .log2()
-            .ceil() as u64)
-            .max(1);
-
-        for _i in 0..count {
-            let secret = generate_secret();
-            let (blinded, r) = blind_message(secret.as_bytes(), None)?;
-
-            let blinded_message = BlindedMessage {
-                amount: Amount::ZERO,
-                b: blinded,
-            };
-
-            blinded_messages.secrets.push(secret);
-            blinded_messages.blinded_messages.push(blinded_message);
-            blinded_messages.rs.push(r.into());
-            blinded_messages.amounts.push(Amount::ZERO);
-        }
-
-        Ok(blinded_messages)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct SplitPayload {
-    pub keep_blinded_messages: BlindedMessages,
-    pub send_blinded_messages: BlindedMessages,
-    pub split_payload: SplitRequest,
-}
-
-/// Promise (BlindedSignature) [NUT-00]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct Promise {
-    pub id: String,
-    pub amount: Amount,
-    /// blinded signature (C_) on the secret message `B_` of [BlindedMessage]
-    #[serde(rename = "C_")]
-    pub c: PublicKey,
-}
-
-/// Proofs [NUT-00]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct Proof {
-    /// Amount in satoshi
-    pub amount: Amount,
-    /// Secret message
-    // #[serde(with = "crate::serde_utils::bytes_base64")]
-    pub secret: String,
-    /// Unblinded signature
-    #[serde(rename = "C")]
-    pub c: PublicKey,
-    /// `Keyset id`
-    pub id: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    /// P2SHScript that specifies the spending condition for this Proof
-    pub script: Option<String>,
-}
-
-/// List of proofs
-pub type Proofs = Vec<Proof>;
-
-/// Mint request response [NUT-03]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct RequestMintResponse {
-    /// Bolt11 payment request
-    pub pr: Invoice,
-    /// Random hash MUST not be the hash of invoice
-    pub hash: String,
-}
-
-/// Post Mint Request [NUT-04]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MintRequest {
-    pub outputs: Vec<BlindedMessage>,
-}
-
-impl MintRequest {
-    pub fn total_amount(&self) -> Amount {
-        self.outputs
-            .iter()
-            .map(|BlindedMessage { amount, .. }| *amount)
-            .sum()
-    }
-}
-
-/// Post Mint Response [NUT-05]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct PostMintResponse {
-    pub promises: Vec<Promise>,
-}
-
-/// Check Fees Response [NUT-05]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct CheckFeesResponse {
-    /// Expected Mac Fee in satoshis    
-    pub fee: Amount,
-}
-
-/// Check Fees request [NUT-05]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct CheckFeesRequest {
-    /// Lighting Invoice
-    pub pr: Invoice,
-}
-
-/// Melt Request [NUT-05]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MeltRequest {
-    pub proofs: Proofs,
-    /// bollt11
-    pub pr: Invoice,
-    /// Blinded Message that can be used to return change [NUT-08]
-    /// Amount field of blindedMessages `SHOULD` be set to zero
-    pub outputs: Option<Vec<BlindedMessage>>,
-}
-
-impl MeltRequest {
-    pub fn proofs_amount(&self) -> Amount {
-        self.proofs.iter().map(|proof| proof.amount).sum()
-    }
-
-    pub fn invoice_amount(&self) -> Result<Amount, Error> {
-        match self.pr.amount_milli_satoshis() {
-            Some(value) => Ok(Amount::from_sat(value)),
-            None => Err(Error::InvoiceAmountUndefined),
-        }
-    }
-}
-
-/// Melt Response [NUT-05]
-/// Lightning fee return [NUT-08] if change is defined
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MeltResponse {
-    pub paid: bool,
-    pub preimage: Option<String>,
-    pub change: Option<Vec<Promise>>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct Melted {
-    pub paid: bool,
-    pub preimage: Option<String>,
-    pub change: Option<Proofs>,
-}
-
-/// Split Request [NUT-06]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct SplitRequest {
-    pub amount: Amount,
-    pub proofs: Proofs,
-    pub outputs: Vec<BlindedMessage>,
-}
-
-impl SplitRequest {
-    pub fn proofs_amount(&self) -> Amount {
-        self.proofs.iter().map(|proof| proof.amount).sum()
-    }
-    pub fn output_amount(&self) -> Amount {
-        self.outputs.iter().map(|proof| proof.amount).sum()
-    }
-}
-
-/// Split Response [NUT-06]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct SplitResponse {
-    /// Promises to keep
-    pub fst: Vec<Promise>,
-    /// Promises to send
-    pub snd: Vec<Promise>,
-}
-
-impl SplitResponse {
-    pub fn change_amount(&self) -> Amount {
-        self.fst.iter().map(|Promise { amount, .. }| *amount).sum()
-    }
-
-    pub fn target_amount(&self) -> Amount {
-        self.snd.iter().map(|Promise { amount, .. }| *amount).sum()
-    }
-}
-
-/// Check spendabale request [NUT-07]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct CheckSpendableRequest {
-    pub proofs: mint::Proofs,
-}
-
-/// Check Spendable Response [NUT-07]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct CheckSpendableResponse {
-    /// booleans indicating whether the provided Proof is still spendable.
-    /// In same order as provided proofs
-    pub spendable: Vec<bool>,
-}
+use crate::nuts::nut00::{mint, Proofs};
 
 
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct ProofsStatus {
 pub struct ProofsStatus {
@@ -269,166 +15,3 @@ pub struct SendProofs {
     pub change_proofs: Proofs,
     pub change_proofs: Proofs,
     pub send_proofs: Proofs,
     pub send_proofs: Proofs,
 }
 }
-
-/// Mint Version
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct MintVersion {
-    pub name: String,
-    pub version: String,
-}
-
-impl Serialize for MintVersion {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        let combined = format!("{}/{}", self.name, self.version);
-        serializer.serialize_str(&combined)
-    }
-}
-
-impl<'de> Deserialize<'de> for MintVersion {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        let combined = String::deserialize(deserializer)?;
-        let parts: Vec<&str> = combined.split('/').collect();
-        if parts.len() != 2 {
-            return Err(serde::de::Error::custom("Invalid input string"));
-        }
-        Ok(MintVersion {
-            name: parts[0].to_string(),
-            version: parts[1].to_string(),
-        })
-    }
-}
-
-/// Mint Info [NIP-09]
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MintInfo {
-    /// name of the mint and should be recognizable
-    pub name: Option<String>,
-    /// hex pubkey of the mint
-    pub pubkey: Option<PublicKey>,
-    /// implementation name and the version running
-    pub version: Option<MintVersion>,
-    /// short description of the mint
-    pub description: Option<String>,
-    /// long description
-    pub description_long: Option<String>,
-    /// contact methods to reach the mint operator
-    pub contact: Vec<Vec<String>>,
-    /// shows which NUTs the mint supports
-    pub nuts: Vec<String>,
-    /// message of the day that the wallet must display to the user
-    pub motd: Option<String>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MintProofs {
-    #[serde(with = "serde_url")]
-    pub mint: Url,
-    pub proofs: Proofs,
-}
-
-impl MintProofs {
-    fn new(mint_url: Url, proofs: Proofs) -> Self {
-        Self {
-            mint: mint_url,
-            proofs,
-        }
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct Token {
-    pub token: Vec<MintProofs>,
-    pub memo: Option<String>,
-}
-
-impl Token {
-    pub fn new(mint_url: Url, proofs: Proofs, memo: Option<String>) -> Self {
-        Self {
-            token: vec![MintProofs::new(mint_url, proofs)],
-            memo,
-        }
-    }
-
-    pub fn token_info(&self) -> (u64, String) {
-        let mut amount = Amount::ZERO;
-
-        for proofs in &self.token {
-            for proof in &proofs.proofs {
-                amount += proof.amount;
-            }
-        }
-
-        (amount.to_sat(), self.token[0].mint.to_string())
-    }
-}
-
-impl FromStr for Token {
-    type Err = Error;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        if !s.starts_with("cashuA") {
-            return Err(Error::UnsupportedToken);
-        }
-
-        let s = s.replace("cashuA", "");
-        let decoded = general_purpose::STANDARD.decode(s)?;
-        let decoded_str = String::from_utf8(decoded)?;
-        println!("decode: {:?}", decoded_str);
-        let token: Token = serde_json::from_str(&decoded_str)?;
-        Ok(token)
-    }
-}
-
-impl Token {
-    pub fn convert_to_string(&self) -> Result<String, Error> {
-        let json_string = serde_json::to_string(self)?;
-        let encoded = general_purpose::STANDARD.encode(json_string);
-        Ok(format!("cashuA{}", encoded))
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_proof_seralize() {
-        let proof = "[{\"id\":\"DSAl9nvvyfva\",\"amount\":2,\"secret\":\"EhpennC9qB3iFlW8FZ_pZw\",\"C\":\"02c020067db727d586bc3183aecf97fcb800c3f4cc4759f69c626c9db5d8f5b5d4\"},{\"id\":\"DSAl9nvvyfva\",\"amount\":8,\"secret\":\"TmS6Cv0YT5PU_5ATVKnukw\",\"C\":\"02ac910bef28cbe5d7325415d5c263026f15f9b967a079ca9779ab6e5c2db133a7\"}]";
-        let proof: Proofs = serde_json::from_str(proof).unwrap();
-
-        assert_eq!(proof[0].clone().id.unwrap(), "DSAl9nvvyfva");
-    }
-
-    #[test]
-    fn test_token_str_round_trip() {
-        let token_str = "cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJpZCI6IkRTQWw5bnZ2eWZ2YSIsImFtb3VudCI6Miwic2VjcmV0IjoiRWhwZW5uQzlxQjNpRmxXOEZaX3BadyIsIkMiOiIwMmMwMjAwNjdkYjcyN2Q1ODZiYzMxODNhZWNmOTdmY2I4MDBjM2Y0Y2M0NzU5ZjY5YzYyNmM5ZGI1ZDhmNWI1ZDQifSx7ImlkIjoiRFNBbDludnZ5ZnZhIiwiYW1vdW50Ijo4LCJzZWNyZXQiOiJUbVM2Q3YwWVQ1UFVfNUFUVktudWt3IiwiQyI6IjAyYWM5MTBiZWYyOGNiZTVkNzMyNTQxNWQ1YzI2MzAyNmYxNWY5Yjk2N2EwNzljYTk3NzlhYjZlNWMyZGIxMzNhNyJ9XX1dLCJtZW1vIjoiVGhhbmt5b3UuIn0=";
-        let token = Token::from_str(token_str).unwrap();
-
-        assert_eq!(
-            token.token[0].mint,
-            Url::from_str("https://8333.space:3338").unwrap()
-        );
-        assert_eq!(token.token[0].proofs[0].clone().id.unwrap(), "DSAl9nvvyfva");
-
-        let encoded = &token.convert_to_string().unwrap();
-
-        let token_data = Token::from_str(encoded).unwrap();
-
-        assert_eq!(token_data, token);
-    }
-
-    #[test]
-    fn test_blank_blinded_messages() {
-        let b = BlindedMessages::blank(Amount::from_sat(1000)).unwrap();
-        assert_eq!(b.blinded_messages.len(), 10);
-
-        let b = BlindedMessages::blank(Amount::from_sat(1)).unwrap();
-        assert_eq!(b.blinded_messages.len(), 1);
-    }
-}