Browse Source

refactor: split into cashu and cashu-sdk

thesimplekid 1 year ago
parent
commit
3aeb6b6ab4

+ 21 - 41
Cargo.toml

@@ -1,47 +1,27 @@
-[package]
-name = "cashu-crab"
-version = "0.4.1-ALPHA"
-edition = "2021"
-authors = ["thesimplekid"]
-license = "BSD-3-Clause"
-readme = "README.md"
-documentation = "https://docs.rs/crate/cashu-crab"
-repository = "https://github.com/thesimplekid/cashu-crab"
-description = "Cashu rust wallet and mint library"
-# exclude = ["integration_test"]
+[workspace]
 
-#[workspace]
-#members = ["bindings"]
+members = [
+    "crates/cashu",
+    "crates/cashu-sdk"
+]
 
-[features]
-default = ["mint", "wallet"]
-mint = []
-wallet = ["minreq"]
+[workspace.package]
+license = "BSD-3-Clause"
+homepage = "https://github.com/thesimplekid/cashu-crab"
+repository = "https://github.com/thesimplekid/cashu-crab.git"
+
+[workspace.metadata]
+authors = ["thesimplekid"]
+edition = "2021"
+description = "cashu-crab"
+readme = "README.md"
+repository = "https://github.com/thesimplekid/cashu-rs-mint"
+license-file = "LICENSE"
+keywords = ["bitcoin", "e-cash", "cashu"]
 
-[dependencies]
-base64 = "0.21.0"
-bitcoin = { version = "0.30.0", features=["serde",  "rand", "no-std"] }
-bitcoin_hashes = "0.12.0"
-hex = "0.4.3"
-k256 = { version = "0.13.1", features=["arithmetic"] }
-lightning-invoice = { version = "0.24.0", features=["serde"] }
-rand = "0.8.5"
-getrandom = { version = "0.2", features = ["js"] }
+[workspace.dependencies]
 serde = { version = "1.0.160", features = ["derive"]}
 serde_json = "1.0.96"
 url = "2.3.1"
