Browse Source

refactor: add mint quote in sdk

thesimplekid 1 year ago
parent
commit
f835ca665d

+ 31 - 1
crates/cashu-sdk/src/client/gloo_client.rs

@@ -7,6 +7,7 @@ use cashu::nuts::{
 };
 #[cfg(feature = "nut07")]
 use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse};
+use cashu::Amount;
 use gloo::net::http::Request;
 use serde_json::Value;
 use url::Url;
@@ -54,6 +55,35 @@ impl Client for HttpClient {
         }
     }
 
+    /// Mint Quote [NUT-04]
+    async fn post_mint_quote(
+        &self,
+        mint_url: Url,
+        amount: Amount,
+        unit: CurrencyUnit,
+    ) -> Result<MintQuoteBolt11Response, Error> {
+        let url = join_url(mint_url, &["v1", "quote", "bolt11"])?;
+
+        let request = MintQuoteBolt11Request { amount, unit };
+        let res = Request::post(url.as_str())
+            .json(&request)
+            .map_err(|err| Error::Gloo(err.to_string()))?
+            .send()
+            .await
+            .map_err(|err| Error::Gloo(err.to_string()))?
+            .json::<Value>()
+            .await
+            .map_err(|err| Error::Gloo(err.to_string()))?;
+
+        let response: Result<MintQuoteBolt11Response, 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]
     async fn post_mint(
         &self,
@@ -61,7 +91,7 @@ impl Client for HttpClient {
         quote: &str,
         premint_secrets: PreMintSecrets,
     ) -> Result<MintBolt11Response, Error> {
-        let url = join_url(mint_url, &["mint"])?;
+        let url = join_url(mint_url, &["v1", "mint"])?;
 
         let request = MintBolt11Request {
             quote: quote.to_string(),

+ 30 - 0
crates/cashu-sdk/src/client/minreq_client.rs

@@ -9,7 +9,9 @@ use cashu::nuts::{
 };
 #[cfg(feature = "nut07")]
 use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse};
+use cashu::Amount;
 use serde_json::Value;
+use tracing::warn;
 use url::Url;
 
 use super::join_url;
@@ -43,6 +45,34 @@ impl Client for HttpClient {
         }
     }
 
+    /// Mint Quote [NUT-04]
+    async fn post_mint_quote(
+        &self,
+        mint_url: Url,
+        amount: Amount,
+        unit: CurrencyUnit,
+    ) -> Result<MintQuoteBolt11Response, Error> {
+        let url = join_url(mint_url, &["v1", "quote", "bolt11"])?;
+
+        let request = MintQuoteBolt11Request { amount, unit };
+
+        let res = minreq::post(url)
+            .with_json(&request)?
+            .send()?
+            .json::<Value>()?;
+
+        let response: Result<MintQuoteBolt11Response, serde_json::Error> =
+            serde_json::from_value(res.clone());
+
+        match response {
+            Ok(res) => Ok(res),
+            Err(_) => {
+                warn!("Bolt11 Mint Quote Unexpected response: {}", res);
+                Err(Error::from_json(&res.to_string())?)
+            }
+        }
+    }
+
     /// Mint Tokens [NUT-04]
     async fn post_mint(
         &self,

+ 10 - 4
crates/cashu-sdk/src/client/mod.rs

@@ -6,10 +6,10 @@ use cashu::nuts::nut00;
 #[cfg(feature = "nut07")]
 use cashu::nuts::CheckSpendableResponse;
 use cashu::nuts::{
-    BlindedMessage, Keys, KeysetResponse, MeltBolt11Response, MintBolt11Response, MintInfo,
-    PreMintSecrets, Proof, SwapRequest, SwapResponse,
+    BlindedMessage, CurrencyUnit, Keys, KeysetResponse, MeltBolt11Response, MintBolt11Response,
+    MintInfo, MintQuoteBolt11Response, PreMintSecrets, Proof, SwapRequest, SwapResponse,
 };
-use cashu::utils;
+use cashu::{utils, Amount};
 use serde::{Deserialize, Serialize};
 use thiserror::Error;
 use url::Url;
@@ -87,7 +87,13 @@ pub trait Client {
 
     async fn get_mint_keysets(&self, mint_url: Url) -> Result<KeysetResponse, Error>;
 
-    // TODO: Hash should have a type
+    async fn post_mint_quote(
+        &self,
+        mint_url: Url,
+        amount: Amount,
+        unit: CurrencyUnit,
+    ) -> Result<MintQuoteBolt11Response, Error>;
+
     async fn post_mint(
         &self,
         mint_url: Url,

+ 56 - 16
crates/cashu-sdk/src/wallet.rs

@@ -1,4 +1,5 @@
 //! Cashu Wallet
+use std::collections::HashMap;
 use std::str::FromStr;
 
 use cashu::dhke::{construct_proofs, unblind_message};
@@ -10,7 +11,7 @@ use cashu::nuts::{
 };
 #[cfg(feature = "nut07")]
 use cashu::types::ProofsStatus;
-use cashu::types::{Melted, SendProofs};
+use cashu::types::{Melted, MintQuoteInfo, SendProofs};
 use cashu::url::UncheckedUrl;
 use cashu::Amount;
 pub use cashu::Bolt11Invoice;
@@ -39,16 +40,23 @@ pub enum Error {
 pub struct Wallet<C: Client> {
     pub client: C,
     pub mint_url: UncheckedUrl,
+    pub quotes: HashMap<String, MintQuoteInfo>,
     pub mint_keys: Keys,
     pub balance: Amount,
 }
 
 impl<C: Client> Wallet<C> {
-    pub fn new(client: C, mint_url: UncheckedUrl, mint_keys: Keys) -> Self {
+    pub fn new(
+        client: C,
+        mint_url: UncheckedUrl,
+        quotes: Vec<MintQuoteInfo>,
+        mint_keys: Keys,
+    ) -> Self {
         Self {
             client,
             mint_url,
             mint_keys,
+            quotes: quotes.into_iter().map(|q| (q.id.clone(), q)).collect(),
             balance: Amount::ZERO,
         }
     }
@@ -80,25 +88,56 @@ impl<C: Client> Wallet<C> {
             spent: spent.into_iter().map(|(s, _)| s).cloned().collect(),
         })
     }
-
-    // TODO: Need to use the unit, check keyset is of the same unit of attempting to
-    // mint
-    pub async fn mint_token(
-        &self,
+    /*
+     // TODO: Need to use the unit, check keyset is of the same unit of attempting to
+     // mint
+     pub async fn mint_token(
+         &self,
+         amount: Amount,
+         quote: &str,
+         memo: Option<String>,
+         unit: Option<CurrencyUnit>,
+     ) -> Result<Token, Error> {
+         let proofs = self.mint(amount, unit.clone().unwrap()).await?;
+
+         let token = Token::new(self.mint_url.clone(), proofs, memo, unit);
+         Ok(token?)
+     }
+
+    */
+
+    pub async fn mint_quote(
+        &mut self,
         amount: Amount,
-        quote: &str,
-        memo: Option<String>,
-        unit: Option<CurrencyUnit>,
-    ) -> Result<Token, Error> {
-        let proofs = self.mint(amount, quote).await?;
+        unit: CurrencyUnit,
+    ) -> Result<MintQuoteInfo, Error> {
+        let quote_res = self
+            .client
+            .post_mint_quote(self.mint_url.clone().try_into()?, amount, unit.clone())
+            .await?;
+
+        let quote = MintQuoteInfo {
+            id: quote_res.quote.clone(),
+            amount,
+            unit,
+            request: Some(Bolt11Invoice::from_str(&quote_res.request).unwrap()),
+            paid: quote_res.paid,
+            expiry: quote_res.expiry,
+        };
 
-        let token = Token::new(self.mint_url.clone(), proofs, memo, unit);
-        Ok(token?)
+        self.quotes.insert(quote_res.quote.clone(), quote.clone());
+
+        Ok(quote)
     }
 
     /// Mint Proofs
-    pub async fn mint(&self, amount: Amount, quote: &str) -> Result<Proofs, Error> {
-        let premint_secrets = PreMintSecrets::random((&self.mint_keys).into(), amount)?;
+    pub async fn mint(&self, quote: &str) -> Result<Proofs, Error> {
+        let quote_info = self
+            .quotes
+            .get(quote)
+            .ok_or(Error::Custom("Unknown quote".to_string()))?;
+
+        let premint_secrets = PreMintSecrets::random((&self.mint_keys).into(), quote_info.amount)?;
 
         let mint_res = self
             .client
@@ -271,6 +310,7 @@ impl<C: Client> Wallet<C> {
         })
     }
 
+    /// Melt
     pub async fn melt(
         &self,
         quote: String,

+ 12 - 1
crates/cashu/src/types.rs

@@ -3,7 +3,7 @@
 use serde::{Deserialize, Serialize};
 
 use crate::nuts::{CurrencyUnit, Proofs};
-use crate::Bolt11Invoice;
+use crate::{Amount, Bolt11Invoice};
 
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct ProofsStatus {
@@ -34,6 +34,17 @@ pub enum InvoiceStatus {
     InFlight,
 }
 
+/// Mint Quote Info
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+pub struct MintQuoteInfo {
+    pub id: String,
+    pub amount: Amount,
+    pub unit: CurrencyUnit,
+    pub request: Option<Bolt11Invoice>,
+    pub paid: bool,
+    pub expiry: u64,
+}
+
 /// Quote
 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
 pub struct Quote {