From 2994d778c037e076603d94a8d17a87c98e2ddc9e Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sat, 17 Aug 2024 21:28:53 +0200 Subject: [PATCH] chore: wallet doc tests --- crates/cdk/src/amount.rs | 2 +- crates/cdk/src/mint_url.rs | 2 + crates/cdk/src/wallet/README.md | 22 +++++ crates/cdk/src/wallet/mod.rs | 166 ++++++++++++++++++++++++++++++-- 4 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 crates/cdk/src/wallet/README.md diff --git a/crates/cdk/src/amount.rs b/crates/cdk/src/amount.rs index 396ec01d..98a544b0 100644 --- a/crates/cdk/src/amount.rs +++ b/crates/cdk/src/amount.rs @@ -190,7 +190,7 @@ pub enum SplitTarget { None, /// Target amount for wallet to have most proofs that add up to value Value(Amount), - /// Specific amounts to split into **must** equal amount being split + /// Specific amounts to split into **MUST** equal amount being split Values(Vec), } diff --git a/crates/cdk/src/mint_url.rs b/crates/cdk/src/mint_url.rs index 0c25e7e3..e5706510 100644 --- a/crates/cdk/src/mint_url.rs +++ b/crates/cdk/src/mint_url.rs @@ -48,6 +48,7 @@ impl MintUrl { Self(self.to_string().trim_end_matches('/').to_string()) } } + impl<'de> Deserialize<'de> for MintUrl { fn deserialize(deserializer: D) -> Result where @@ -57,6 +58,7 @@ impl<'de> Deserialize<'de> for MintUrl { MintUrl::from_str(&s).map_err(serde::de::Error::custom) } } + impl From for MintUrl where S: Into, diff --git a/crates/cdk/src/wallet/README.md b/crates/cdk/src/wallet/README.md new file mode 100644 index 00000000..49d8114c --- /dev/null +++ b/crates/cdk/src/wallet/README.md @@ -0,0 +1,22 @@ +# CDK Wallet + +The CDK [`Wallet`] is a high level Cashu wallet. The [`Wallet`] is for a single mint and single unit. Multiple [`Wallet`]s can be created to support multi mints and multi units. + + +## Example + +### Create [`Wallet`] +```rust + use std::sync::Arc; + use cdk::cdk_database::WalletMemoryDatabase; + use cdk::nuts::CurrencyUnit; + use cdk::wallet::Wallet; + use rand::Rng; + + let seed = rand::thread_rng().gen::<[u8; 32]>(); + let mint_url = "https://testnut.cashu.space"; + let unit = CurrencyUnit::Sat; + + let localstore = WalletMemoryDatabase::default(); + let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None); +``` diff --git a/crates/cdk/src/wallet/mod.rs b/crates/cdk/src/wallet/mod.rs index 2d297209..b1197ea1 100644 --- a/crates/cdk/src/wallet/mod.rs +++ b/crates/cdk/src/wallet/mod.rs @@ -1,6 +1,4 @@ -//! Cashu Wallet -//! -//! Each wallet is single mint and single unit +#![doc = include_str!("./README.md")] use std::collections::{HashMap, HashSet}; use std::str::FromStr; @@ -39,6 +37,10 @@ pub use multi_mint_wallet::MultiMintWallet; pub use types::{MeltQuote, MintQuote, SendKind}; /// CDK Wallet +/// +/// The CDK [`Wallet`] is a high level cashu wallet. +/// +/// A [`Wallet`] is for a single mint and single unit. #[derive(Debug, Clone)] pub struct Wallet { /// Mint Url @@ -55,6 +57,22 @@ pub struct Wallet { impl Wallet { /// Create new [`Wallet`] + /// # Synopsis + /// ```rust + /// # use std::sync::Arc; + /// + /// # use cdk::cdk_database::WalletMemoryDatabase; + /// # use cdk::nuts::CurrencyUnit; + /// # use cdk::wallet::Wallet; + /// # use rand::Rng; + /// + /// let seed = rand::thread_rng().gen::<[u8; 32]>(); + /// let mint_url = "https://testnut.cashu.space"; + /// let unit = CurrencyUnit::Sat; + /// + /// let localstore = WalletMemoryDatabase::default(); + /// let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None); + /// ``` pub fn new( mint_url: &str, unit: CurrencyUnit, @@ -248,7 +266,7 @@ impl Wallet { Ok(()) } - /// Add mint to wallet + /// Qeury mint for current mint information #[instrument(skip(self))] pub async fn get_mint_info(&self) -> Result, Error> { let mint_info = match self @@ -267,12 +285,15 @@ impl Wallet { .add_mint(self.mint_url.clone(), mint_info.clone()) .await?; - tracing::trace!("Mint info fetched for {}", self.mint_url); + tracing::trace!("Mint info updated for {}", self.mint_url); Ok(mint_info) } /// Get keys for mint keyset + /// + /// Selected keys from localstore if they are already known + /// If they are not known queries mint for keyset id and stores the [`Keys`] #[instrument(skip(self))] pub async fn get_keyset_keys(&self, keyset_id: Id) -> Result { let keys = if let Some(keys) = self.localstore.get_keys(&keyset_id).await? { @@ -292,6 +313,8 @@ impl Wallet { } /// Get keysets for mint + /// + /// Queries mint for all keysets #[instrument(skip(self))] pub async fn get_mint_keysets(&self) -> Result, Error> { let keysets = self @@ -307,7 +330,8 @@ impl Wallet { } /// Get active keyset for mint - /// Quieries mint for current keysets then gets Keys for any unknown keysets + /// + /// Quieries mint for current keysets then gets [`Keys`] for any unknown keysets #[instrument(skip(self))] pub async fn get_active_mint_keyset(&self) -> Result { let keysets = self @@ -352,6 +376,8 @@ impl Wallet { } /// Reclaim unspent proofs + /// + /// Checks the stats of [`Proofs`] swapping for a new [`Proof`] if unspent #[instrument(skip(self, proofs))] pub async fn reclaim_unspent(&self, proofs: Proofs) -> Result<(), Error> { let proof_ys = proofs @@ -378,7 +404,7 @@ impl Wallet { Ok(()) } - /// Check if a proof is spent + /// NUT-07 Check the state of a [`Proof`] with the mint #[instrument(skip(self, proofs))] pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result, Error> { let spendable = self @@ -444,6 +470,30 @@ impl Wallet { } /// Mint Quote + /// # Synopsis + /// ```rust + /// # use std::sync::Arc; + /// + /// # use cdk::amount::Amount; + /// # use cdk::cdk_database::WalletMemoryDatabase; + /// # use cdk::nuts::CurrencyUnit; + /// # use cdk::wallet::Wallet; + /// # use rand::Rng; + /// + /// #[tokio::main] + /// async fn main() -> anyhow::Result<()> { + /// # let seed = rand::thread_rng().gen::<[u8; 32]>(); + /// # let mint_url = "https://testnut.cashu.space"; + /// # let unit = CurrencyUnit::Sat; + /// + /// # let localstore = WalletMemoryDatabase::default(); + /// let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None); + /// let amount = Amount::from(100); + /// + /// let quote = wallet.mint_quote(amount).await?; + /// Ok(()) + /// } + /// ``` #[instrument(skip(self))] pub async fn mint_quote(&self, amount: Amount) -> Result { let mint_url = self.mint_url.clone(); @@ -468,7 +518,7 @@ impl Wallet { Ok(quote) } - /// Mint quote status + /// Check mint quote status #[instrument(skip(self, quote_id))] pub async fn mint_quote_state(&self, quote_id: &str) -> Result { let response = self @@ -513,6 +563,35 @@ impl Wallet { } /// Mint + /// # Synopsis + /// ```rust + /// # use std::sync::Arc; + /// + /// # use anyhow::Result; + /// # use cdk::amount::{Amount, SplitTarget}; + /// # use cdk::cdk_database::WalletMemoryDatabase; + /// # use cdk::nuts::CurrencyUnit; + /// # use cdk::wallet::Wallet; + /// # use rand::Rng; + /// + /// #[tokio::main] + /// async fn main() -> Result<()> { + /// # let seed = rand::thread_rng().gen::<[u8; 32]>(); + /// # let mint_url = "https://testnut.cashu.space"; + /// # let unit = CurrencyUnit::Sat; + /// + /// # let localstore = WalletMemoryDatabase::default(); + /// let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None); + /// let amount = Amount::from(100); + /// + /// let quote = wallet.mint_quote(amount).await?; + /// let quote_id = quote.id; + /// // To be called after quote request is paid + /// let amount_minted = wallet.mint("e_id, SplitTarget::default(), None).await?; + /// + /// Ok(()) + /// } + /// ``` #[instrument(skip(self))] pub async fn mint( &self, @@ -1173,6 +1252,29 @@ impl Wallet { } /// Melt Quote + /// # Synopsis + /// ```rust + /// # use std::sync::Arc; + /// + /// # use cdk::cdk_database::WalletMemoryDatabase; + /// # use cdk::nuts::CurrencyUnit; + /// # use cdk::wallet::Wallet; + /// # use rand::Rng; + /// + /// #[tokio::main] + /// async fn main() -> anyhow::Result<()> { + /// # let seed = rand::thread_rng().gen::<[u8; 32]>(); + /// # let mint_url = "https://testnut.cashu.space"; + /// # let unit = CurrencyUnit::Sat; + /// + /// # let localstore = WalletMemoryDatabase::default(); + /// let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None); + /// let bolt11 = "lnbc100n1pnvpufspp5djn8hrq49r8cghwye9kqw752qjncwyfnrprhprpqk43mwcy4yfsqdq5g9kxy7fqd9h8vmmfvdjscqzzsxqyz5vqsp5uhpjt36rj75pl7jq2sshaukzfkt7uulj456s4mh7uy7l6vx7lvxs9qxpqysgqedwz08acmqwtk8g4vkwm2w78suwt2qyzz6jkkwcgrjm3r3hs6fskyhvud4fan3keru7emjm8ygqpcrwtlmhfjfmer3afs5hhwamgr4cqtactdq".to_string(); + /// let quote = wallet.melt_quote(bolt11, None).await?; + /// + /// Ok(()) + /// } + /// ``` #[instrument(skip(self))] pub async fn melt_quote( &self, @@ -1361,6 +1463,31 @@ impl Wallet { } /// Melt + /// # Synopsis + /// ```rust, no_run + /// # use std::sync::Arc; + /// + /// # use cdk::cdk_database::WalletMemoryDatabase; + /// # use cdk::nuts::CurrencyUnit; + /// # use cdk::wallet::Wallet; + /// # use rand::Rng; + /// + /// #[tokio::main] + /// async fn main() -> anyhow::Result<()> { + /// # let seed = rand::thread_rng().gen::<[u8; 32]>(); + /// # let mint_url = "https://testnut.cashu.space"; + /// # let unit = CurrencyUnit::Sat; + /// + /// # let localstore = WalletMemoryDatabase::default(); + /// # let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None); + /// # let bolt11 = "lnbc100n1pnvpufspp5djn8hrq49r8cghwye9kqw752qjncwyfnrprhprpqk43mwcy4yfsqdq5g9kxy7fqd9h8vmmfvdjscqzzsxqyz5vqsp5uhpjt36rj75pl7jq2sshaukzfkt7uulj456s4mh7uy7l6vx7lvxs9qxpqysgqedwz08acmqwtk8g4vkwm2w78suwt2qyzz6jkkwcgrjm3r3hs6fskyhvud4fan3keru7emjm8ygqpcrwtlmhfjfmer3afs5hhwamgr4cqtactdq".to_string(); + /// let quote = wallet.melt_quote(bolt11, None).await?; + /// let quote_id = quote.id; + /// + /// let _ = wallet.melt("e_id).await?; + /// + /// Ok(()) + /// } #[instrument(skip(self))] pub async fn melt(&self, quote_id: &str) -> Result { let quote_info = self.localstore.get_melt_quote(quote_id).await?; @@ -1635,6 +1762,29 @@ impl Wallet { } /// Receive + /// # Synopsis + /// ```rust, no_run + /// # use std::sync::Arc; + /// + /// # use cdk::amount::SplitTarget; + /// # use cdk::cdk_database::WalletMemoryDatabase; + /// # use cdk::nuts::CurrencyUnit; + /// # use cdk::wallet::Wallet; + /// # use rand::Rng; + /// + /// #[tokio::main] + /// async fn main() -> anyhow::Result<()> { + /// # let seed = rand::thread_rng().gen::<[u8; 32]>(); + /// # let mint_url = "https://testnut.cashu.space"; + /// # let unit = CurrencyUnit::Sat; + /// + /// # let localstore = WalletMemoryDatabase::default(); + /// let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None); + /// let token = "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJhbW91bnQiOjEsInNlY3JldCI6ImI0ZjVlNDAxMDJhMzhiYjg3NDNiOTkwMzU5MTU1MGYyZGEzZTQxNWEzMzU0OTUyN2M2MmM5ZDc5MGVmYjM3MDUiLCJDIjoiMDIzYmU1M2U4YzYwNTMwZWVhOWIzOTQzZmRhMWEyY2U3MWM3YjNmMGNmMGRjNmQ4NDZmYTc2NWFhZjc3OWZhODFkIiwiaWQiOiIwMDlhMWYyOTMyNTNlNDFlIn1dLCJtaW50IjoiaHR0cHM6Ly90ZXN0bnV0LmNhc2h1LnNwYWNlIn1dLCJ1bml0Ijoic2F0In0="; + /// let amount_receive = wallet.receive(token, SplitTarget::default(), &[], &[]).await?; + /// Ok(()) + /// } + /// ``` #[instrument(skip_all)] pub async fn receive( &self,