-regex = "1.8.4"
-log = "0.4.19"
-
-[workspace.dependencies]
-uniffi = "0.24"
-
-[target.'cfg(target_arch = "wasm32")'.dependencies]
-gloo = { version = "0.9.0", features = ["net"]}
-
-[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
-minreq = { version = "2.7.0", optional = true, features = ["json-using-serde", "https"] }
-
-
-[dev-dependencies]
-tokio = {version = "1.27.0", features = ["rt", "macros"] }
+tracing = "0.1"
+tracing-subscriber = "0.3"

+ 21 - 0
LICENSES/nostr-MIT

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022-2023 Yuki Kishimoto
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 28 - 0
crates/cashu-sdk/Cargo.toml

@@ -0,0 +1,28 @@
+[package]
+name = "cashu-sdk"
+version = "0.1.0"
+edition = "2021"
+authors = ["thesimplekid"]
+homepage.workspace = true
+repository.workspace = true
+license.workspace = true
+
+
+[features]
+default = ["mint", "wallet"]
+mint = []
+# Fix: Should be minreq or gloo
+wallet = ["minreq"]
+
+[dependencies]
+cashu = { path = "../cashu" }
+serde = { workspace = true }
+serde_json = { workspace = true }
+url = { workspace = true }
+tracing = { workspace = true }
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+gloo = { version = "0.9.0", features = ["net"]}
+
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+minreq = { version = "2.7.0", optional = true, features = ["json-using-serde", "https"] }

+ 242 - 15
src/wasm_client.rs → crates/cashu-sdk/src/client/mod.rs

@@ -1,24 +1,27 @@
 //! Client to connet to mint
 use std::fmt;
 
-use gloo::net::http::Request;
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
 use url::Url;
 
-use crate::nuts::nut00::{wallet::BlindedMessages, BlindedMessage, 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;
-use crate::Amount;
-pub use crate::Bolt11Invoice;
+use cashu::nuts::nut00::{wallet::BlindedMessages, BlindedMessage, Proof};
+use cashu::nuts::nut01::Keys;
+use cashu::nuts::nut03::RequestMintResponse;
+use cashu::nuts::nut04::{MintRequest, PostMintResponse};
+use cashu::nuts::nut05::{CheckFeesRequest, CheckFeesResponse};
+use cashu::nuts::nut06::{SplitRequest, SplitResponse};
+use cashu::nuts::nut07::{CheckSpendableRequest, CheckSpendableResponse};
+use cashu::nuts::nut08::{MeltRequest, MeltResponse};
+use cashu::nuts::nut09::MintInfo;
+use cashu::nuts::*;
+use cashu::utils;
+use cashu::Amount;
+
+#[cfg(target_arch = "wasm32")]
+use gloo::net::http::Request;
+
+pub use cashu::Bolt11Invoice;
 
 #[derive(Debug)]
 pub enum Error {
@@ -28,7 +31,10 @@ pub enum Error {
     UrlParseError(url::ParseError),
     /// Serde Json error
     SerdeJsonError(serde_json::Error),
-    /// Gloo error
+    ///  Min req error
+    #[cfg(not(target_arch = "wasm32"))]
+    MinReqError(minreq::Error),
+    #[cfg(target_arch = "wasm32")]
     GlooError(String),
     /// Custom Error
     Custom(String),
@@ -46,6 +52,13 @@ impl From<serde_json::Error> for Error {
     }
 }
 
+#[cfg(not(target_arch = "wasm32"))]
+impl From<minreq::Error> for Error {
+    fn from(err: minreq::Error) -> Error {
+        Error::MinReqError(err)
+    }
+}
+
 impl std::error::Error for Error {}
 
 impl fmt::Display for Error {
@@ -61,6 +74,9 @@ impl fmt::Display for Error {
             }
             Error::UrlParseError(err) => write!(f, "{}", err),
             Error::SerdeJsonError(err) => write!(f, "{}", err),
+            #[cfg(not(target_arch = "wasm32"))]
+            Error::MinReqError(err) => write!(f, "{}", err),
+            #[cfg(target_arch = "wasm32")]
             Error::GlooError(err) => write!(f, "{}", err),
             Error::Custom(message) => write!(f, "{}", message),
         }
@@ -106,6 +122,38 @@ impl Client {
     }
 
     /// Get Mint Keys [NUT-01]
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn get_keys(&self) -> Result<Keys, Error> {
+        let url = self.mint_url.join("keys")?;
+        let keys = minreq::get(url).send()?.json::<Value>()?;
+
+        let keys: Keys = serde_json::from_str(&keys.to_string())?;
+        /*
+                let keys: BTreeMap<u64, String> = match serde_json::from_value(keys.clone()) {
+                    Ok(keys) => keys,
+                    Err(_err) => {
+                        return Err(Error::CustomError(format!(
+                            "url: {}, {}",
+                            url,
+                            serde_json::to_string(&keys)?
+                        )))
+                    }
+                };
+
+                let mint_keys: BTreeMap<u64, PublicKey> = keys
+                    .into_iter()
+                    .filter_map(|(k, v)| {
+                        let key = hex::decode(v).ok()?;
+                        let public_key = PublicKey::from_sec1_bytes(&key).ok()?;
+                        Some((k, public_key))
+                    })
+                    .collect();
+        */
+        Ok(keys)
+    }
+
+    /// Get Mint Keys [NUT-01]
+    #[cfg(target_arch = "wasm32")]
     pub async fn get_keys(&self) -> Result<Keys, Error> {
         let url = self.mint_url.join("keys")?;
         let keys = Request::get(url.as_str())
@@ -142,6 +190,22 @@ impl Client {
     }
 
     /// Get Keysets [NUT-02]
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn get_keysets(&self) -> Result<nut02::Response, Error> {
+        let url = self.mint_url.join("keysets")?;
+        let res = minreq::get(url).send()?.json::<Value>()?;
+
+        let response: Result<nut02::Response, serde_json::Error> =
+            serde_json::from_value(res.clone());
+
+        match response {
+            Ok(res) => Ok(res),
+            Err(_) => Err(Error::from_json(&res.to_string())?),
+        }
+    }
+
+    /// Get Keysets [NUT-02]
+    #[cfg(target_arch = "wasm32")]
     pub async fn get_keysets(&self) -> Result<nut02::Response, Error> {
         let url = self.mint_url.join("keysets")?;
         let res = Request::get(url.as_str())
@@ -162,6 +226,25 @@ impl Client {
     }
 
     /// Request Mint [NUT-03]
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn request_mint(&self, amount: Amount) -> Result<RequestMintResponse, Error> {
+        let mut url = self.mint_url.join("mint")?;
+        url.query_pairs_mut()
+            .append_pair("amount", &amount.to_sat().to_string());
+
+        let res = minreq::get(url).send()?.json::<Value>()?;
+
+        let response: Result<RequestMintResponse, serde_json::Error> =
+            serde_json::from_value(res.clone());
+
+        match response {
+            Ok(res) => Ok(res),
+            Err(_) => Err(Error::from_json(&res.to_string())?),
+        }
+    }
+
+    /// Request Mint [NUT-03]
+    #[cfg(target_arch = "wasm32")]
     pub async fn request_mint(&self, amount: Amount) -> Result<RequestMintResponse, Error> {
         let mut url = self.mint_url.join("mint")?;
         url.query_pairs_mut()
@@ -185,6 +268,35 @@ impl Client {
     }
 
     /// Mint Tokens [NUT-04]
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn mint(
+        &self,
+        blinded_messages: BlindedMessages,
+        hash: &str,
+    ) -> Result<PostMintResponse, Error> {
+        let mut url = self.mint_url.join("mint")?;
+        url.query_pairs_mut().append_pair("hash", hash);
+
+        let request = MintRequest {
+            outputs: blinded_messages.blinded_messages,
+        };
+
+        let res = minreq::post(url)
+            .with_json(&request)?
+            .send()?
+            .json::<Value>()?;
+
+        let response: Result<PostMintResponse, serde_json::Error> =
+            serde_json::from_value(res.clone());
+
+        match response {
+            Ok(res) => Ok(res),
+            Err(_) => Err(Error::from_json(&res.to_string())?),
+        }
+    }
+
+    /// Mint Tokens [NUT-04]
+    #[cfg(target_arch = "wasm32")]
     pub async fn mint(
         &self,
         blinded_messages: BlindedMessages,
@@ -217,6 +329,28 @@ impl Client {
     }
 
     /// Check Max expected fee [NUT-05]
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn check_fees(&self, invoice: Bolt11Invoice) -> Result<CheckFeesResponse, Error> {
+        let url = self.mint_url.join("checkfees")?;
+
+        let request = CheckFeesRequest { pr: invoice };
+
+        let res = minreq::post(url)
+            .with_json(&request)?
+            .send()?
+            .json::<Value>()?;
+
+        let response: Result<CheckFeesResponse, serde_json::Error> =
+            serde_json::from_value(res.clone());
+
+        match response {
+            Ok(res) => Ok(res),
+            Err(_) => Err(Error::from_json(&res.to_string())?),
+        }
+    }
+
+    /// Check Max expected fee [NUT-05]
+    #[cfg(target_arch = "wasm32")]
     pub async fn check_fees(&self, invoice: Bolt11Invoice) -> Result<CheckFeesResponse, Error> {
         let url = self.mint_url.join("checkfees")?;
 
@@ -243,6 +377,38 @@ impl Client {
 
     /// Melt [NUT-05]
     /// [Nut-08] Lightning fee return if outputs defined
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn melt(
+        &self,
+        proofs: Vec<Proof>,
+        invoice: Bolt11Invoice,
+        outputs: Option<Vec<BlindedMessage>>,
+    ) -> Result<MeltResponse, Error> {
+        let url = self.mint_url.join("melt")?;
+
+        let request = MeltRequest {
+            proofs,
+            pr: invoice,
+            outputs,
+        };
+
+        let value = minreq::post(url)
+            .with_json(&request)?
+            .send()?
+            .json::<Value>()?;
+
+        let response: Result<MeltResponse, serde_json::Error> =
+            serde_json::from_value(value.clone());
+
+        match response {
+            Ok(res) => Ok(res),
+            Err(_) => Err(Error::from_json(&value.to_string())?),
+        }
+    }
+
+    /// Melt [NUT-05]
+    /// [Nut-08] Lightning fee return if outputs defined
+    #[cfg(target_arch = "wasm32")]
     pub async fn melt(
         &self,
         proofs: Vec<Proof>,
@@ -277,6 +443,26 @@ impl Client {
     }
 
     /// Split Token [NUT-06]
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn split(&self, split_request: SplitRequest) -> Result<SplitResponse, Error> {
+        let url = self.mint_url.join("split")?;
+
+        let res = minreq::post(url)
+            .with_json(&split_request)?
+            .send()?
+            .json::<Value>()?;
+
+        let response: Result<SplitResponse, serde_json::Error> =
+            serde_json::from_value(res.clone());
+
+        match response {
+            Ok(res) => Ok(res),
+            Err(_) => Err(Error::from_json(&res.to_string())?),
+        }
+    }
+
+    /// Split Token [NUT-06]
+    #[cfg(target_arch = "wasm32")]
     pub async fn split(&self, split_request: SplitRequest) -> Result<SplitResponse, Error> {
         let url = self.mint_url.join("split")?;
 
@@ -300,6 +486,32 @@ impl Client {
     }
 
     /// Spendable check [NUT-07]
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn check_spendable(
+        &self,
+        proofs: &Vec<nut00::mint::Proof>,
+    ) -> Result<CheckSpendableResponse, Error> {
+        let url = self.mint_url.join("check")?;
+        let request = CheckSpendableRequest {
+            proofs: proofs.to_owned(),
+        };
+
+        let res = minreq::post(url)
+            .with_json(&request)?
+            .send()?
+            .json::<Value>()?;
+
+        let response: Result<CheckSpendableResponse, serde_json::Error> =
+            serde_json::from_value(res.clone());
+
+        match response {
+            Ok(res) => Ok(res),
+            Err(_) => Err(Error::from_json(&res.to_string())?),
+        }
+    }
+
+    /// Spendable check [NUT-07]
+    #[cfg(target_arch = "wasm32")]
     pub async fn check_spendable(
         &self,
         proofs: &Vec<nut00::mint::Proof>,
@@ -329,6 +541,21 @@ impl Client {
     }
 
     /// Get Mint Info [NUT-09]
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn get_info(&self) -> Result<MintInfo, Error> {
+        let url = self.mint_url.join("info")?;
+        let res = minreq::get(url).send()?.json::<Value>()?;
+
+        let response: Result<MintInfo, serde_json::Error> = serde_json::from_value(res.clone());
+
+        match response {
+            Ok(res) => Ok(res),
+            Err(_) => Err(Error::from_json(&res.to_string())?),
+        }
+    }
+
+    /// Get Mint Info [NUT-09]
+    #[cfg(target_arch = "wasm32")]
     pub async fn get_info(&self) -> Result<MintInfo, Error> {
         let url = self.mint_url.join("info")?;
         let res = Request::get(url.as_str())

+ 7 - 0
crates/cashu-sdk/src/lib.rs

@@ -0,0 +1,7 @@
+#[cfg(feature = "wallet")]
+pub(crate) mod client;
+
+#[cfg(feature = "mint")]
+pub mod mint;
+#[cfg(feature = "wallet")]
+pub mod wallet;

+ 14 - 14
src/mint.rs → crates/cashu-sdk/src/mint.rs

@@ -1,19 +1,19 @@
 use std::collections::{HashMap, HashSet};
 
-use crate::dhke::sign_message;
-use crate::dhke::verify_message;
-use crate::error::mint::Error;
-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 cashu::dhke::sign_message;
+use cashu::dhke::verify_message;
+use cashu::error::mint::Error;
+use cashu::nuts::nut00::BlindedMessage;
+use cashu::nuts::nut00::BlindedSignature;
+use cashu::nuts::nut00::Proof;
+use cashu::nuts::nut06::SplitRequest;
+use cashu::nuts::nut06::SplitResponse;
+use cashu::nuts::nut07::CheckSpendableRequest;
+use cashu::nuts::nut07::CheckSpendableResponse;
+use cashu::nuts::nut08::MeltRequest;
+use cashu::nuts::nut08::MeltResponse;
+use cashu::nuts::*;
+use cashu::Amount;
 
 pub struct Mint {
     //    pub pubkey: PublicKey,

+ 62 - 52
src/wallet.rs → crates/cashu-sdk/src/wallet.rs

@@ -1,29 +1,57 @@
 //! Cashu Wallet
+use std::error::Error as StdError;
+use std::fmt;
 use std::str::FromStr;
 
-use log::warn;
-
-use crate::dhke::unblind_message;
-use crate::nuts::nut00::{
+use cashu::dhke::construct_proofs;
+use cashu::dhke::unblind_message;
+use cashu::nuts::nut00::{
     mint, wallet::BlindedMessages, wallet::Token, BlindedSignature, Proof, Proofs,
 };
-use crate::nuts::nut01::Keys;
-use crate::nuts::nut03::RequestMintResponse;
-use crate::nuts::nut06::{SplitPayload, SplitRequest};
-use crate::types::{Melted, ProofsStatus, SendProofs};
-use crate::Amount;
-pub use crate::Bolt11Invoice;
-use crate::{
-    dhke::construct_proofs,
-    error::{self, wallet::Error},
-};
-
-#[cfg(target_arch = "wasm32")]
-use crate::wasm_client::Client;
+use cashu::nuts::nut01::Keys;
+use cashu::nuts::nut03::RequestMintResponse;
+use cashu::nuts::nut06::{SplitPayload, SplitRequest};
+use cashu::types::{Melted, ProofsStatus, SendProofs};
+use cashu::Amount;
+pub use cashu::Bolt11Invoice;
+use tracing::warn;
 
-#[cfg(not(target_arch = "wasm32"))]
 use crate::client::Client;
 
+#[derive(Debug)]
+pub enum Error {
+    /// Insufficaint Funds
+    InsufficantFunds,
+    CashuError(cashu::error::wallet::Error),
+    ClientError(crate::client::Error),
+    CustomError(String),
+}
+
+impl StdError for Error {}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Error::InsufficantFunds => write!(f, "Insufficant Funds"),
+            Error::CashuError(err) => write!(f, "{}", err),
+            Error::ClientError(err) => write!(f, "{}", err),
+            Error::CustomError(err) => write!(f, "{}", err),
+        }
+    }
+}
+
+impl From<cashu::error::wallet::Error> for Error {
+    fn from(err: cashu::error::wallet::Error) -> Self {
+        Self::CashuError(err)
+    }
+}
+
+impl From<crate::client::Error> for Error {
+    fn from(err: crate::client::Error) -> Error {
+        Error::ClientError(err)
+    }
+}
+
 #[derive(Clone, Debug)]
 pub struct Wallet {
     pub client: Client,
@@ -43,10 +71,7 @@ impl Wallet {
     // TODO: getter method for keys that if it cant get them try again
 
     /// Check if a proof is spent
-    pub async fn check_proofs_spent(
-        &self,
-        proofs: &mint::Proofs,
-    ) -> Result<ProofsStatus, error::wallet::Error> {
+    pub async fn check_proofs_spent(&self, proofs: &mint::Proofs) -> Result<ProofsStatus, Error> {
         let spendable = self.client.check_spendable(proofs).await?;
 
         // Separate proofs in spent and unspent based on mint response
@@ -62,19 +87,12 @@ impl Wallet {
     }
 
     /// Request Token Mint
-    pub async fn request_mint(
-        &self,
-        amount: Amount,
-    ) -> Result<RequestMintResponse, error::wallet::Error> {
+    pub async fn request_mint(&self, amount: Amount) -> Result<RequestMintResponse, Error> {
         Ok(self.client.request_mint(amount).await?)
     }
 
     /// Mint Token
-    pub async fn mint_token(
-        &self,
-        amount: Amount,
-        hash: &str,
-    ) -> Result<Token, error::wallet::Error> {
+    pub async fn mint_token(&self, amount: Amount, hash: &str) -> Result<Token, Error> {
         let proofs = self.mint(amount, hash).await?;
 
         let token = Token::new(self.client.mint_url.clone(), proofs, None);
@@ -82,7 +100,7 @@ impl Wallet {
     }
 
     /// Mint Proofs
-    pub async fn mint(&self, amount: Amount, hash: &str) -> Result<Proofs, error::wallet::Error> {
+    pub async fn mint(&self, amount: Amount, hash: &str) -> Result<Proofs, Error> {
         let blinded_messages = BlindedMessages::random(amount)?;
 
         let mint_res = self.client.mint(blinded_messages.clone(), hash).await?;
@@ -98,12 +116,12 @@ impl Wallet {
     }
 
     /// Check fee
-    pub async fn check_fee(&self, invoice: Bolt11Invoice) -> Result<Amount, error::wallet::Error> {
+    pub async fn check_fee(&self, invoice: Bolt11Invoice) -> Result<Amount, Error> {
         Ok(self.client.check_fees(invoice).await?.fee)
     }
 
     /// Receive
-    pub async fn receive(&self, encoded_token: &str) -> Result<Proofs, error::wallet::Error> {
+    pub async fn receive(&self, encoded_token: &str) -> Result<Proofs, Error> {
         let token_data = Token::from_str(encoded_token)?;
 
         let mut proofs: Vec<Proofs> = vec![vec![]];
@@ -119,7 +137,7 @@ impl Wallet {
             };
 
             // Sum amount of all proofs
-            let amount: Amount = token.proofs.iter().map(|p| p.amount).sum();
+            let _amount: Amount = token.proofs.iter().map(|p| p.amount).sum();
 
             let split_payload = self.create_split(token.proofs)?;
 
@@ -145,7 +163,7 @@ impl Wallet {
     }
 
     /// Create Split Payload
-    fn create_split(&self, proofs: Proofs) -> Result<SplitPayload, error::wallet::Error> {
+    fn create_split(&self, proofs: Proofs) -> Result<SplitPayload, Error> {
         let value = proofs.iter().map(|p| p.amount).sum();
 
         let blinded_messages = BlindedMessages::random(value)?;
@@ -166,7 +184,7 @@ impl Wallet {
         &self,
         blinded_messages: BlindedMessages,
         promises: Vec<BlindedSignature>,
-    ) -> Result<Proofs, error::wallet::Error> {
+    ) -> Result<Proofs, Error> {
         let BlindedMessages {
             blinded_messages: _,
             secrets,
@@ -201,11 +219,7 @@ impl Wallet {
     }
 
     /// Send
-    pub async fn send(
-        &self,
-        amount: Amount,
-        proofs: Proofs,
-    ) -> Result<SendProofs, error::wallet::Error> {
+    pub async fn send(&self, amount: Amount, proofs: Proofs) -> Result<SendProofs, Error> {
         let mut amount_available = Amount::ZERO;
         let mut send_proofs = SendProofs::default();
 
@@ -221,7 +235,7 @@ impl Wallet {
 
         if amount_available.lt(&amount) {
             println!("Not enough funds");
-            return Err(error::wallet::Error::InsufficantFunds);
+            return Err(Error::InsufficantFunds);
         }
 
         // If amount available is EQUAL to send amount no need to split
@@ -229,7 +243,7 @@ impl Wallet {
             return Ok(send_proofs);
         }
 
-        let amount_to_keep = amount_available - amount;
+        let _amount_to_keep = amount_available - amount;
         let amount_to_send = amount;
 
         let split_payload = self.create_split(send_proofs.send_proofs)?;
@@ -270,7 +284,7 @@ impl Wallet {
         invoice: Bolt11Invoice,
         proofs: Proofs,
         fee_reserve: Amount,
-    ) -> Result<Melted, error::wallet::Error> {
+    ) -> Result<Melted, Error> {
         let blinded = BlindedMessages::blank(fee_reserve)?;
         let melt_response = self
             .client
@@ -296,12 +310,8 @@ impl Wallet {
         Ok(melted)
     }
 
-    pub fn proofs_to_token(
-        &self,
-        proofs: Proofs,
-        memo: Option<String>,
-    ) -> Result<String, error::wallet::Error> {
-        Token::new(self.client.mint_url.clone(), proofs, memo).convert_to_string()
+    pub fn proofs_to_token(&self, proofs: Proofs, memo: Option<String>) -> Result<String, Error> {
+        Ok(Token::new(self.client.mint_url.clone(), proofs, memo).convert_to_string()?)
     }
 }
 
@@ -314,7 +324,7 @@ mod tests {
 
     use crate::client::Client;
     use crate::mint::Mint;
-    use crate::nuts::nut04;
+    use cashu::nuts::nut04;
 
     #[test]
     fn test_wallet() {

+ 35 - 0
crates/cashu/Cargo.toml

@@ -0,0 +1,35 @@
+[package]
+name = "cashu"
+version = "0.4.1-ALPHA"
+edition = "2021"
+authors = ["thesimplekid"]
+readme = "README.md"
+homepage.workspace = true
+repository.workspace = true
+license.workspace = true
+description = "Cashu rust wallet and mint library"
+
+
+[features]
+default = ["mint", "wallet"]
+mint = []
+wallet = []
+
+[dependencies]
+base64 = "0.21.0"
+bitcoin = { version = "0.30.0", features=["serde",  "rand", "no-std"] }
+bitcoin_hashes = "0.12.0"
+hex = "0.4.3"
+k256 = { version = "0.13.1", features=["arithmetic"] }
+lightning-invoice = { version = "0.24.0", features=["serde"] }
+rand = "0.8.5"
+getrandom = { version = "0.2", features = ["js"] }
+serde = { workspace = true }
+serde_json = { workspace = true }
+url = { workspace = true }
+regex = "1.8.4"
+log = "0.4.19"
+
+
+[dev-dependencies]
+tokio = {version = "1.27.0", features = ["rt", "macros"] }

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


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


+ 0 - 19
src/error.rs → crates/cashu/src/error.rs

@@ -80,10 +80,6 @@ pub mod wallet {
 
     #[derive(Debug)]
     pub enum Error {
-        #[cfg(not(target_arch = "wasm32"))]
-        CrabMintError(crate::client::Error),
-        #[cfg(target_arch = "wasm32")]
-        CrabMintError(crate::wasm_client::Error),
         /// Serde Json error
         SerdeJsonError(serde_json::Error),
         /// From elliptic curve
@@ -104,7 +100,6 @@ pub mod wallet {
     impl fmt::Display for Error {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             match self {
-                Error::CrabMintError(err) => write!(f, "{}", err),
                 Error::CustomError(err) => write!(f, "{}", err),
                 Error::InsufficantFunds => write!(f, "Insufficant Funds"),
                 Error::Utf8ParseError(err) => write!(f, "{}", err),
@@ -116,20 +111,6 @@ pub mod wallet {
         }
     }
 
-    #[cfg(not(target_arch = "wasm32"))]
-    impl From<crate::client::Error> for Error {
-        fn from(err: crate::client::Error) -> Error {
-            Error::CrabMintError(err)
-        }
-    }
-
-    #[cfg(target_arch = "wasm32")]
-    impl From<crate::wasm_client::Error> for Error {
-        fn from(err: crate::wasm_client::Error) -> Error {
-            Error::CrabMintError(err)
-        }
-    }
-
     impl From<serde_json::Error> for Error {
         fn from(err: serde_json::Error) -> Error {
             Error::SerdeJsonError(err)

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

@@ -0,0 +1,15 @@
+pub mod amount;
+#[cfg(feature = "wallet")]
+pub mod dhke;
+pub mod error;
+pub mod nuts;
+pub mod serde_utils;
+pub mod types;
+pub mod utils;
+
+pub use amount::Amount;
+pub use bitcoin::hashes::sha256::Hash as Sha256;
+pub use lightning_invoice;
+pub use lightning_invoice::Bolt11Invoice;
+
+pub type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;

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


+ 1 - 1
src/nuts/nut00.rs → crates/cashu/src/nuts/nut00.rs

@@ -249,7 +249,7 @@ mod tests {
     use super::*;
 
     #[test]
-    fn test_proof_seralize() {
+    fn test_proof_serialize() {
         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();
 

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


+ 0 - 0
src/nuts/nut02.rs → crates/cashu/src/nuts/nut02.rs


+ 0 - 0
src/nuts/nut03.rs → crates/cashu/src/nuts/nut03.rs


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


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


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


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


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


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


+ 0 - 0
src/serde_utils.rs → crates/cashu/src/serde_utils.rs


+ 0 - 0
src/types.rs → crates/cashu/src/types.rs


+ 0 - 0
src/utils.rs → crates/cashu/src/utils.rs


+ 0 - 2
justfile

@@ -1,2 +0,0 @@
-test:
-    cargo r -p integration_test

+ 0 - 332
src/client.rs

@@ -1,332 +0,0 @@
-//! Client to connet to mint
-use std::fmt;
-
-use serde::{Deserialize, Serialize};
-use serde_json::Value;
-use url::Url;
-
-use crate::nuts::nut00::{wallet::BlindedMessages, BlindedMessage, 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;
-use crate::Amount;
-pub use crate::Bolt11Invoice;
-
-#[derive(Debug)]
-pub enum Error {
-    InvoiceNotPaid,
-    LightingWalletNotResponding(Option<String>),
-    /// Parse Url Error
-    UrlParseError(url::ParseError),
-    /// Serde Json error
-    SerdeJsonError(serde_json::Error),
-    ///  Min req error
-    MinReqError(minreq::Error),
-    /// Custom Error
-    Custom(String),
-}
-
-impl From<url::ParseError> for Error {
-    fn from(err: url::ParseError) -> Error {
-        Error::UrlParseError(err)
-    }
-}
-
-impl From<serde_json::Error> for Error {
-    fn from(err: serde_json::Error) -> Error {
-        Error::SerdeJsonError(err)
-    }
-}
-
-impl From<minreq::Error> for Error {
-    fn from(err: minreq::Error) -> Error {
-        Error::MinReqError(err)
-    }
-}
-
-impl std::error::Error for Error {}
-
-impl fmt::Display for Error {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Error::InvoiceNotPaid => write!(f, "Invoice not paid"),
-            Error::LightingWalletNotResponding(mint) => {
-                write!(
-                    f,
-                    "Lightning Wallet not responding: {}",
-                    mint.clone().unwrap_or("".to_string())
-                )
-            }
-            Error::UrlParseError(err) => write!(f, "{}", err),
-            Error::SerdeJsonError(err) => write!(f, "{}", err),
-            Error::MinReqError(err) => write!(f, "{}", err),
-            Error::Custom(message) => write!(f, "{}", message),
-        }
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MintErrorResponse {
-    code: u32,
-    error: String,
-}
-
-impl Error {
-    pub fn from_json(json: &str) -> Result<Self, Error> {
-        let mint_res: MintErrorResponse = serde_json::from_str(json)?;
-
-        let mint_error = match mint_res.error {
-            error if error.starts_with("Lightning invoice not paid yet.") => Error::InvoiceNotPaid,
-            error if error.starts_with("Lightning wallet not responding") => {
-                let mint = utils::extract_url_from_error(&error);
-                Error::LightingWalletNotResponding(mint)
-            }
-            error => Error::Custom(error),
-        };
-        Ok(mint_error)
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct Client {
-    pub mint_url: Url,
-}
-
-impl Client {
-    pub fn new(mint_url: &str) -> Result<Self, Error> {
-        // HACK
-        let mut mint_url = String::from(mint_url);
-        if !mint_url.ends_with('/') {
-            mint_url.push('/');
-        }
-        let mint_url = Url::parse(&mint_url)?;
-        Ok(Self { mint_url })
-    }
-
-    /// Get Mint Keys [NUT-01]
-    pub async fn get_keys(&self) -> Result<Keys, Error> {
-        let url = self.mint_url.join("keys")?;
-        let keys = minreq::get(url).send()?.json::<Value>()?;
-
-        let keys: Keys = serde_json::from_str(&keys.to_string())?;
-        /*
-                let keys: BTreeMap<u64, String> = match serde_json::from_value(keys.clone()) {
-                    Ok(keys) => keys,
-                    Err(_err) => {
-                        return Err(Error::CustomError(format!(
-                            "url: {}, {}",
-                            url,
-                            serde_json::to_string(&keys)?
-                        )))
-                    }
-                };
-
-                let mint_keys: BTreeMap<u64, PublicKey> = keys
-                    .into_iter()
-                    .filter_map(|(k, v)| {
-                        let key = hex::decode(v).ok()?;
-                        let public_key = PublicKey::from_sec1_bytes(&key).ok()?;
-                        Some((k, public_key))
-                    })
-                    .collect();
-        */
-        Ok(keys)
-    }
-
-    /// Get Keysets [NUT-02]
-    pub async fn get_keysets(&self) -> Result<nut02::Response, Error> {
-        let url = self.mint_url.join("keysets")?;
-        let res = minreq::get(url).send()?.json::<Value>()?;
-
-        let response: Result<nut02::Response, serde_json::Error> =
-            serde_json::from_value(res.clone());
-
-        match response {
-            Ok(res) => Ok(res),
-            Err(_) => Err(Error::from_json(&res.to_string())?),
-        }
-    }
-
-    /// Request Mint [NUT-03]
-    pub async fn request_mint(&self, amount: Amount) -> Result<RequestMintResponse, Error> {
-        let mut url = self.mint_url.join("mint")?;
-        url.query_pairs_mut()
-            .append_pair("amount", &amount.to_sat().to_string());
-
-        let res = minreq::get(url).send()?.json::<Value>()?;
-
-        let response: Result<RequestMintResponse, serde_json::Error> =
-            serde_json::from_value(res.clone());
-
-        match response {
-            Ok(res) => Ok(res),
-            Err(_) => Err(Error::from_json(&res.to_string())?),
-        }
-    }
-
-    /// Mint Tokens [NUT-04]
-    pub async fn mint(
-        &self,
-        blinded_messages: BlindedMessages,
-        hash: &str,
-    ) -> Result<PostMintResponse, Error> {
-        let mut url = self.mint_url.join("mint")?;
-        url.query_pairs_mut().append_pair("hash", hash);
-
-        let request = MintRequest {
-            outputs: blinded_messages.blinded_messages,
-        };
-
-        let res = minreq::post(url)
-            .with_json(&request)?
-            .send()?
-            .json::<Value>()?;
-
-        let response: Result<PostMintResponse, serde_json::Error> =
-            serde_json::from_value(res.clone());
-
-        match response {
-            Ok(res) => Ok(res),
-            Err(_) => Err(Error::from_json(&res.to_string())?),
-        }
-    }
-
-    /// Check Max expected fee [NUT-05]
-    pub async fn check_fees(&self, invoice: Bolt11Invoice) -> Result<CheckFeesResponse, Error> {
-        let url = self.mint_url.join("checkfees")?;
-
-        let request = CheckFeesRequest { pr: invoice };
-
-        let res = minreq::post(url)
-            .with_json(&request)?
-            .send()?
-            .json::<Value>()?;
-
-        let response: Result<CheckFeesResponse, serde_json::Error> =
-            serde_json::from_value(res.clone());
-
-        match response {
-            Ok(res) => Ok(res),
-            Err(_) => Err(Error::from_json(&res.to_string())?),
-        }
-    }
-
-    /// Melt [NUT-05]
-    /// [Nut-08] Lightning fee return if outputs defined
-    pub async fn melt(
-        &self,
-        proofs: Vec<Proof>,
-        invoice: Bolt11Invoice,
-        outputs: Option<Vec<BlindedMessage>>,
-    ) -> Result<MeltResponse, Error> {
-        let url = self.mint_url.join("melt")?;
-
-        let request = MeltRequest {
-            proofs,
-            pr: invoice,
-            outputs,
-        };
-
-        let value = minreq::post(url)
-            .with_json(&request)?
-            .send()?
-            .json::<Value>()?;
-
-        let response: Result<MeltResponse, serde_json::Error> =
-            serde_json::from_value(value.clone());
-
-        match response {
-            Ok(res) => Ok(res),
-            Err(_) => Err(Error::from_json(&value.to_string())?),
-        }
-    }
-
-    /// Split Token [NUT-06]
-    pub async fn split(&self, split_request: SplitRequest) -> Result<SplitResponse, Error> {
-        let url = self.mint_url.join("split")?;
-
-        let res = minreq::post(url)
-            .with_json(&split_request)?
-            .send()?
-            .json::<Value>()?;
-
-        let response: Result<SplitResponse, serde_json::Error> =
-            serde_json::from_value(res.clone());
-
-        match response {
-            Ok(res) => Ok(res),
-            Err(_) => Err(Error::from_json(&res.to_string())?),
-        }
-    }
-
-    /// Spendable check [NUT-07]
-    pub async fn check_spendable(
-        &self,
-        proofs: &Vec<nut00::mint::Proof>,
-    ) -> Result<CheckSpendableResponse, Error> {
-        let url = self.mint_url.join("check")?;
-        let request = CheckSpendableRequest {
-            proofs: proofs.to_owned(),
-        };
-
-        let res = minreq::post(url)
-            .with_json(&request)?
-            .send()?
-            .json::<Value>()?;
-
-        let response: Result<CheckSpendableResponse, serde_json::Error> =
-            serde_json::from_value(res.clone());
-
-        match response {
-            Ok(res) => Ok(res),
-            Err(_) => Err(Error::from_json(&res.to_string())?),
-        }
-    }
-
-    /// Get Mint Info [NUT-09]
-    pub async fn get_info(&self) -> Result<MintInfo, Error> {
-        let url = self.mint_url.join("info")?;
-        let res = minreq::get(url).send()?.json::<Value>()?;
-
-        let response: Result<MintInfo, serde_json::Error> = serde_json::from_value(res.clone());
-
-        match response {
-            Ok(res) => Ok(res),
-            Err(_) => Err(Error::from_json(&res.to_string())?),
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-
-    use super::*;
-
-    #[test]
-    fn test_decode_error() {
-        let err = r#"{"code":0,"error":"Lightning invoice not paid yet."}"#;
-
-        let error = Error::from_json(err).unwrap();
-
-        match error {
-            Error::InvoiceNotPaid => {}
-            _ => panic!("Wrong error"),
-        }
-
-        let err = r#"{"code": 0, "error": "Lightning wallet not responding: Failed to connect to https://legend.lnbits.com due to: All connection attempts failed"}"#;
-        let error = Error::from_json(err).unwrap();
-        match error {
-            Error::LightingWalletNotResponding(mint) => {
-                assert_eq!(mint, Some("https://legend.lnbits.com".to_string()));
-            }
-            _ => panic!("Wrong error"),
-        }
-    }
-}

+ 0 - 30
src/lib.rs

@@ -1,30 +0,0 @@
-pub mod amount;
-#[cfg(feature = "wallet")]
-pub mod dhke;
-pub mod error;
-#[cfg(feature = "mint")]
-pub mod mint;
-pub mod nuts;
-pub mod serde_utils;
-pub mod types;
-pub mod utils;
-#[cfg(feature = "wallet")]
-pub mod wallet;
-
-#[cfg(all(feature = "wallet", not(target_arch = "wasm32")))]
-pub mod client;
-#[cfg(all(feature = "wallet", target_arch = "wasm32"))]
-pub mod wasm_client;
-
-#[cfg(all(feature = "wallet", target_arch = "wasm32"))]
-pub use wasm_client::Client;
-
-#[cfg(all(feature = "wallet", not(target_arch = "wasm32")))]
-pub use client::Client;
-
-pub use amount::Amount;
-pub use bitcoin::hashes::sha256::Hash as Sha256;
-pub use lightning_invoice;
-pub use lightning_invoice::Bolt11Invoice;
-
-pub type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;