diff --git a/src/cashu_mint.rs b/src/cashu_mint.rs index 264995c9..f26be6ab 100644 --- a/src/cashu_mint.rs +++ b/src/cashu_mint.rs @@ -109,6 +109,7 @@ impl CashuMint { // TODO: need to handle response error // specfically token already spent + println!("{:?}", res); Ok(serde_json::from_value(res).unwrap()) } diff --git a/src/cashu_wallet.rs b/src/cashu_wallet.rs index 3becedfd..d0e137ce 100644 --- a/src/cashu_wallet.rs +++ b/src/cashu_wallet.rs @@ -7,8 +7,8 @@ use crate::{ dhke::construct_proof, error::Error, types::{ - BlindedMessages, MintKeys, Proof, ProofsStatus, RequestMintResponse, SplitPayload, - SplitRequest, TokenData, + BlindedMessages, MintKeys, Proof, ProofsStatus, RequestMintResponse, SendProofs, + SplitPayload, SplitRequest, TokenData, }, }; @@ -102,6 +102,7 @@ impl CashuWallet { Ok(proofs.iter().flatten().cloned().collect()) } + /// Create Split Payload pub async fn create_split( &self, keep_amount: Amount, @@ -128,4 +129,59 @@ impl CashuWallet { split_payload, }) } + + /// Send + pub async fn send(&self, amount: Amount, proofs: Vec) -> Result { + let mut amount_avaliable = Amount::ZERO; + let mut send_proofs = SendProofs::default(); + + for proof in proofs { + amount_avaliable += proof.amount; + if amount_avaliable > amount { + send_proofs.change_proofs.push(proof); + break; + } else { + send_proofs.send_proofs.push(proof); + } + } + + if amount_avaliable.lt(&amount) { + return Err(Error::InsufficantFunds); + } + + // If amount avaliable is EQUAL to send amount no need to split + if amount_avaliable.eq(&amount) { + return Ok(send_proofs); + } + + let amount_to_keep = amount_avaliable - amount; + let amount_to_send = amount; + + let split_payload = self + .create_split(amount_to_keep, amount_to_send, send_proofs.send_proofs) + .await?; + + let split_response = self.mint.split(split_payload.split_payload).await?; + + // Proof to keep + let keep_proofs = construct_proof( + split_response.fst, + split_payload.keep_blinded_messages.rs, + split_payload.keep_blinded_messages.secrets, + &self.keys, + )?; + + // Proofs to send + let send_proofs = construct_proof( + split_response.snd, + split_payload.send_blinded_messages.rs, + split_payload.send_blinded_messages.secrets, + &self.keys, + )?; + + Ok(SendProofs { + change_proofs: keep_proofs, + send_proofs, + }) + } } diff --git a/src/error.rs b/src/error.rs index 2006c5b5..2d6312d3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,4 +23,7 @@ pub enum Error { /// Base64 error #[error("Base64 error: {0}")] Base64Error(#[from] base64::DecodeError), + /// Insufficaint Funds + #[error("Not enough funds")] + InsufficantFunds, } diff --git a/src/types.rs b/src/types.rs index 919c80b4..0a087a41 100644 --- a/src/types.rs +++ b/src/types.rs @@ -147,6 +147,7 @@ pub struct PostMintResponse { } /// Check Fees Response [NUT-05] + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct CheckFeesResponse { /// Expected Mac Fee in satoshis @@ -219,6 +220,12 @@ pub struct ProofsStatus { pub spent: Vec, } +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct SendProofs { + pub change_proofs: Vec, + pub send_proofs: Vec, +} + /// Mint Version #[derive(Debug, Clone, PartialEq, Eq)] pub struct MintVersion { diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 36bb4991..2f847989 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -83,6 +83,23 @@ async fn test_receive() { println!("{:?}", prom); } +// #[ignore] +#[tokio::test] +async fn test_send() { + let url = Url::from_str(MINTURL).unwrap(); + let mint = CashuMint::new(url); + let mint_keys = mint.get_keys().await.unwrap(); + + let wallet = CashuWallet::new(mint, mint_keys); + // FIXME: Have to manully paste an unspent token + let token = "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJpZCI6Im9DV2NkWXJyeVRrUiIsImFtb3VudCI6MSwiQyI6IjAyMjRhMjU5NGY5NWMyMmRiZTA2YjZlN2YzMDNkYTdiZWYwNmM1YzI5YTBjMDU3ZWYyNmNhOWU3ZDVlYzc3MTYzZiIsInNlY3JldCI6IncyL1FpZjZFdlBRYWRtUlYxZzQyTWMrZWVVZ1V3TVZtSC9ndlVlaHhZTXM9In0seyJpZCI6Im9DV2NkWXJyeVRrUiIsImFtb3VudCI6NCwiQyI6IjAyMWEwYTIwYTZmOGEwY2JmMWY2Njc5OTIzNWE5N2U4ZTgxNjkxZWExMTFkMWVjYWJiOWZlZjE5OWRhMzYxNmU0YiIsInNlY3JldCI6InFYazRGbjZKdFBaUnVIRWlFMVVBUDB4MCtEcjd4Y21yNWRwTUVRRldDZ2s9In1dLCJtaW50IjoiaHR0cHM6Ly9sZWdlbmQubG5iaXRzLmNvbS9jYXNodS9hcGkvdjEvU0t2SFJ1czlkbWpXSGhzdEhyc2F6VyJ9XX0="; + + let prom = wallet.receive(token).await.unwrap(); + let send = wallet.send(Amount::from_sat(1), prom).await.unwrap(); + + println!("{:?}", send); +} + #[ignore] #[tokio::test] async fn test_get_mint_info() {