diff --git a/crates/cdk-cli/src/sub_commands/mint_info.rs b/crates/cdk-cli/src/sub_commands/mint_info.rs index 17804f2f..bcf9f5ec 100644 --- a/crates/cdk-cli/src/sub_commands/mint_info.rs +++ b/crates/cdk-cli/src/sub_commands/mint_info.rs @@ -6,7 +6,6 @@ use url::Url; #[derive(Args)] pub struct MintInfoSubcommand { - /// Cashu Token mint_url: MintUrl, } @@ -17,7 +16,7 @@ pub async fn mint_info(proxy: Option, sub_command_args: &MintInfoSubcommand }; let info = client - .get_mint_info(sub_command_args.mint_url.clone().try_into()?) + .get_mint_info(sub_command_args.mint_url.clone()) .await?; println!("{:#?}", info); diff --git a/crates/cdk/src/mint_url.rs b/crates/cdk/src/mint_url.rs index 1232ee39..917d6ad8 100644 --- a/crates/cdk/src/mint_url.rs +++ b/crates/cdk/src/mint_url.rs @@ -6,7 +6,7 @@ use core::fmt; use core::str::FromStr; -use serde::{Deserialize, Deserializer, Serialize}; +use serde::{Deserialize, Serialize}; use thiserror::Error; use url::{ParseError, Url}; @@ -22,7 +22,7 @@ pub enum Error { } /// MintUrl Url -#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct MintUrl(String); impl MintUrl { @@ -60,25 +60,16 @@ impl MintUrl { Ok(formatted_url) } - /// Empty mint url - pub fn empty() -> Self { - Self(String::new()) - } - /// Join onto url pub fn join(&self, path: &str) -> Result { - let url: Url = self.try_into()?; - Ok(url.join(path)?) + Url::parse(&self.0) + .and_then(|url| url.join(path)) + .map_err(Into::into) } -} -impl<'de> Deserialize<'de> for MintUrl { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - MintUrl::from_str(&s).map_err(serde::de::Error::custom) + /// Append path elements onto the URL + pub fn join_paths(&self, path_elements: &[&str]) -> Result { + self.join(&path_elements.join("/")) } } @@ -94,22 +85,6 @@ impl FromStr for MintUrl { } } -impl TryFrom for Url { - type Error = Error; - - fn try_from(mint_url: MintUrl) -> Result { - Ok(Self::parse(&mint_url.0)?) - } -} - -impl TryFrom<&MintUrl> for Url { - type Error = Error; - - fn try_from(mint_url: &MintUrl) -> Result { - Ok(Self::parse(mint_url.0.as_str())?) - } -} - impl fmt::Display for MintUrl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) diff --git a/crates/cdk/src/nuts/nut00/mod.rs b/crates/cdk/src/nuts/nut00/mod.rs index 5b03a185..ea0e395f 100644 --- a/crates/cdk/src/nuts/nut00/mod.rs +++ b/crates/cdk/src/nuts/nut00/mod.rs @@ -66,9 +66,6 @@ pub enum Error { /// Unsupported token #[error("Unsupported payment method")] UnsupportedPaymentMethod, - /// Invalid Url - #[error("Invalid URL")] - InvalidUrl, /// Serde Json error #[error(transparent)] SerdeJsonError(#[from] serde_json::Error), @@ -78,9 +75,6 @@ pub enum Error { /// Base64 error #[error(transparent)] Base64Error(#[from] bitcoin::base64::DecodeError), - /// Parse Url Error - #[error(transparent)] - UrlParseError(#[from] url::ParseError), /// Ciborium error #[error(transparent)] CiboriumError(#[from] ciborium::de::Error), diff --git a/crates/cdk/src/nuts/nut00/token.rs b/crates/cdk/src/nuts/nut00/token.rs index 8fe9e99f..abaa1024 100644 --- a/crates/cdk/src/nuts/nut00/token.rs +++ b/crates/cdk/src/nuts/nut00/token.rs @@ -9,7 +9,6 @@ use std::str::FromStr; use bitcoin::base64::engine::{general_purpose, GeneralPurpose}; use bitcoin::base64::{alphabet, Engine as _}; use serde::{Deserialize, Serialize}; -use url::Url; use super::{Error, Proof, ProofV4, Proofs}; use crate::mint_url::MintUrl; @@ -181,9 +180,6 @@ impl TokenV3 { return Err(Error::ProofsRequired); } - // Check Url is valid - let _: Url = (&mint_url).try_into().map_err(|_| Error::InvalidUrl)?; - Ok(Self { token: vec![TokenV3Token::new(mint_url, proofs)], memo, diff --git a/crates/cdk/src/wallet/client.rs b/crates/cdk/src/wallet/client.rs index c99723a1..d5b413ce 100644 --- a/crates/cdk/src/wallet/client.rs +++ b/crates/cdk/src/wallet/client.rs @@ -7,6 +7,7 @@ use url::Url; use super::Error; use crate::error::ErrorResponse; +use crate::mint_url::MintUrl; use crate::nuts::nut15::Mpp; use crate::nuts::{ BlindedMessage, CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysResponse, @@ -17,24 +18,6 @@ use crate::nuts::{ }; use crate::{Amount, Bolt11Invoice}; -fn join_url(url: Url, paths: &[&str]) -> Result { - let mut url = url; - for path in paths { - if !url.path().ends_with('/') { - url.path_segments_mut() - .map_err(|_| Error::UrlPathSegments)? - .push(path); - } else { - url.path_segments_mut() - .map_err(|_| Error::UrlPathSegments)? - .pop() - .push(path); - } - } - - Ok(url) -} - /// Http Client #[derive(Debug, Clone)] pub struct HttpClient { @@ -87,8 +70,8 @@ impl HttpClient { /// Get Active Mint Keys [NUT-01] #[instrument(skip(self), fields(mint_url = %mint_url))] - pub async fn get_mint_keys(&self, mint_url: Url) -> Result, Error> { - let url = join_url(mint_url, &["v1", "keys"])?; + pub async fn get_mint_keys(&self, mint_url: MintUrl) -> Result, Error> { + let url = mint_url.join_paths(&["v1", "keys"])?; let keys = self.inner.get(url).send().await?.json::().await?; match serde_json::from_value::(keys.clone()) { @@ -99,8 +82,8 @@ impl HttpClient { /// Get Keyset Keys [NUT-01] #[instrument(skip(self), fields(mint_url = %mint_url))] - pub async fn get_mint_keyset(&self, mint_url: Url, keyset_id: Id) -> Result { - let url = join_url(mint_url, &["v1", "keys", &keyset_id.to_string()])?; + pub async fn get_mint_keyset(&self, mint_url: MintUrl, keyset_id: Id) -> Result { + let url = mint_url.join_paths(&["v1", "keys", &keyset_id.to_string()])?; let keys = self.inner.get(url).send().await?.json::().await?; match serde_json::from_value::(keys.clone()) { @@ -111,8 +94,8 @@ impl HttpClient { /// Get Keysets [NUT-02] #[instrument(skip(self), fields(mint_url = %mint_url))] - pub async fn get_mint_keysets(&self, mint_url: Url) -> Result { - let url = join_url(mint_url, &["v1", "keysets"])?; + pub async fn get_mint_keysets(&self, mint_url: MintUrl) -> Result { + let url = mint_url.join_paths(&["v1", "keysets"])?; let res = self.inner.get(url).send().await?.json::().await?; match serde_json::from_value::(res.clone()) { @@ -125,12 +108,12 @@ impl HttpClient { #[instrument(skip(self), fields(mint_url = %mint_url))] pub async fn post_mint_quote( &self, - mint_url: Url, + mint_url: MintUrl, amount: Amount, unit: CurrencyUnit, description: Option, ) -> Result { - let url = join_url(mint_url, &["v1", "mint", "quote", "bolt11"])?; + let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11"])?; let request = MintQuoteBolt11Request { amount, @@ -160,10 +143,10 @@ impl HttpClient { #[instrument(skip(self), fields(mint_url = %mint_url))] pub async fn get_mint_quote_status( &self, - mint_url: Url, + mint_url: MintUrl, quote_id: &str, ) -> Result { - let url = join_url(mint_url, &["v1", "mint", "quote", "bolt11", quote_id])?; + let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?; let res = self.inner.get(url).send().await?.json::().await?; @@ -180,11 +163,11 @@ impl HttpClient { #[instrument(skip(self, quote, premint_secrets), fields(mint_url = %mint_url))] pub async fn post_mint( &self, - mint_url: Url, + mint_url: MintUrl, quote: &str, premint_secrets: PreMintSecrets, ) -> Result { - let url = join_url(mint_url, &["v1", "mint", "bolt11"])?; + let url = mint_url.join_paths(&["v1", "mint", "bolt11"])?; let request = MintBolt11Request { quote: quote.to_string(), @@ -210,12 +193,12 @@ impl HttpClient { #[instrument(skip(self, request), fields(mint_url = %mint_url))] pub async fn post_melt_quote( &self, - mint_url: Url, + mint_url: MintUrl, unit: CurrencyUnit, request: Bolt11Invoice, mpp_amount: Option, ) -> Result { - let url = join_url(mint_url, &["v1", "melt", "quote", "bolt11"])?; + let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11"])?; let options = mpp_amount.map(|amount| Mpp { amount }); @@ -244,10 +227,10 @@ impl HttpClient { #[instrument(skip(self), fields(mint_url = %mint_url))] pub async fn get_melt_quote_status( &self, - mint_url: Url, + mint_url: MintUrl, quote_id: &str, ) -> Result { - let url = join_url(mint_url, &["v1", "melt", "quote", "bolt11", quote_id])?; + let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?; let res = self.inner.get(url).send().await?.json::().await?; @@ -262,12 +245,12 @@ impl HttpClient { #[instrument(skip(self, quote, inputs, outputs), fields(mint_url = %mint_url))] pub async fn post_melt( &self, - mint_url: Url, + mint_url: MintUrl, quote: String, inputs: Vec, outputs: Option>, ) -> Result { - let url = join_url(mint_url, &["v1", "melt", "bolt11"])?; + let url = mint_url.join_paths(&["v1", "melt", "bolt11"])?; let request = MeltBolt11Request { quote, @@ -299,10 +282,10 @@ impl HttpClient { #[instrument(skip(self, swap_request), fields(mint_url = %mint_url))] pub async fn post_swap( &self, - mint_url: Url, + mint_url: MintUrl, swap_request: SwapRequest, ) -> Result { - let url = join_url(mint_url, &["v1", "swap"])?; + let url = mint_url.join_paths(&["v1", "swap"])?; let res = self .inner @@ -321,8 +304,8 @@ impl HttpClient { /// Get Mint Info [NUT-06] #[instrument(skip(self), fields(mint_url = %mint_url))] - pub async fn get_mint_info(&self, mint_url: Url) -> Result { - let url = join_url(mint_url, &["v1", "info"])?; + pub async fn get_mint_info(&self, mint_url: MintUrl) -> Result { + let url = mint_url.join_paths(&["v1", "info"])?; let res = self.inner.get(url).send().await?.json::().await?; @@ -339,10 +322,10 @@ impl HttpClient { #[instrument(skip(self), fields(mint_url = %mint_url))] pub async fn post_check_state( &self, - mint_url: Url, + mint_url: MintUrl, ys: Vec, ) -> Result { - let url = join_url(mint_url, &["v1", "checkstate"])?; + let url = mint_url.join_paths(&["v1", "checkstate"])?; let request = CheckStateRequest { ys }; let res = self @@ -364,10 +347,10 @@ impl HttpClient { #[instrument(skip(self, request), fields(mint_url = %mint_url))] pub async fn post_restore( &self, - mint_url: Url, + mint_url: MintUrl, request: RestoreRequest, ) -> Result { - let url = join_url(mint_url, &["v1", "restore"])?; + let url = mint_url.join_paths(&["v1", "restore"])?; let res = self .inner diff --git a/crates/cdk/src/wallet/keysets.rs b/crates/cdk/src/wallet/keysets.rs index a8c1f27a..77fe39fe 100644 --- a/crates/cdk/src/wallet/keysets.rs +++ b/crates/cdk/src/wallet/keysets.rs @@ -18,7 +18,7 @@ impl Wallet { } else { let keys = self .client - .get_mint_keyset(self.mint_url.clone().try_into()?, keyset_id) + .get_mint_keyset(self.mint_url.clone(), keyset_id) .await?; self.localstore.add_keys(keys.keys.clone()).await?; @@ -34,10 +34,7 @@ impl Wallet { /// Queries mint for all keysets #[instrument(skip(self))] pub async fn get_mint_keysets(&self) -> Result, Error> { - let keysets = self - .client - .get_mint_keysets(self.mint_url.clone().try_into()?) - .await?; + let keysets = self.client.get_mint_keysets(self.mint_url.clone()).await?; self.localstore .add_mint_keysets(self.mint_url.clone(), keysets.keysets.clone()) @@ -52,10 +49,7 @@ impl Wallet { /// keysets #[instrument(skip(self))] pub async fn get_active_mint_keyset(&self) -> Result { - let keysets = self - .client - .get_mint_keysets(self.mint_url.clone().try_into()?) - .await?; + let keysets = self.client.get_mint_keysets(self.mint_url.clone()).await?; let keysets = keysets.keysets; self.localstore diff --git a/crates/cdk/src/wallet/melt.rs b/crates/cdk/src/wallet/melt.rs index 37b3f71e..6d0f709b 100644 --- a/crates/cdk/src/wallet/melt.rs +++ b/crates/cdk/src/wallet/melt.rs @@ -59,7 +59,7 @@ impl Wallet { let quote_res = self .client - .post_melt_quote(self.mint_url.clone().try_into()?, self.unit, invoice, mpp) + .post_melt_quote(self.mint_url.clone(), self.unit, invoice, mpp) .await?; if quote_res.amount != amount { @@ -90,7 +90,7 @@ impl Wallet { ) -> Result { let response = self .client - .get_melt_quote_status(self.mint_url.clone().try_into()?, quote_id) + .get_melt_quote_status(self.mint_url.clone(), quote_id) .await?; match self.localstore.get_melt_quote(quote_id).await? { @@ -149,7 +149,7 @@ impl Wallet { let melt_response = self .client .post_melt( - self.mint_url.clone().try_into()?, + self.mint_url.clone(), quote_id.to_string(), proofs.clone(), Some(premint_secrets.blinded_messages()), diff --git a/crates/cdk/src/wallet/mint.rs b/crates/cdk/src/wallet/mint.rs index 766f40c8..3717edad 100644 --- a/crates/cdk/src/wallet/mint.rs +++ b/crates/cdk/src/wallet/mint.rs @@ -67,7 +67,7 @@ impl Wallet { let quote_res = self .client - .post_mint_quote(mint_url.clone().try_into()?, amount, unit, description) + .post_mint_quote(mint_url.clone(), amount, unit, description) .await?; let quote = MintQuote { @@ -90,7 +90,7 @@ impl Wallet { pub async fn mint_quote_state(&self, quote_id: &str) -> Result { let response = self .client - .get_mint_quote_status(self.mint_url.clone().try_into()?, quote_id) + .get_mint_quote_status(self.mint_url.clone(), quote_id) .await?; match self.localstore.get_mint_quote(quote_id).await? { @@ -215,11 +215,7 @@ impl Wallet { let mint_res = self .client - .post_mint( - self.mint_url.clone().try_into()?, - quote_id, - premint_secrets.clone(), - ) + .post_mint(self.mint_url.clone(), quote_id, premint_secrets.clone()) .await?; let keys = self.get_keyset_keys(active_keyset_id).await?; diff --git a/crates/cdk/src/wallet/mod.rs b/crates/cdk/src/wallet/mod.rs index 174a83a8..0d9e9bec 100644 --- a/crates/cdk/src/wallet/mod.rs +++ b/crates/cdk/src/wallet/mod.rs @@ -161,11 +161,7 @@ impl Wallet { /// Qeury mint for current mint information #[instrument(skip(self))] pub async fn get_mint_info(&self) -> Result, Error> { - let mint_info = match self - .client - .get_mint_info(self.mint_url.clone().try_into()?) - .await - { + let mint_info = match self.client.get_mint_info(self.mint_url.clone()).await { Ok(mint_info) => Some(mint_info), Err(err) => { tracing::warn!("Could not get mint info {}", err); @@ -281,7 +277,7 @@ impl Wallet { let response = self .client - .post_restore(self.mint_url.clone().try_into()?, restore_request) + .post_restore(self.mint_url.clone(), restore_request) .await?; if response.signatures.is_empty() { diff --git a/crates/cdk/src/wallet/proofs.rs b/crates/cdk/src/wallet/proofs.rs index 79764a73..5ea4f53b 100644 --- a/crates/cdk/src/wallet/proofs.rs +++ b/crates/cdk/src/wallet/proofs.rs @@ -77,7 +77,7 @@ impl Wallet { let spendable = self .client - .post_check_state(self.mint_url.clone().try_into()?, proof_ys) + .post_check_state(self.mint_url.clone(), proof_ys) .await? .states; @@ -98,7 +98,7 @@ impl Wallet { pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result, Error> { let spendable = self .client - .post_check_state(self.mint_url.clone().try_into()?, proofs.ys()?) + .post_check_state(self.mint_url.clone(), proofs.ys()?) .await?; let spent_ys: Vec<_> = spendable .states diff --git a/crates/cdk/src/wallet/receive.rs b/crates/cdk/src/wallet/receive.rs index 898eb40c..fe565b30 100644 --- a/crates/cdk/src/wallet/receive.rs +++ b/crates/cdk/src/wallet/receive.rs @@ -34,10 +34,7 @@ impl Wallet { .await? .is_none() { - tracing::debug!( - "Mint not in localstore fetching info for: {}", - self.mint_url - ); + tracing::debug!("Mint not in localstore fetching info for: {mint_url}"); self.get_mint_info().await?; } @@ -134,7 +131,7 @@ impl Wallet { let swap_response = self .client - .post_swap(mint_url.clone().try_into()?, pre_swap.swap_request) + .post_swap(mint_url.clone(), pre_swap.swap_request) .await?; // Proof to keep diff --git a/crates/cdk/src/wallet/swap.rs b/crates/cdk/src/wallet/swap.rs index 82274fb9..043d37c5 100644 --- a/crates/cdk/src/wallet/swap.rs +++ b/crates/cdk/src/wallet/swap.rs @@ -42,7 +42,7 @@ impl Wallet { let swap_response = self .client - .post_swap(mint_url.clone().try_into()?, pre_swap.swap_request) + .post_swap(mint_url.clone(), pre_swap.swap_request) .await?; let active_keyset_id = pre_swap.pre_mint_secrets.keyset_id;