diff --git a/bindings/cashu-ffi/src/nuts/nut08/mod.rs b/bindings/cashu-ffi/src/nuts/nut08/mod.rs index 21d964d4..7bdd33ec 100644 --- a/bindings/cashu-ffi/src/nuts/nut08/mod.rs +++ b/bindings/cashu-ffi/src/nuts/nut08/mod.rs @@ -62,6 +62,25 @@ pub struct MeltResponse { inner: MeltResponseSdk, } +impl Deref for MeltResponse { + type Target = MeltResponseSdk; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for MeltResponse { + fn from(inner: cashu::nuts::nut08::MeltResponse) -> MeltResponse { + MeltResponse { inner } + } +} + +impl From for cashu::nuts::nut08::MeltResponse { + fn from(res: MeltResponse) -> cashu::nuts::nut08::MeltResponse { + res.inner + } +} + impl MeltResponse { pub fn new( paid: bool, @@ -93,15 +112,3 @@ impl MeltResponse { .map(|change| change.into_iter().map(|bs| Arc::new(bs.into())).collect()) } } - -impl From for MeltResponse { - fn from(inner: cashu::nuts::nut08::MeltResponse) -> MeltResponse { - MeltResponse { inner } - } -} - -impl From for cashu::nuts::nut08::MeltResponse { - fn from(res: MeltResponse) -> cashu::nuts::nut08::MeltResponse { - res.inner - } -} diff --git a/bindings/cashu-js/Cargo.toml b/bindings/cashu-js/Cargo.toml index c59f3b7c..96c1c428 100644 --- a/bindings/cashu-js/Cargo.toml +++ b/bindings/cashu-js/Cargo.toml @@ -15,7 +15,8 @@ cashu = { path = "../../crates/cashu"} js-sys = "0.3.64" serde-wasm-bindgen = "0.6.0" serde_json.workspace = true -wasm-bindgen = "0.2.87" +serde.workspace = true +wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] } wasm-bindgen-futures = "0.4.37" [package.metadata.wasm-pack.profile.release] diff --git a/bindings/cashu-js/src/nuts/mod.rs b/bindings/cashu-js/src/nuts/mod.rs index 8c044bd4..0bea4856 100644 --- a/bindings/cashu-js/src/nuts/mod.rs +++ b/bindings/cashu-js/src/nuts/mod.rs @@ -5,3 +5,5 @@ mod nut03; mod nut04; mod nut05; mod nut06; +mod nut07; +mod nut08; diff --git a/bindings/cashu-js/src/nuts/nut07.rs b/bindings/cashu-js/src/nuts/nut07.rs new file mode 100644 index 00000000..1e91bc19 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut07.rs @@ -0,0 +1,107 @@ +use std::ops::Deref; + +use cashu::nuts::nut07::{CheckSpendableRequest, CheckSpendableResponse}; +use wasm_bindgen::prelude::*; + +use crate::error::{into_err, Result}; + +#[wasm_bindgen(js_name = CheckSpendableRequest)] +pub struct JsCheckSpendableRequest { + inner: CheckSpendableRequest, +} + +impl Deref for JsCheckSpendableRequest { + type Target = CheckSpendableRequest; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsCheckSpendableRequest { + fn from(inner: CheckSpendableRequest) -> JsCheckSpendableRequest { + JsCheckSpendableRequest { inner } + } +} + +#[wasm_bindgen(js_class = CheckSpendable)] +impl JsCheckSpendableRequest { + // REVIEW: Use into serde + #[wasm_bindgen(constructor)] + pub fn new(proofs: String) -> Result { + let proofs = serde_json::from_str(&proofs).map_err(into_err)?; + + Ok(JsCheckSpendableRequest { + inner: CheckSpendableRequest { proofs }, + }) + } + + /// Get Proofs + #[wasm_bindgen(getter)] + // REVIEW: INTO Serde + pub fn proofs(&self) -> Result { + Ok(serde_json::to_string(&self.inner.proofs).map_err(into_err)?) + } +} + +#[wasm_bindgen(js_name = CheckSpendableResponse)] +pub struct JsCheckSpendableResponse { + inner: CheckSpendableResponse, +} + +impl Deref for JsCheckSpendableResponse { + type Target = CheckSpendableResponse; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsCheckSpendableResponse { + fn from(inner: CheckSpendableResponse) -> JsCheckSpendableResponse { + JsCheckSpendableResponse { inner } + } +} + +#[wasm_bindgen(js_class = CheckSpendableResponse)] +impl JsCheckSpendableResponse { + #[wasm_bindgen(constructor)] + pub fn new( + js_spendable: Box<[JsValue]>, + js_pending: Box<[JsValue]>, + ) -> Result { + let spendable: Vec = js_spendable.iter().flat_map(|s| s.as_bool()).collect(); + + if spendable.len().ne(&js_spendable.len()) { + return Err(JsValue::from_str("Wrong value")); + } + + let pending: Vec = js_pending.iter().flat_map(|p| p.as_bool()).collect(); + + if pending.len().ne(&js_pending.len()) { + return Err(JsValue::from_str("Wrong value")); + } + + Ok(JsCheckSpendableResponse { + inner: CheckSpendableResponse { spendable, pending }, + }) + } + + /// Get Pending + #[wasm_bindgen(getter)] + pub fn pending(&self) -> Box<[JsValue]> { + self.inner + .pending + .iter() + .map(|p| JsValue::from_bool(*p)) + .collect() + } + + /// Get Spendable + #[wasm_bindgen(getter)] + pub fn spendable(&self) -> Box<[JsValue]> { + self.inner + .spendable + .iter() + .map(|s| JsValue::from_bool(*s)) + .collect() + } +} diff --git a/bindings/cashu-js/src/nuts/nut08.rs b/bindings/cashu-js/src/nuts/nut08.rs new file mode 100644 index 00000000..c2cd2e81 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut08.rs @@ -0,0 +1,135 @@ +use std::ops::Deref; + +use cashu::nuts::{ + nut00::{BlindedMessage, BlindedSignature, Proof}, + nut08::{MeltRequest, MeltResponse}, +}; +use wasm_bindgen::prelude::*; + +use crate::{ + error::{into_err, Result}, + types::{JsAmount, JsBolt11Invoice}, +}; + +#[wasm_bindgen(js_name = MeltRequest)] +pub struct JsMeltRequest { + inner: MeltRequest, +} + +impl Deref for JsMeltRequest { + type Target = MeltRequest; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsMeltRequest { + fn from(inner: MeltRequest) -> JsMeltRequest { + JsMeltRequest { inner } + } +} + +#[wasm_bindgen(js_class = MeltRequest)] +impl JsMeltRequest { + #[wasm_bindgen(constructor)] + pub fn new( + proofs: JsValue, + invoice: JsBolt11Invoice, + outputs: JsValue, + ) -> Result { + let proofs: Vec = serde_wasm_bindgen::from_value(proofs).map_err(into_err)?; + let outputs: Option> = if !outputs.is_null() { + Some(serde_wasm_bindgen::from_value(outputs).map_err(into_err)?) + } else { + None + }; + + Ok(JsMeltRequest { + inner: MeltRequest { + proofs, + pr: invoice.deref().clone(), + outputs, + }, + }) + } + + /// Get Proofs + #[wasm_bindgen(getter)] + pub fn proofs(&self) -> Result { + Ok(serde_wasm_bindgen::to_value(&self.inner.proofs).map_err(into_err)?) + } + + /// Get Invoice + #[wasm_bindgen(getter)] + pub fn invoice(&self) -> JsBolt11Invoice { + self.inner.pr.clone().into() + } + + /// Get outputs + #[wasm_bindgen(getter)] + pub fn outputs(&self) -> Result { + Ok(serde_wasm_bindgen::to_value(&self.inner.outputs).map_err(into_err)?) + } +} + +#[wasm_bindgen(js_name = MeltResponse)] +pub struct JsMeltResponse { + inner: MeltResponse, +} + +impl Deref for JsMeltResponse { + type Target = MeltResponse; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsMeltResponse { + fn from(inner: MeltResponse) -> JsMeltResponse { + JsMeltResponse { inner } + } +} + +#[wasm_bindgen(js_class = MeltResponse)] +impl JsMeltResponse { + #[wasm_bindgen(constructor)] + pub fn new(paid: bool, preimage: Option, change: JsValue) -> Result { + let change: Option> = if change.is_null() { + Some(serde_wasm_bindgen::from_value(change).map_err(into_err)?) + } else { + None + }; + + Ok(JsMeltResponse { + inner: MeltResponse { + paid, + preimage, + change, + }, + }) + } + + /// Get Paid + #[wasm_bindgen(getter)] + pub fn paid(&self) -> bool { + self.inner.paid + } + + /// Get Preimage + #[wasm_bindgen(getter)] + pub fn preimage(&self) -> Option { + self.inner.preimage.clone() + } + + /// Get Change + #[wasm_bindgen(getter)] + pub fn change(&self) -> Result { + Ok(serde_wasm_bindgen::to_value(&self.inner.change).map_err(into_err)?) + } + + /// Change Amount + #[wasm_bindgen(js_name = "cahngeAmount")] + pub fn change_amount(&self) -> JsAmount { + self.inner.change_amount().into() + } +}