Explorar el Código

refactor: v1 MeltRequest

TODO: ffi bindings
thesimplekid hace 1 año
padre
commit
c706e367e9

+ 35 - 27
bindings/cashu-ffi/src/cashu.udl

@@ -6,6 +6,12 @@ interface CashuError {
 };
 
 // Types
+
+[Enum]
+interface CurrencyUnit {
+	Sat();
+	Custom(string unit);
+};
  
 interface Bolt11Invoice {
     [Throws=CashuError]
@@ -88,11 +94,11 @@ interface MintProofs {
 
 interface Token {
     [Throws=CashuError]
-	constructor(string mint, sequence<Proof> token, string? unit, string? memo);
+	constructor(string mint, sequence<Proof> token, string? memo, string? unit);
 	sequence<MintProofs> token();
 	string? memo();
-    [Throws=CashuError]
-	string as_string();
+	string? unit();
+	string to_string();
     [Throws=CashuError, Name=from_string]
 	constructor(string token);
 	
@@ -144,6 +150,32 @@ interface KeySetResponse {
 	sequence<KeySetInfo> keysets();
 };
 
+// NUT-05
+
+interface MeltQuoteBolt11Response {
+    [Throws=CashuError]
+	constructor(string quote, u64 amount, u64 fee_reserve, boolean paid, u64 expiry);
+	string quote();
+	u64 amount();
+	u64 fee_reserve();
+	boolean paid();
+	u64 expiry();
+};
+
+interface MeltQuoteBolt11Request {
+    [Throws=CashuError]
+	constructor(string request, string unit);
+	string request();
+	string unit();
+};
+
+interface MeltBolt11Request {
+    [Throws=CashuError]
+	constructor(sequence<Proof> inputs, string quote);
+	sequence<Proof> inputs();
+	string quote();
+};
+
 interface RequestMintResponse {
     [Throws=CashuError]
 	constructor(string invoice, string hash);
@@ -162,30 +194,6 @@ interface PostMintResponse {
 	sequence<BlindedSignature> promises();
 };
 
-interface CheckFeesRequest {
-    [Throws=CashuError]
-	constructor(string invoice);
-	string invoice();
-};
-
-interface CheckFeesResponse {
-	constructor(Amount amount);
-	Amount amount();
-};
-
-interface Nut05MeltRequest {
-    [Throws=CashuError]
-	constructor(sequence<Proof> proofs, string Invoice);
-	sequence<Proof> proofs();
-	string invoice();
-};
-
-interface Nut05MeltResponse {
-	constructor(boolean paid, string? preimage);
-	boolean paid();
-	string? preimage();
-};
-
 interface SplitRequest {
 	constructor(sequence<Proof> proofs, sequence<BlindedMessage> outputs);
 	sequence<Proof> proofs();

+ 4 - 4
bindings/cashu-ffi/src/lib.rs

@@ -12,7 +12,7 @@ mod ffi {
     pub use crate::nuts::nut00::premint_secrets::PreMintSecrets;
     pub use crate::nuts::nut00::proof::mint::Proof as MintProof;
     pub use crate::nuts::nut00::proof::Proof;
-    pub use crate::nuts::nut00::token::Token;
+    pub use crate::nuts::nut00::token::{CurrencyUnit, Token};
     pub use crate::nuts::nut01::key_pair::KeyPair;
     pub use crate::nuts::nut01::keys::{Keys, KeysResponse};
     pub use crate::nuts::nut01::public_key::PublicKey;
@@ -21,11 +21,11 @@ mod ffi {
     pub use crate::nuts::nut03::{RequestMintResponse, SplitRequest, SplitResponse};
     pub use crate::nuts::nut04::{MintRequest, PostMintResponse};
     pub use crate::nuts::nut05::{
-        CheckFeesRequest, CheckFeesResponse, MeltRequest as Nut05MeltRequest,
-        MeltResponse as Nut05MeltResponse,
+        MeltBolt11Request as Nut05MeltBolt11Request, MeltBolt11Response as Nut05MeltBolt11Response,
+        MeltQuoteBolt11Request,
     };
     pub use crate::nuts::nut07::{CheckSpendableRequest, CheckSpendableResponse};
-    pub use crate::nuts::nut08::{MeltRequest, MeltResponse};
+    pub use crate::nuts::nut08::{MeltBolt11Request, MeltBolt11Response};
     pub use crate::nuts::nut09::{MintInfo, MintVersion};
     pub use crate::types::amount::Amount;
     pub use crate::types::{Bolt11Invoice, KeySetInfo, Secret};

+ 7 - 0
bindings/cashu-ffi/src/nuts/nut00/blinded_signature.rs

@@ -9,6 +9,13 @@ pub struct BlindedSignature {
     inner: BlindedSignatureSdk,
 }
 
+impl Deref for BlindedSignature {
+    type Target = BlindedSignatureSdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
 impl BlindedSignature {
     pub fn new(id: Arc<Id>, amount: Arc<Amount>, c: Arc<PublicKey>) -> Self {
         Self {

+ 41 - 4
bindings/cashu-ffi/src/nuts/nut00/token.rs

@@ -1,12 +1,37 @@
+use std::fmt;
 use std::str::FromStr;
 use std::sync::Arc;
 
 use cashu::nuts::nut00::wallet::Token as TokenSdk;
+use cashu::nuts::CurrencyUnit as CurrencyUnitSdk;
 use cashu::url::UncheckedUrl;
 
 use crate::error::Result;
 use crate::{MintProofs, Proof};
 
+pub enum CurrencyUnit {
+    Sat,
+    Custom { unit: String },
+}
+
+impl From<&CurrencyUnit> for CurrencyUnitSdk {
+    fn from(unit: &CurrencyUnit) -> CurrencyUnitSdk {
+        match unit {
+            CurrencyUnit::Sat => CurrencyUnitSdk::Sat,
+            CurrencyUnit::Custom { unit } => CurrencyUnitSdk::Custom(unit.clone()),
+        }
+    }
+}
+
+impl From<CurrencyUnitSdk> for CurrencyUnit {
+    fn from(unit: CurrencyUnitSdk) -> CurrencyUnit {
+        match unit {
+            CurrencyUnitSdk::Sat => CurrencyUnit::Sat,
+            CurrencyUnitSdk::Custom(unit) => CurrencyUnit::Custom { unit: unit.clone() },
+        }
+    }
+}
+
 pub struct Token {
     inner: TokenSdk,
 }
@@ -15,13 +40,16 @@ impl Token {
     pub fn new(
         mint: String,
         proofs: Vec<Arc<Proof>>,
-        unit: Option<String>,
         memo: Option<String>,
+        unit: Option<String>,
     ) -> Result<Self> {
         let mint = UncheckedUrl::from_str(&mint)?;
         let proofs = proofs.into_iter().map(|p| p.as_ref().into()).collect();
+
+        let unit = unit.map(|u| CurrencyUnitSdk::from_str(&u).unwrap_or_default().into());
+
         Ok(Self {
-            inner: TokenSdk::new(mint, proofs, unit, memo)?,
+            inner: TokenSdk::new(mint, proofs, memo, unit)?,
         })
     }
 
@@ -38,14 +66,23 @@ impl Token {
         self.inner.memo.clone()
     }
 
+    pub fn unit(&self) -> Option<String> {
+        self.inner
+            .unit
+            .clone()
+            .map(|u| Into::<CurrencyUnitSdk>::into(u).to_string())
+    }
+
     pub fn from_string(token: String) -> Result<Self> {
         Ok(Self {
             inner: TokenSdk::from_str(&token)?,
         })
     }
+}
 
-    pub fn as_string(&self) -> Result<String> {
-        Ok(self.inner.to_string())
+impl fmt::Display for Token {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.inner.to_string())
     }
 }
 

+ 81 - 51
bindings/cashu-ffi/src/nuts/nut05/mod.rs

@@ -2,100 +2,130 @@ use std::ops::Deref;
 use std::str::FromStr;
 use std::sync::Arc;
 
-use cashu::nuts::nut05::{
-    CheckFeesRequest as CheckFeesRequestSdk, CheckFeesResponse as CheckFeesResponseSdk,
-    MeltRequest as MeltRequestSdk, MeltResponse as MeltResponseSdk,
+use cashu::nuts::{
+    CurrencyUnit, MeltBolt11Request as MeltBolt11RequestSdk,
+    MeltBolt11Response as MeltBolt11ResponseSdk,
+    MeltQuoteBolt11Request as MeltQuoteBolt11RequestSdk,
+    MeltQuoteBolt11Response as MeltQuoteBolt11ResponseSdk,
 };
 use cashu::Bolt11Invoice;
 
 use crate::error::Result;
-use crate::{Amount, Proof};
+use crate::{BlindedMessage, BlindedSignature, Proof};
 
-pub struct CheckFeesRequest {
-    inner: CheckFeesRequestSdk,
+pub struct MeltQuoteBolt11Response {
+    inner: MeltQuoteBolt11ResponseSdk,
 }
 
-impl CheckFeesRequest {
-    pub fn new(invoice: String) -> Result<Self> {
+impl MeltQuoteBolt11Response {
+    pub fn new(
+        quote: String,
+        amount: u64,
+        fee_reserve: u64,
+        paid: bool,
+        expiry: u64,
+    ) -> Result<Self> {
         Ok(Self {
-            inner: CheckFeesRequestSdk {
-                pr: Bolt11Invoice::from_str(&invoice)?,
+            inner: MeltQuoteBolt11ResponseSdk {
+                quote,
+                amount,
+                fee_reserve,
+                paid,
+                expiry,
             },
         })
     }
 
-    pub fn invoice(&self) -> String {
-        self.inner.pr.to_string()
+    pub fn quote(&self) -> String {
+        self.inner.quote.clone()
     }
-}
 
-pub struct CheckFeesResponse {
-    inner: CheckFeesResponseSdk,
-}
+    pub fn amount(&self) -> u64 {
+        self.inner.amount
+    }
 
-impl CheckFeesResponse {
-    pub fn new(amount: Arc<Amount>) -> Self {
-        Self {
-            inner: CheckFeesResponseSdk {
-                fee: *amount.as_ref().deref(),
-            },
-        }
+    pub fn fee_reserve(&self) -> u64 {
+        self.inner.fee_reserve
     }
 
-    pub fn amount(&self) -> Arc<Amount> {
-        Arc::new(self.inner.fee.into())
+    pub fn paid(&self) -> bool {
+        self.inner.paid
     }
-}
 
-impl From<cashu::nuts::nut05::CheckFeesResponse> for CheckFeesResponse {
-    fn from(inner: cashu::nuts::nut05::CheckFeesResponse) -> CheckFeesResponse {
-        Self { inner }
+    pub fn expiry(&self) -> u64 {
+        self.inner.expiry
     }
 }
 
-impl From<CheckFeesResponse> for cashu::nuts::nut05::CheckFeesResponse {
-    fn from(res: CheckFeesResponse) -> cashu::nuts::nut05::CheckFeesResponse {
-        res.inner
+pub struct MeltQuoteBolt11Request {
+    inner: MeltQuoteBolt11RequestSdk,
+}
+
+impl MeltQuoteBolt11Request {
+    pub fn new(request: String, unit: String) -> Result<Self> {
+        Ok(Self {
+            inner: MeltQuoteBolt11RequestSdk {
+                request: Bolt11Invoice::from_str(&request)?,
+                unit: CurrencyUnit::from_str(&unit)?,
+            },
+        })
+    }
+
+    pub fn request(&self) -> String {
+        self.inner.request.to_string()
+    }
+
+    pub fn unit(&self) -> String {
+        self.inner.unit.to_string()
     }
 }
 
-pub struct MeltRequest {
-    inner: MeltRequestSdk,
+pub struct MeltBolt11Request {
+    inner: MeltBolt11RequestSdk,
 }
 
-impl MeltRequest {
-    pub fn new(proofs: Vec<Arc<Proof>>, invoice: String) -> Result<Self> {
-        let pr = Bolt11Invoice::from_str(&invoice)?;
+impl MeltBolt11Request {
+    pub fn new(
+        quote: String,
+        inputs: Vec<Arc<Proof>>,
+        outputs: Option<Vec<Arc<BlindedMessage>>>,
+    ) -> Result<Self> {
         Ok(Self {
-            inner: MeltRequestSdk {
-                pr,
-                proofs: proofs.into_iter().map(|p| p.as_ref().into()).collect(),
+            inner: MeltBolt11RequestSdk {
+                quote,
+                inputs: inputs.into_iter().map(|p| p.as_ref().into()).collect(),
+                outputs: outputs
+                    .map(|o| o.into_iter().map(|p| p.as_ref().deref().clone()).collect()),
             },
         })
     }
 
-    pub fn proofs(&self) -> Vec<Arc<Proof>> {
+    pub fn inputs(&self) -> Vec<Arc<Proof>> {
         self.inner
-            .proofs
+            .inputs
             .clone()
             .into_iter()
             .map(|p| Arc::new(p.into()))
             .collect()
     }
 
-    pub fn invoice(&self) -> String {
-        self.inner.pr.to_string()
+    pub fn quote(&self) -> String {
+        self.inner.quote
     }
 }
 
-pub struct MeltResponse {
-    inner: MeltResponseSdk,
+pub struct MeltBolt11Response {
+    inner: MeltBolt11ResponseSdk,
 }
 
-impl MeltResponse {
-    pub fn new(paid: bool, preimage: Option<String>) -> Self {
+impl MeltBolt11Response {
+    pub fn new(paid: bool, proof: String, change: Option<Vec<Arc<BlindedSignature>>>) -> Self {
         Self {
-            inner: MeltResponseSdk { paid, preimage },
+            inner: MeltBolt11ResponseSdk {
+                paid,
+                proof,
+                change: change.map(|c| c.into_iter().map(|b| b.as_ref().deref().clone()).collect()),
+            },
         }
     }
 
@@ -103,7 +133,7 @@ impl MeltResponse {
         self.inner.paid
     }
 
-    pub fn preimage(&self) -> Option<String> {
-        self.inner.preimage.clone()
+    pub fn proof(&self) -> String {
+        self.inner.proof.clone()
     }
 }

+ 31 - 37
bindings/cashu-ffi/src/nuts/nut08/mod.rs

@@ -1,53 +1,51 @@
 use std::ops::Deref;
-use std::str::FromStr;
 use std::sync::Arc;
 
-use cashu::nuts::nut08::{MeltRequest as MeltRequestSdk, MeltResponse as MeltResponseSdk};
-use cashu::Bolt11Invoice;
+use cashu::nuts::nut08::{
+    MeltBolt11Request as MeltBolt11RequestSdk, MeltBolt11Response as MeltBolt11ResponseSdk,
+};
 
 use crate::error::Result;
 use crate::{BlindedMessage, BlindedSignature, Proof};
 
-pub struct MeltRequest {
-    inner: MeltRequestSdk,
+pub struct MeltBolt11Request {
+    inner: MeltBolt11RequestSdk,
 }
 
-impl Deref for MeltRequest {
-    type Target = MeltRequestSdk;
+impl Deref for MeltBolt11Request {
+    type Target = MeltBolt11RequestSdk;
     fn deref(&self) -> &Self::Target {
         &self.inner
     }
 }
 
-impl MeltRequest {
+impl MeltBolt11Request {
     pub fn new(
+        quote: String,
         proofs: Vec<Arc<Proof>>,
-        invoice: String,
         outputs: Option<Vec<Arc<BlindedMessage>>>,
     ) -> Result<Self> {
-        let pr = Bolt11Invoice::from_str(&invoice)?;
-
         Ok(Self {
-            inner: MeltRequestSdk {
-                proofs: proofs.iter().map(|p| p.as_ref().into()).collect(),
-                pr,
+            inner: MeltBolt11RequestSdk {
+                quote,
+                inputs: proofs.iter().map(|p| p.as_ref().into()).collect(),
                 outputs: outputs
                     .map(|outputs| outputs.into_iter().map(|o| o.as_ref().into()).collect()),
             },
         })
     }
 
-    pub fn proofs(&self) -> Vec<Arc<Proof>> {
+    pub fn inputs(&self) -> Vec<Arc<Proof>> {
         self.inner
-            .proofs
+            .inputs
             .clone()
             .into_iter()
             .map(|o| Arc::new(o.into()))
             .collect()
     }
 
-    pub fn invoice(&self) -> String {
-        self.inner.pr.to_string()
+    pub fn quote(&self) -> String {
+        self.inner.quote.clone()
     }
 
     pub fn outputs(&self) -> Option<Vec<Arc<BlindedMessage>>> {
@@ -58,39 +56,35 @@ impl MeltRequest {
     }
 }
 
-pub struct MeltResponse {
-    inner: MeltResponseSdk,
+pub struct MeltBolt11Response {
+    inner: MeltBolt11ResponseSdk,
 }
 
-impl Deref for MeltResponse {
-    type Target = MeltResponseSdk;
+impl Deref for MeltBolt11Response {
+    type Target = MeltBolt11ResponseSdk;
     fn deref(&self) -> &Self::Target {
         &self.inner
     }
 }
 
-impl From<cashu::nuts::nut08::MeltResponse> for MeltResponse {
-    fn from(inner: cashu::nuts::nut08::MeltResponse) -> MeltResponse {
-        MeltResponse { inner }
+impl From<cashu::nuts::nut08::MeltBolt11Response> for MeltBolt11Response {
+    fn from(inner: cashu::nuts::nut08::MeltBolt11Response) -> MeltBolt11Response {
+        MeltBolt11Response { inner }
     }
 }
 
-impl From<MeltResponse> for cashu::nuts::nut08::MeltResponse {
-    fn from(res: MeltResponse) -> cashu::nuts::nut08::MeltResponse {
+impl From<MeltBolt11Response> for cashu::nuts::nut08::MeltBolt11Response {
+    fn from(res: MeltBolt11Response) -> cashu::nuts::nut08::MeltBolt11Response {
         res.inner
     }
 }
 
-impl MeltResponse {
-    pub fn new(
-        paid: bool,
-        preimage: Option<String>,
-        change: Option<Vec<Arc<BlindedSignature>>>,
-    ) -> Self {
+impl MeltBolt11Response {
+    pub fn new(paid: bool, proof: String, change: Option<Vec<Arc<BlindedSignature>>>) -> Self {
         Self {
-            inner: MeltResponseSdk {
+            inner: MeltBolt11ResponseSdk {
                 paid,
-                preimage,
+                proof,
                 change: change
                     .map(|change| change.into_iter().map(|bs| bs.as_ref().into()).collect()),
             },
@@ -101,8 +95,8 @@ impl MeltResponse {
         self.inner.paid
     }
 
-    pub fn preimage(&self) -> Option<String> {
-        self.inner.preimage.clone()
+    pub fn proof(&self) -> String {
+        self.inner.proof.clone()
     }
 
     pub fn change(&self) -> Option<Vec<Arc<BlindedSignature>>> {

+ 8 - 2
bindings/cashu-js/src/nuts/nut00/token.rs

@@ -2,6 +2,7 @@ use std::ops::Deref;
 use std::str::FromStr;
 
 use cashu::nuts::nut00::wallet::Token;
+use cashu::nuts::CurrencyUnit;
 use cashu::url::UncheckedUrl;
 use wasm_bindgen::prelude::*;
 
@@ -31,13 +32,18 @@ impl JsToken {
     pub fn new(
         mint: String,
         proofs: JsValue,
-        unit: Option<String>,
         memo: Option<String>,
+        unit: Option<String>,
     ) -> Result<JsToken> {
         let mint = UncheckedUrl::from_str(&mint).map_err(into_err)?;
         let proofs = serde_wasm_bindgen::from_value(proofs).map_err(into_err)?;
+        let unit = unit.map(|u| {
+            CurrencyUnit::from_str(&u)
+                .map_err(into_err)
+                .unwrap_or_default()
+        });
         Ok(Self {
-            inner: Token::new(mint, proofs, unit, memo).map_err(into_err)?,
+            inner: Token::new(mint, proofs, memo, unit).map_err(into_err)?,
         })
     }
 

+ 0 - 78
bindings/cashu-js/src/nuts/nut05.rs

@@ -1,79 +1 @@
-use std::ops::Deref;
 
-use cashu::nuts::nut05::{CheckFeesRequest, CheckFeesResponse};
-use wasm_bindgen::prelude::*;
-
-use crate::error::Result;
-use crate::types::{JsAmount, JsBolt11Invoice};
-
-#[wasm_bindgen(js_name = CheckFeesRequest)]
-pub struct JsCheckFeesRequest {
-    inner: CheckFeesRequest,
-}
-
-impl Deref for JsCheckFeesRequest {
-    type Target = CheckFeesRequest;
-    fn deref(&self) -> &Self::Target {
-        &self.inner
-    }
-}
-
-impl From<CheckFeesRequest> for JsCheckFeesRequest {
-    fn from(inner: CheckFeesRequest) -> JsCheckFeesRequest {
-        JsCheckFeesRequest { inner }
-    }
-}
-
-#[wasm_bindgen(js_class = CheckFeesRequest)]
-impl JsCheckFeesRequest {
-    #[wasm_bindgen(constructor)]
-    pub fn new(invoice: JsBolt11Invoice) -> Result<JsCheckFeesRequest> {
-        Ok(JsCheckFeesRequest {
-            inner: CheckFeesRequest {
-                pr: invoice.clone(),
-            },
-        })
-    }
-
-    /// Get Amount
-    #[wasm_bindgen(getter)]
-    pub fn invoice(&self) -> JsBolt11Invoice {
-        self.inner.pr.clone().into()
-    }
-}
-
-#[wasm_bindgen(js_name = CheckFeesResponse)]
-pub struct JsCheckFeesResponse {
-    inner: CheckFeesResponse,
-}
-
-impl Deref for JsCheckFeesResponse {
-    type Target = CheckFeesResponse;
-    fn deref(&self) -> &Self::Target {
-        &self.inner
-    }
-}
-
-impl From<CheckFeesResponse> for JsCheckFeesResponse {
-    fn from(inner: CheckFeesResponse) -> JsCheckFeesResponse {
-        JsCheckFeesResponse { inner }
-    }
-}
-
-#[wasm_bindgen(js_class = CheckFeesResponse)]
-impl JsCheckFeesResponse {
-    #[wasm_bindgen(constructor)]
-    pub fn new(amount: JsAmount) -> Result<JsCheckFeesResponse> {
-        Ok(JsCheckFeesResponse {
-            inner: CheckFeesResponse {
-                fee: *amount.deref(),
-            },
-        })
-    }
-
-    /// Get Amount
-    #[wasm_bindgen(getter)]
-    pub fn amount(&self) -> JsAmount {
-        self.inner.fee.into()
-    }
-}

+ 26 - 30
bindings/cashu-js/src/nuts/nut08.rs

@@ -1,26 +1,26 @@
 use std::ops::Deref;
 
 use cashu::nuts::nut00::{BlindedMessage, BlindedSignature, Proof};
-use cashu::nuts::nut08::{MeltRequest, MeltResponse};
+use cashu::nuts::nut08::{MeltBolt11Request, MeltBolt11Response};
 use wasm_bindgen::prelude::*;
 
 use crate::error::{into_err, Result};
-use crate::types::{JsAmount, JsBolt11Invoice};
+use crate::types::JsAmount;
 
 #[wasm_bindgen(js_name = MeltRequest)]
 pub struct JsMeltRequest {
-    inner: MeltRequest,
+    inner: MeltBolt11Request,
 }
 
 impl Deref for JsMeltRequest {
-    type Target = MeltRequest;
+    type Target = MeltBolt11Request;
     fn deref(&self) -> &Self::Target {
         &self.inner
     }
 }
 
-impl From<MeltRequest> for JsMeltRequest {
-    fn from(inner: MeltRequest) -> JsMeltRequest {
+impl From<MeltBolt11Request> for JsMeltRequest {
+    fn from(inner: MeltBolt11Request) -> JsMeltRequest {
         JsMeltRequest { inner }
     }
 }
@@ -28,12 +28,8 @@ impl From<MeltRequest> for JsMeltRequest {
 #[wasm_bindgen(js_class = MeltRequest)]
 impl JsMeltRequest {
     #[wasm_bindgen(constructor)]
-    pub fn new(
-        proofs: JsValue,
-        invoice: JsBolt11Invoice,
-        outputs: JsValue,
-    ) -> Result<JsMeltRequest> {
-        let proofs: Vec<Proof> = serde_wasm_bindgen::from_value(proofs).map_err(into_err)?;
+    pub fn new(quote: String, inputs: JsValue, outputs: JsValue) -> Result<JsMeltRequest> {
+        let inputs: Vec<Proof> = serde_wasm_bindgen::from_value(inputs).map_err(into_err)?;
         let outputs: Option<Vec<BlindedMessage>> = if !outputs.is_null() {
             Some(serde_wasm_bindgen::from_value(outputs).map_err(into_err)?)
         } else {
@@ -41,9 +37,9 @@ impl JsMeltRequest {
         };
 
         Ok(JsMeltRequest {
-            inner: MeltRequest {
-                proofs,
-                pr: invoice.deref().clone(),
+            inner: MeltBolt11Request {
+                quote,
+                inputs,
                 outputs,
             },
         })
@@ -51,14 +47,14 @@ impl JsMeltRequest {
 
     /// Get Proofs
     #[wasm_bindgen(getter)]
-    pub fn proofs(&self) -> Result<JsValue> {
-        serde_wasm_bindgen::to_value(&self.inner.proofs).map_err(into_err)
+    pub fn inputs(&self) -> Result<JsValue> {
+        serde_wasm_bindgen::to_value(&self.inner.inputs).map_err(into_err)
     }
 
     /// Get Invoice
     #[wasm_bindgen(getter)]
-    pub fn invoice(&self) -> JsBolt11Invoice {
-        self.inner.pr.clone().into()
+    pub fn quote(&self) -> String {
+        self.inner.quote.clone()
     }
 
     /// Get outputs
@@ -70,18 +66,18 @@ impl JsMeltRequest {
 
 #[wasm_bindgen(js_name = MeltResponse)]
 pub struct JsMeltResponse {
-    inner: MeltResponse,
+    inner: MeltBolt11Response,
 }
 
 impl Deref for JsMeltResponse {
-    type Target = MeltResponse;
+    type Target = MeltBolt11Response;
     fn deref(&self) -> &Self::Target {
         &self.inner
     }
 }
 
-impl From<MeltResponse> for JsMeltResponse {
-    fn from(inner: MeltResponse) -> JsMeltResponse {
+impl From<MeltBolt11Response> for JsMeltResponse {
+    fn from(inner: MeltBolt11Response) -> JsMeltResponse {
         JsMeltResponse { inner }
     }
 }
@@ -89,7 +85,7 @@ impl From<MeltResponse> for JsMeltResponse {
 #[wasm_bindgen(js_class = MeltResponse)]
 impl JsMeltResponse {
     #[wasm_bindgen(constructor)]
-    pub fn new(paid: bool, preimage: Option<String>, change: JsValue) -> Result<JsMeltResponse> {
+    pub fn new(paid: bool, proof: String, change: JsValue) -> Result<JsMeltResponse> {
         let change: Option<Vec<BlindedSignature>> = if change.is_null() {
             Some(serde_wasm_bindgen::from_value(change).map_err(into_err)?)
         } else {
@@ -97,9 +93,9 @@ impl JsMeltResponse {
         };
 
         Ok(JsMeltResponse {
-            inner: MeltResponse {
+            inner: MeltBolt11Response {
+                proof,
                 paid,
-                preimage,
                 change,
             },
         })
@@ -113,8 +109,8 @@ impl JsMeltResponse {
 
     /// Get Preimage
     #[wasm_bindgen(getter)]
-    pub fn preimage(&self) -> Option<String> {
-        self.inner.preimage.clone()
+    pub fn proof(&self) -> String {
+        self.inner.proof.clone()
     }
 
     /// Get Change
@@ -125,7 +121,7 @@ impl JsMeltResponse {
 
     /// Change Amount
     #[wasm_bindgen(js_name = "changeAmount")]
-    pub fn change_amount(&self) -> JsAmount {
-        self.inner.change_amount().into()
+    pub fn change_amount(&self) -> Option<JsAmount> {
+        self.inner.change_amount().map(|a| a.into())
     }
 }

+ 36 - 52
bindings/cashu-sdk-ffi/src/cashu_sdk.udl

@@ -10,6 +10,13 @@ interface CashuError {
 
 // Types 
 
+
+[Enum]
+interface CurrencyUnit {
+	Sat();
+	Custom(string unit);
+};
+
 interface Bolt11Invoice {
     [Throws=CashuError]
     constructor(string bolt11);
@@ -91,11 +98,11 @@ interface MintProofs {
 
 interface Token {
     [Throws=CashuError]
-	constructor(string mint, sequence<Proof> token, string? unit, string? memo);
+	constructor(string mint, sequence<Proof> token, string? memo, CurrencyUnit? unit);
 	sequence<MintProofs> token();
 	string? memo();
-    [Throws=CashuError]
-	string as_string();
+	CurrencyUnit? unit();
+	string to_string();
     [Throws=CashuError, Name=from_string]
 	constructor(string token);
 	
@@ -148,6 +155,32 @@ interface KeySetResponse {
 	sequence<KeySetInfo> keysets();
 };
 
+// NUT-05
+
+interface MeltQuoteBolt11Response {
+    [Throws=CashuError]
+	constructor(string quote, u64 amount, u64 fee_reserve, boolean paid, u64 expiry);
+	string quote();
+	u64 amount();
+	u64 fee_reserve();
+	boolean paid();
+	u64 expiry();
+};
+
+interface MeltQuoteBolt11Request {
+    [Throws=CashuError]
+	constructor(string request, string unit);
+	string request();
+	string unit();
+};
+
+interface MeltBolt11Request {
+    [Throws=CashuError]
+	constructor(sequence<Proof> inputs, string quote);
+	sequence<Proof> inputs();
+	string quote();
+};
+
 interface RequestMintResponse {
     [Throws=CashuError]
 	constructor(string invoice, string hash);
@@ -166,30 +199,6 @@ interface PostMintResponse {
 	sequence<BlindedSignature> promises();
 };
 
-interface CheckFeesRequest {
-    [Throws=CashuError]
-	constructor(string invoice);
-	string invoice();
-};
-
-interface CheckFeesResponse {
-	constructor(Amount amount);
-	Amount amount();
-};
-
-interface Nut05MeltRequest {
-    [Throws=CashuError]
-	constructor(sequence<Proof> proofs, string Invoice);
-	sequence<Proof> proofs();
-	string invoice();
-};
-
-interface Nut05MeltResponse {
-	constructor(boolean paid, string? preimage);
-	boolean paid();
-	string? preimage();
-};
-
 interface SplitRequest {
 	constructor(sequence<Proof> proofs, sequence<BlindedMessage> outputs);
 	sequence<Proof> proofs();
@@ -205,31 +214,6 @@ interface SplitResponse {
 
 };
 
-interface CheckSpendableRequest {
-	constructor(sequence<MintProof> proofs);
-	sequence<MintProof> proofs();
-};
-
-interface CheckSpendableResponse {
-	constructor(sequence<boolean> spendable, sequence<boolean> pending);
-	sequence<boolean> spendable();
-	sequence<boolean> pending();	
-};
-
-interface MeltRequest {
-    [Throws=CashuError]
-	constructor(sequence<Proof> proofs, string Invoice, sequence<BlindedMessage>? outputs);
-	sequence<Proof> proofs();
-	string invoice();
-	sequence<BlindedMessage>? outputs();
-};
-
-interface MeltResponse {
-	constructor(boolean paid, string? preimage, sequence<BlindedSignature>? change);
-	boolean paid();
-	string? preimage();
-	sequence<BlindedSignature>? change();
-};
 
 interface MintVersion {
 	constructor(string name, string version);

+ 4 - 0
bindings/cashu-sdk-js/src/mint.rs

@@ -38,16 +38,20 @@ impl JsMint {
         secret: String,
         keyset_info: JsValue,
         spent_secrets: JsValue,
+        quotes: JsValue,
         min_fee_reserve: JsAmount,
         percent_fee_reserve: f32,
     ) -> Result<JsMint> {
         let keyset_info = serde_wasm_bindgen::from_value(keyset_info).map_err(into_err)?;
         let spent_secrets = serde_wasm_bindgen::from_value(spent_secrets).map_err(into_err)?;
+
+        let quotes = serde_wasm_bindgen::from_value(quotes).map_err(into_err)?;
         Ok(JsMint {
             inner: Mint::new(
                 &secret,
                 keyset_info,
                 spent_secrets,
+                quotes,
                 *min_fee_reserve.deref(),
                 percent_fee_reserve,
             ),

+ 16 - 17
bindings/cashu-sdk-js/src/wallet.rs

@@ -1,12 +1,14 @@
 use std::ops::Deref;
+use std::str::FromStr;
 
 use cashu_js::nuts::nut00::{JsBlindedMessages, JsToken};
 use cashu_js::nuts::nut01::JsKeys;
 use cashu_js::nuts::nut03::JsRequestMintResponse;
+use cashu_js::JsAmount;
 #[cfg(feature = "nut07")]
 use cashu_js::JsProofsStatus;
-use cashu_js::{JsAmount, JsBolt11Invoice};
 use cashu_sdk::client::gloo_client::HttpClient;
+use cashu_sdk::nuts::CurrencyUnit;
 use cashu_sdk::wallet::Wallet;
 use wasm_bindgen::prelude::*;
 
@@ -72,12 +74,14 @@ impl JsWallet {
         &self,
         amount: JsAmount,
         hash: String,
-        unit: Option<String>,
         memo: Option<String>,
+        unit: Option<String>,
     ) -> Result<JsToken> {
+        let unit = unit.map(|u| CurrencyUnit::from_str(&u).unwrap_or_default());
+
         Ok(self
             .inner
-            .mint_token(*amount.deref(), &hash, unit, memo)
+            .mint_token(*amount.deref(), &hash, memo, unit)
             .await
             .map_err(into_err)?
             .into())
@@ -96,17 +100,6 @@ impl JsWallet {
         .map_err(into_err)
     }
 
-    /// Check Fee
-    #[wasm_bindgen(js_name = checkFee)]
-    pub async fn check_fee(&self, invoice: JsBolt11Invoice) -> Result<JsAmount> {
-        Ok(self
-            .inner
-            .check_fee(invoice.deref().clone())
-            .await
-            .map_err(into_err)?
-            .into())
-    }
-
     /// Receive
     #[wasm_bindgen(js_name = receive)]
     pub async fn receive(&self, token: String) -> Result<JsValue> {
@@ -149,7 +142,7 @@ impl JsWallet {
     #[wasm_bindgen(js_name = melt)]
     pub async fn melt(
         &self,
-        invoice: JsBolt11Invoice,
+        quote: String,
         proofs: JsValue,
         fee_reserve: JsAmount,
     ) -> Result<JsMelted> {
@@ -157,7 +150,7 @@ impl JsWallet {
 
         Ok(self
             .inner
-            .melt(invoice.deref().clone(), proofs, *fee_reserve.deref())
+            .melt(quote, proofs, *fee_reserve.deref())
             .await
             .map_err(into_err)?
             .into())
@@ -173,8 +166,14 @@ impl JsWallet {
     ) -> Result<String> {
         let proofs = serde_wasm_bindgen::from_value(proofs).map_err(into_err)?;
 
+        let unit = unit.map(|u| {
+            CurrencyUnit::from_str(&u)
+                .map_err(into_err)
+                .unwrap_or_default()
+        });
+
         self.inner
-            .proofs_to_token(proofs, unit, memo)
+            .proofs_to_token(proofs, memo, unit)
             .map_err(into_err)
     }
 }

+ 10 - 40
crates/cashu-sdk/src/client/gloo_client.rs

@@ -4,13 +4,12 @@ use async_trait::async_trait;
 #[cfg(feature = "nut09")]
 use cashu::nuts::MintInfo;
 use cashu::nuts::{
-    BlindedMessage, CheckFeesRequest, CheckFeesResponse, Keys, MeltRequest, MeltResponse,
-    MintRequest, PostMintResponse, PreMintSecrets, Proof, RequestMintResponse, SplitRequest,
-    SplitResponse, *,
+    BlindedMessage, Keys, MeltBolt11Request, MeltBolt11Response, MintRequest, PostMintResponse,
+    PreMintSecrets, Proof, RequestMintResponse, SplitRequest, SplitResponse, *,
 };
 #[cfg(feature = "nut07")]
 use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse};
-use cashu::{Amount, Bolt11Invoice};
+use cashu::Amount;
 use gloo::net::http::Request;
 use serde_json::Value;
 use url::Url;
@@ -118,49 +117,20 @@ impl Client for HttpClient {
         }
     }
 
-    /// Check Max expected fee [NUT-05]
-    async fn post_check_fees(
-        &self,
-        mint_url: Url,
-        invoice: Bolt11Invoice,
-    ) -> Result<CheckFeesResponse, Error> {
-        let url = join_url(mint_url, "checkfees")?;
-
-        let request = CheckFeesRequest { pr: invoice };
-
-        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<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
     async fn post_melt(
         &self,
         mint_url: Url,
-        proofs: Vec<Proof>,
-        invoice: Bolt11Invoice,
+        quote: String,
+        inputs: Vec<Proof>,
         outputs: Option<Vec<BlindedMessage>>,
-    ) -> Result<MeltResponse, Error> {
+    ) -> Result<MeltBolt11Response, Error> {
         let url = join_url(mint_url, "melt")?;
 
-        let request = MeltRequest {
-            proofs,
-            pr: invoice,
+        let request = MeltBolt11Request {
+            quote,
+            inputs,
             outputs,
         };
 
@@ -174,7 +144,7 @@ impl Client for HttpClient {
             .await
             .map_err(|err| Error::Gloo(err.to_string()))?;
 
-        let response: Result<MeltResponse, serde_json::Error> =
+        let response: Result<MeltBolt11Response, serde_json::Error> =
             serde_json::from_value(value.clone());
 
         match response {

+ 10 - 35
crates/cashu-sdk/src/client/minreq_client.rs

@@ -6,13 +6,12 @@ use async_trait::async_trait;
 #[cfg(feature = "nut09")]
 use cashu::nuts::MintInfo;
 use cashu::nuts::{
-    BlindedMessage, CheckFeesRequest, CheckFeesResponse, Keys, MeltRequest, MeltResponse,
-    MintRequest, PostMintResponse, PreMintSecrets, Proof, RequestMintResponse, SplitRequest,
-    SplitResponse, *,
+    BlindedMessage, Keys, MeltBolt11Request, MeltBolt11Response, MintRequest, PostMintResponse,
+    PreMintSecrets, Proof, RequestMintResponse, SplitRequest, SplitResponse, *,
 };
 #[cfg(feature = "nut07")]
 use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse};
-use cashu::{Amount, Bolt11Invoice};
+use cashu::Amount;
 use serde_json::Value;
 use url::Url;
 
@@ -96,44 +95,20 @@ impl Client for HttpClient {
         }
     }
 
-    /// Check Max expected fee [NUT-05]
-    async fn post_check_fees(
-        &self,
-        mint_url: Url,
-        invoice: Bolt11Invoice,
-    ) -> Result<CheckFeesResponse, Error> {
-        let url = join_url(mint_url, "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
     async fn post_melt(
         &self,
         mint_url: Url,
-        proofs: Vec<Proof>,
-        invoice: Bolt11Invoice,
+        quote: String,
+        inputs: Vec<Proof>,
         outputs: Option<Vec<BlindedMessage>>,
-    ) -> Result<MeltResponse, Error> {
+    ) -> Result<MeltBolt11Response, Error> {
         let url = join_url(mint_url, "melt")?;
 
-        let request = MeltRequest {
-            proofs,
-            pr: invoice,
+        let request = MeltBolt11Request {
+            quote,
+            inputs,
             outputs,
         };
 
@@ -142,7 +117,7 @@ impl Client for HttpClient {
             .send()?
             .json::<Value>()?;
 
-        let response: Result<MeltResponse, serde_json::Error> =
+        let response: Result<MeltBolt11Response, serde_json::Error> =
             serde_json::from_value(value.clone());
 
         match response {

+ 5 - 11
crates/cashu-sdk/src/client/mod.rs

@@ -8,8 +8,8 @@ use cashu::nuts::CheckSpendableResponse;
 #[cfg(feature = "nut09")]
 use cashu::nuts::MintInfo;
 use cashu::nuts::{
-    BlindedMessage, CheckFeesResponse, Keys, KeysetResponse, MeltResponse, PostMintResponse,
-    PreMintSecrets, Proof, RequestMintResponse, SplitRequest, SplitResponse,
+    BlindedMessage, Keys, KeysetResponse, MeltBolt11Response, PostMintResponse, PreMintSecrets,
+    Proof, RequestMintResponse, SplitRequest, SplitResponse,
 };
 use cashu::{utils, Amount};
 use serde::{Deserialize, Serialize};
@@ -103,19 +103,13 @@ pub trait Client {
         hash: &str,
     ) -> Result<PostMintResponse, Error>;
 
-    async fn post_check_fees(
-        &self,
-        mint_url: Url,
-        invoice: Bolt11Invoice,
-    ) -> Result<CheckFeesResponse, Error>;
-
     async fn post_melt(
         &self,
         mint_url: Url,
-        proofs: Vec<Proof>,
-        invoice: Bolt11Invoice,
+        quote: String,
+        inputs: Vec<Proof>,
         outputs: Option<Vec<BlindedMessage>>,
-    ) -> Result<MeltResponse, Error>;
+    ) -> Result<MeltBolt11Response, Error>;
 
     // REVIEW: Should be consistent aboue passing in the Request struct or the
     // compnatants and making it within the function. Here the struct is passed

+ 21 - 27
crates/cashu-sdk/src/mint.rs

@@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
 use cashu::dhke::{sign_message, verify_message};
 pub use cashu::error::mint::Error;
 use cashu::nuts::{
-    BlindedMessage, BlindedSignature, MeltRequest, MeltResponse, Proof, SplitRequest,
+    BlindedMessage, BlindedSignature, MeltBolt11Request, MeltBolt11Response, Proof, SplitRequest,
     SplitResponse, *,
 };
 #[cfg(feature = "nut07")]
@@ -13,6 +13,8 @@ use cashu::Amount;
 use serde::{Deserialize, Serialize};
 use tracing::{debug, info};
 
+use crate::types::Quote;
+
 pub struct Mint {
     //    pub pubkey: PublicKey
     secret: String,
@@ -21,6 +23,7 @@ pub struct Mint {
     pub spent_secrets: HashSet<Secret>,
     pub pending_secrets: HashSet<Secret>,
     pub fee_reserve: FeeReserve,
+    pub quotes: HashMap<String, Quote>,
 }
 
 impl Mint {
@@ -28,6 +31,7 @@ impl Mint {
         secret: &str,
         keysets_info: HashSet<MintKeySetInfo>,
         spent_secrets: HashSet<Secret>,
+        quotes: Vec<Quote>,
         min_fee_reserve: Amount,
         percent_fee_reserve: f32,
     ) -> Self {
@@ -36,6 +40,8 @@ impl Mint {
 
         let mut active_units: HashSet<String> = HashSet::default();
 
+        let quotes = quotes.into_iter().map(|q| (q.id.clone(), q)).collect();
+
         // Check that there is only one active keyset per unit
         for keyset_info in keysets_info {
             if keyset_info.active && !active_units.insert(keyset_info.unit.clone()) {
@@ -58,6 +64,7 @@ impl Mint {
         Self {
             secret: secret.to_string(),
             keysets,
+            quotes,
             keysets_info: info,
             spent_secrets,
             pending_secrets: HashSet::new(),
@@ -222,41 +229,28 @@ impl Mint {
         Ok(CheckSpendableResponse { spendable, pending })
     }
 
-    pub fn verify_melt_request(&mut self, melt_request: &MeltRequest) -> Result<(), Error> {
-        let proofs_total = melt_request.proofs_amount();
-
-        let percent_fee_reserve = Amount::from_sat(
-            (proofs_total.to_sat() as f32 * self.fee_reserve.percent_fee_reserve) as u64,
-        );
-
-        let fee_reserve = if percent_fee_reserve > self.fee_reserve.min_fee_reserve {
-            percent_fee_reserve
-        } else {
-            self.fee_reserve.min_fee_reserve
-        };
+    pub fn verify_melt_request(&mut self, melt_request: &MeltBolt11Request) -> Result<(), Error> {
+        let quote = self.quotes.get(&melt_request.quote).unwrap();
+        let proofs_total = melt_request.proofs_amount().to_sat();
 
-        let required_total = melt_request
-            .invoice_amount()
-            .map_err(|_| Error::InvoiceAmountUndefined)?
-            + fee_reserve;
+        let required_total = quote.amount + quote.fee_reserve;
 
         if proofs_total < required_total {
             debug!(
                 "Insufficient Proofs: Got: {}, Required: {}",
-                proofs_total.to_sat().to_string(),
-                required_total.to_sat().to_string()
+                proofs_total, required_total
             );
             return Err(Error::Amount);
         }
 
-        let secrets: HashSet<&Secret> = melt_request.proofs.iter().map(|p| &p.secret).collect();
+        let secrets: HashSet<&Secret> = melt_request.inputs.iter().map(|p| &p.secret).collect();
 
         // Ensure proofs are unique and not being double spent
-        if melt_request.proofs.len().ne(&secrets.len()) {
+        if melt_request.inputs.len().ne(&secrets.len()) {
             return Err(Error::DuplicateProofs);
         }
 
-        for proof in &melt_request.proofs {
+        for proof in &melt_request.inputs {
             self.verify_proof(proof)?
         }
 
@@ -265,13 +259,13 @@ impl Mint {
 
     pub fn process_melt_request(
         &mut self,
-        melt_request: &MeltRequest,
+        melt_request: &MeltBolt11Request,
         preimage: &str,
         total_spent: Amount,
-    ) -> Result<MeltResponse, Error> {
+    ) -> Result<MeltBolt11Response, Error> {
         self.verify_melt_request(melt_request)?;
 
-        let secrets = Vec::with_capacity(melt_request.proofs.len());
+        let secrets = Vec::with_capacity(melt_request.inputs.len());
         for secret in secrets {
             self.spent_secrets.insert(secret);
         }
@@ -312,9 +306,9 @@ impl Mint {
             );
         }
 
-        Ok(MeltResponse {
+        Ok(MeltBolt11Response {
             paid: true,
-            preimage: Some(preimage.to_string()),
+            proof: preimage.to_string(),
             change,
         })
     }

+ 9 - 18
crates/cashu-sdk/src/wallet.rs

@@ -5,8 +5,8 @@ use cashu::dhke::{construct_proofs, unblind_message};
 #[cfg(feature = "nut07")]
 use cashu::nuts::nut00::mint;
 use cashu::nuts::{
-    BlindedSignature, Keys, PreMintSecrets, PreSplit, Proof, Proofs, RequestMintResponse,
-    SplitRequest, Token,
+    BlindedSignature, CurrencyUnit, Keys, PreMintSecrets, PreSplit, Proof, Proofs,
+    RequestMintResponse, SplitRequest, Token,
 };
 #[cfg(feature = "nut07")]
 use cashu::types::ProofsStatus;
@@ -95,12 +95,12 @@ impl<C: Client> Wallet<C> {
         &self,
         amount: Amount,
         hash: &str,
-        unit: Option<String>,
         memo: Option<String>,
+        unit: Option<CurrencyUnit>,
     ) -> Result<Token, Error> {
         let proofs = self.mint(amount, hash).await?;
 
-        let token = Token::new(self.mint_url.clone(), proofs, unit, memo);
+        let token = Token::new(self.mint_url.clone(), proofs, memo, unit);
         Ok(token?)
     }
 
@@ -127,15 +127,6 @@ impl<C: Client> Wallet<C> {
         Ok(proofs)
     }
 
-    /// Check fee
-    pub async fn check_fee(&self, invoice: Bolt11Invoice) -> Result<Amount, Error> {
-        Ok(self
-            .client
-            .post_check_fees(self.mint_url.clone().try_into()?, invoice)
-            .await?
-            .fee)
-    }
-
     /// Receive
     pub async fn receive(&self, encoded_token: &str) -> Result<Proofs, Error> {
         let token_data = Token::from_str(encoded_token)?;
@@ -300,7 +291,7 @@ impl<C: Client> Wallet<C> {
 
     pub async fn melt(
         &self,
-        invoice: Bolt11Invoice,
+        quote: String,
         proofs: Proofs,
         fee_reserve: Amount,
     ) -> Result<Melted, Error> {
@@ -309,8 +300,8 @@ impl<C: Client> Wallet<C> {
             .client
             .post_melt(
                 self.mint_url.clone().try_into()?,
+                quote,
                 proofs,
-                invoice,
                 Some(blinded.blinded_messages()),
             )
             .await?;
@@ -327,7 +318,7 @@ impl<C: Client> Wallet<C> {
 
         let melted = Melted {
             paid: true,
-            preimage: melt_response.preimage,
+            preimage: Some(melt_response.proof),
             change: change_proofs,
         };
 
@@ -337,10 +328,10 @@ impl<C: Client> Wallet<C> {
     pub fn proofs_to_token(
         &self,
         proofs: Proofs,
-        unit: Option<String>,
         memo: Option<String>,
+        unit: Option<CurrencyUnit>,
     ) -> Result<String, Error> {
-        Ok(Token::new(self.mint_url.clone(), proofs, unit, memo)?.to_string())
+        Ok(Token::new(self.mint_url.clone(), proofs, memo, unit)?.to_string())
     }
 }
 

+ 4 - 4
crates/cashu/src/nuts/mod.rs

@@ -13,7 +13,7 @@ pub mod nut09;
 
 #[cfg(feature = "wallet")]
 pub use nut00::wallet::{PreMint, PreMintSecrets, Token};
-pub use nut00::{BlindedMessage, BlindedSignature, Proof};
+pub use nut00::{BlindedMessage, BlindedSignature, CurrencyUnit, Proof};
 pub use nut01::{Keys, KeysResponse, PublicKey, SecretKey};
 pub use nut02::mint::KeySet as MintKeySet;
 pub use nut02::{Id, KeySet, KeySetInfo, KeysetResponse};
@@ -21,14 +21,14 @@ pub use nut02::{Id, KeySet, KeySetInfo, KeysetResponse};
 pub use nut03::PreSplit;
 pub use nut03::{RequestMintResponse, SplitRequest, SplitResponse};
 pub use nut04::{MintRequest, PostMintResponse};
-pub use nut05::{CheckFeesRequest, CheckFeesResponse};
 #[cfg(not(feature = "nut08"))]
-pub use nut05::{MeltRequest, MeltResponse};
+pub use nut05::{MeltBolt11Request, MeltBolt11Response};
+pub use nut05::{MeltQuoteBolt11Request, MeltQuoteBolt11Response};
 #[cfg(feature = "wallet")]
 #[cfg(feature = "nut07")]
 pub use nut07::{CheckSpendableRequest, CheckSpendableResponse};
 #[cfg(feature = "nut08")]
-pub use nut08::{MeltRequest, MeltResponse};
+pub use nut08::{MeltBolt11Request, MeltBolt11Response};
 #[cfg(feature = "nut09")]
 pub use nut09::MintInfo;
 

+ 35 - 3
crates/cashu/src/nuts/nut00.rs

@@ -1,9 +1,13 @@
 //! Notation and Models
 // https://github.com/cashubtc/nuts/blob/main/00.md
 
+use std::fmt;
+use std::str::FromStr;
+
 use serde::{Deserialize, Serialize};
 
 use super::{Id, Proofs, PublicKey};
+use crate::error::Error;
 use crate::secret::Secret;
 use crate::url::UncheckedUrl;
 use crate::Amount;
@@ -20,6 +24,33 @@ pub struct BlindedMessage {
     pub keyset_id: Id,
 }
 
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub enum CurrencyUnit {
+    #[default]
+    Sat,
+    Custom(String),
+}
+
+impl FromStr for CurrencyUnit {
+    type Err = Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "sat" => Ok(Self::Sat),
+            _ => Ok(Self::Custom(s.to_string())),
+        }
+    }
+}
+
+impl fmt::Display for CurrencyUnit {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            CurrencyUnit::Sat => write!(f, "sat"),
+            CurrencyUnit::Custom(unit) => write!(f, "{}", unit),
+        }
+    }
+}
+
 #[cfg(feature = "wallet")]
 pub mod wallet {
     use std::cmp::Ordering;
@@ -31,7 +62,7 @@ pub mod wallet {
     use serde::{Deserialize, Serialize};
     use url::Url;
 
-    use super::MintProofs;
+    use super::{CurrencyUnit, MintProofs};
     use crate::dhke::blind_message;
     use crate::error::wallet;
     use crate::nuts::{BlindedMessage, Id, Proofs, SecretKey};
@@ -201,8 +232,9 @@ pub mod wallet {
         /// Memo for token
         #[serde(skip_serializing_if = "Option::is_none")]
         pub memo: Option<String>,
+        /// Token Unit
         #[serde(skip_serializing_if = "Option::is_none")]
-        pub unit: Option<String>,
+        pub unit: Option<CurrencyUnit>,
     }
 
     impl Token {
@@ -210,7 +242,7 @@ pub mod wallet {
             mint_url: UncheckedUrl,
             proofs: Proofs,
             memo: Option<String>,
-            unit: Option<String>,
+            unit: Option<CurrencyUnit>,
         ) -> Result<Self, wallet::Error> {
             if proofs.is_empty() {
                 return Err(wallet::Error::ProofsRequired);

+ 31 - 25
crates/cashu/src/nuts/nut05.rs

@@ -3,48 +3,54 @@
 
 use serde::{Deserialize, Serialize};
 
-use crate::error::Error;
+use super::CurrencyUnit;
 use crate::nuts::Proofs;
 use crate::{Amount, Bolt11Invoice};
 
-/// Check Fees Response [NUT-05]
+/// Melt quote request [NUT-05]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct CheckFeesResponse {
-    /// Expected Mac Fee in satoshis    
-    pub fee: Amount,
+pub struct MeltQuoteBolt11Request {
+    /// Bolt11 invoice to be paid
+    pub request: Bolt11Invoice,
+    /// Unit wallet would like to pay with
+    pub unit: CurrencyUnit,
 }
 
-/// Check Fees request [NUT-05]
+/// Melt quote response [NUT-05]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct CheckFeesRequest {
-    /// Lighting Invoice
-    pub pr: Bolt11Invoice,
+pub struct MeltQuoteBolt11Response {
+    /// Quote Id
+    pub quote: String,
+    /// The amount that needs to be provided
+    pub amount: u64,
+    /// The fee reserve that is required
+    pub fee_reserve: u64,
+    /// Whether the the request haas be paid
+    pub paid: bool,
+    /// Unix timestamp until the quote is valid
+    pub expiry: u64,
 }
 
-/// Melt Request [NUT-05]
+/// Melt Bolt11 Request [NUT-05]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MeltRequest {
-    pub proofs: Proofs,
-    /// bollt11
-    pub pr: Bolt11Invoice,
+pub struct MeltBolt11Request {
+    /// Quote ID
+    pub quote: String,
+    /// Proofs
+    pub inputs: Proofs,
 }
 
-impl MeltRequest {
+impl MeltBolt11Request {
     pub fn proofs_amount(&self) -> Amount {
-        self.proofs.iter().map(|proof| proof.amount).sum()
-    }
-
-    pub fn invoice_amount(&self) -> Result<Amount, Error> {
-        match self.pr.amount_milli_satoshis() {
-            Some(value) => Ok(Amount::from_sat(value)),
-            None => Err(Error::InvoiceAmountUndefined),
-        }
+        self.inputs.iter().map(|proof| proof.amount).sum()
     }
 }
 
 /// Melt Response [NUT-05]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MeltResponse {
+pub struct MeltBolt11Response {
+    /// Indicate if payment was successful
     pub paid: bool,
-    pub preimage: Option<String>,
+    /// Bolt11 preimage
+    pub payment_preimage: String,
 }

+ 20 - 22
crates/cashu/src/nuts/nut08.rs

@@ -1,51 +1,49 @@
 //! Lightning fee return
 // https://github.com/cashubtc/nuts/blob/main/08.md
 
-use lightning_invoice::Bolt11Invoice;
 use serde::{Deserialize, Serialize};
 
 use super::{BlindedMessage, BlindedSignature, Proofs};
-use crate::error::Error;
 use crate::Amount;
 
-/// Melt Request [NUT-08]
+/// Melt Bolt11 Request [NUT-08]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MeltRequest {
-    pub proofs: Proofs,
-    /// bollt11
-    pub pr: Bolt11Invoice,
+pub struct MeltBolt11Request {
+    /// Quote ID
+    pub quote: String,
+    /// Proofs
+    pub inputs: Proofs,
     /// Blinded Message that can be used to return change [NUT-08]
     /// Amount field of blindedMessages `SHOULD` be set to zero
     pub outputs: Option<Vec<BlindedMessage>>,
 }
 
-impl MeltRequest {
+impl MeltBolt11Request {
     pub fn proofs_amount(&self) -> Amount {
-        self.proofs.iter().map(|proof| proof.amount).sum()
+        self.inputs.iter().map(|proof| proof.amount).sum()
     }
 
-    pub fn invoice_amount(&self) -> Result<Amount, Error> {
-        match self.pr.amount_milli_satoshis() {
-            Some(value) => Ok(Amount::from_msat(value)),
-            None => Err(Error::InvoiceAmountUndefined),
-        }
+    pub fn output_amount(&self) -> Option<Amount> {
+        self.outputs
+            .as_ref()
+            .map(|o| o.iter().map(|proof| proof.amount).sum())
     }
 }
 
 /// Melt Response [NUT-08]
 /// Lightning fee return [NUT-08] if change is defined
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct MeltResponse {
+pub struct MeltBolt11Response {
     pub paid: bool,
-    pub preimage: Option<String>,
+    // REVIEW: https://github.com/cashubtc/nuts/pull/55#discussion_r1419991818
+    pub proof: String,
     pub change: Option<Vec<BlindedSignature>>,
 }
 
-impl MeltResponse {
-    pub fn change_amount(&self) -> Amount {
-        match &self.change {
-            Some(change) => change.iter().map(|c| c.amount).sum(),
-            None => Amount::ZERO,
-        }
+impl MeltBolt11Response {
+    pub fn change_amount(&self) -> Option<Amount> {
+        self.change
+            .as_ref()
+            .map(|c| c.iter().map(|b| b.amount).sum())
     }
 }

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

@@ -2,7 +2,8 @@
 
 use serde::{Deserialize, Serialize};
 
-use crate::nuts::{Id, KeySetInfo, Proofs};
+use crate::nuts::{CurrencyUnit, Proofs};
+use crate::Bolt11Invoice;
 
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct ProofsStatus {
@@ -32,3 +33,15 @@ pub enum InvoiceStatus {
     Expired,
     InFlight,
 }
+
+/// Quote
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+pub struct Quote {
+    pub id: String,
+    pub amount: u64,
+    pub request: Bolt11Invoice,
+    pub unit: CurrencyUnit,
+    pub fee_reserve: u64,
+    pub paid: bool,
+    pub expiry: u64,
+}