From e830cbf0b7be0b528cf25336aeecd817656bab19 Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sun, 24 Sep 2023 09:19:50 +0100 Subject: [PATCH] `bindings/cashu-js` nut00 and nu01 --- Cargo.toml | 3 +- bindings/cashu-js/Cargo.toml | 22 ++++++ bindings/cashu-js/src/error.rs | 12 ++++ bindings/cashu-js/src/lib.rs | 3 + bindings/cashu-js/src/nuts/mod.rs | 3 + .../src/nuts/nut00/blinded_message.rs | 51 ++++++++++++++ .../src/nuts/nut00/blinded_messages.rs | 39 +++++++++++ .../src/nuts/nut00/blinded_signature.rs | 33 +++++++++ bindings/cashu-js/src/nuts/nut00/mod.rs | 10 +++ bindings/cashu-js/src/nuts/nut00/proof.rs | 63 +++++++++++++++++ bindings/cashu-js/src/nuts/nut00/token.rs | 59 ++++++++++++++++ bindings/cashu-js/src/nuts/nut01/key_pair.rs | 47 +++++++++++++ bindings/cashu-js/src/nuts/nut01/keys.rs | 54 +++++++++++++++ bindings/cashu-js/src/nuts/nut01/mod.rs | 9 +++ .../cashu-js/src/nuts/nut01/public_key.rs | 41 +++++++++++ .../cashu-js/src/nuts/nut01/secret_key.rs | 31 +++++++++ bindings/cashu-js/src/nuts/nut02/id.rs | 41 +++++++++++ bindings/cashu-js/src/nuts/nut02/mod.rs | 3 + bindings/cashu-js/src/types/amount.rs | 68 +++++++++++++++++++ bindings/cashu-js/src/types/bolt11_invoice.rs | 49 +++++++++++++ bindings/cashu-js/src/types/mod.rs | 7 ++ bindings/cashu-js/src/types/proofs_status.rs | 1 + bindings/cashu-js/src/types/secret.rs | 38 +++++++++++ crates/cashu/src/nuts/nut02.rs | 2 + 24 files changed, 688 insertions(+), 1 deletion(-) create mode 100644 bindings/cashu-js/Cargo.toml create mode 100644 bindings/cashu-js/src/error.rs create mode 100644 bindings/cashu-js/src/lib.rs create mode 100644 bindings/cashu-js/src/nuts/mod.rs create mode 100644 bindings/cashu-js/src/nuts/nut00/blinded_message.rs create mode 100644 bindings/cashu-js/src/nuts/nut00/blinded_messages.rs create mode 100644 bindings/cashu-js/src/nuts/nut00/blinded_signature.rs create mode 100644 bindings/cashu-js/src/nuts/nut00/mod.rs create mode 100644 bindings/cashu-js/src/nuts/nut00/proof.rs create mode 100644 bindings/cashu-js/src/nuts/nut00/token.rs create mode 100644 bindings/cashu-js/src/nuts/nut01/key_pair.rs create mode 100644 bindings/cashu-js/src/nuts/nut01/keys.rs create mode 100644 bindings/cashu-js/src/nuts/nut01/mod.rs create mode 100644 bindings/cashu-js/src/nuts/nut01/public_key.rs create mode 100644 bindings/cashu-js/src/nuts/nut01/secret_key.rs create mode 100644 bindings/cashu-js/src/nuts/nut02/id.rs create mode 100644 bindings/cashu-js/src/nuts/nut02/mod.rs create mode 100644 bindings/cashu-js/src/types/amount.rs create mode 100644 bindings/cashu-js/src/types/bolt11_invoice.rs create mode 100644 bindings/cashu-js/src/types/mod.rs create mode 100644 bindings/cashu-js/src/types/proofs_status.rs create mode 100644 bindings/cashu-js/src/types/secret.rs diff --git a/Cargo.toml b/Cargo.toml index bd6aa1e5..2d8b4084 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,8 @@ members = [ "crates/cashu-sdk", "bindings/uniffi-bindgen", "bindings/cashu-ffi", - "bindings/cashu-sdk-ffi" + "bindings/cashu-sdk-ffi", + "bindings/cashu-js" ] resolver = "2" diff --git a/bindings/cashu-js/Cargo.toml b/bindings/cashu-js/Cargo.toml new file mode 100644 index 00000000..c59f3b7c --- /dev/null +++ b/bindings/cashu-js/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "cashu-js" +version = "0.1.0" +edition = "2021" +description = "Cashu rust implementation, for JavaScript" +authors = ["thesimplekid "] +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["lib", "cdylib"] + +[dependencies] +cashu = { path = "../../crates/cashu"} +js-sys = "0.3.64" +serde-wasm-bindgen = "0.6.0" +serde_json.workspace = true +wasm-bindgen = "0.2.87" +wasm-bindgen-futures = "0.4.37" + +[package.metadata.wasm-pack.profile.release] +wasm-opt = true diff --git a/bindings/cashu-js/src/error.rs b/bindings/cashu-js/src/error.rs new file mode 100644 index 00000000..019f9a3a --- /dev/null +++ b/bindings/cashu-js/src/error.rs @@ -0,0 +1,12 @@ +use wasm_bindgen::JsValue; + +pub type Result = std::result::Result; + +/// Helper to replace the `E` to `Error` to `napi::Error` conversion. +#[inline] +pub fn into_err(error: E) -> JsValue +where + E: std::error::Error, +{ + JsValue::from_str(&error.to_string()) +} diff --git a/bindings/cashu-js/src/lib.rs b/bindings/cashu-js/src/lib.rs new file mode 100644 index 00000000..e5ab5c44 --- /dev/null +++ b/bindings/cashu-js/src/lib.rs @@ -0,0 +1,3 @@ +pub mod error; +mod nuts; +pub mod types; diff --git a/bindings/cashu-js/src/nuts/mod.rs b/bindings/cashu-js/src/nuts/mod.rs new file mode 100644 index 00000000..6a251035 --- /dev/null +++ b/bindings/cashu-js/src/nuts/mod.rs @@ -0,0 +1,3 @@ +mod nut00; +mod nut01; +mod nut02; diff --git a/bindings/cashu-js/src/nuts/nut00/blinded_message.rs b/bindings/cashu-js/src/nuts/nut00/blinded_message.rs new file mode 100644 index 00000000..d9f50c58 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut00/blinded_message.rs @@ -0,0 +1,51 @@ +use std::ops::Deref; + +use cashu::nuts::nut00::BlindedMessage; +use wasm_bindgen::prelude::*; + +use crate::nuts::nut01::JsPublicKey; +use crate::types::amount::JsAmount; + +#[wasm_bindgen(js_name = BlindedMessage)] +pub struct JsBlindedMessage { + inner: BlindedMessage, +} + +impl Deref for JsBlindedMessage { + type Target = BlindedMessage; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsBlindedMessage { + fn from(inner: BlindedMessage) -> JsBlindedMessage { + JsBlindedMessage { inner } + } +} + +#[wasm_bindgen(js_class = BlindedMessage)] +impl JsBlindedMessage { + #[allow(clippy::new_without_default)] + #[wasm_bindgen(constructor)] + pub fn new(amount: JsAmount, b: JsPublicKey) -> Self { + Self { + inner: BlindedMessage { + amount: amount.deref().clone(), + b: b.deref().clone(), + }, + } + } + + /// Amount + #[wasm_bindgen(getter)] + pub fn amount(&self) -> JsAmount { + self.inner.amount.into() + } + + /// B + #[wasm_bindgen(getter)] + pub fn b(&self) -> JsPublicKey { + self.inner.b.clone().into() + } +} diff --git a/bindings/cashu-js/src/nuts/nut00/blinded_messages.rs b/bindings/cashu-js/src/nuts/nut00/blinded_messages.rs new file mode 100644 index 00000000..55a9bfac --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut00/blinded_messages.rs @@ -0,0 +1,39 @@ +use std::ops::Deref; + +use wasm_bindgen::prelude::*; + +use cashu::nuts::nut00::wallet::BlindedMessages; + +use crate::error::{into_err, Result}; +use crate::types::JsAmount; + +#[wasm_bindgen(js_name = BlindedMessages)] +pub struct JsBlindedMessages { + inner: BlindedMessages, +} + +impl Deref for JsBlindedMessages { + type Target = BlindedMessages; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +#[wasm_bindgen(js_class = BlindedMessages)] +impl JsBlindedMessages { + #[wasm_bindgen(js_name = random)] + pub fn random(amount: JsAmount) -> Result { + Ok(JsBlindedMessages { + inner: BlindedMessages::random(*amount.deref()).map_err(into_err)?, + }) + } + + #[wasm_bindgen(js_name = blank)] + pub fn blank(fee_reserve: JsAmount) -> Result { + Ok(JsBlindedMessages { + inner: BlindedMessages::blank(*fee_reserve.deref()).map_err(into_err)?, + }) + } + + // TODO: Gettters +} diff --git a/bindings/cashu-js/src/nuts/nut00/blinded_signature.rs b/bindings/cashu-js/src/nuts/nut00/blinded_signature.rs new file mode 100644 index 00000000..9ca2c9b4 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut00/blinded_signature.rs @@ -0,0 +1,33 @@ +use std::ops::Deref; + +use cashu::nuts::nut00::BlindedSignature; +use wasm_bindgen::prelude::*; + +use crate::{nuts::nut01::JsPublicKey, nuts::nut02::JsId, types::JsAmount}; + +#[wasm_bindgen(js_name = BlindedSignature)] +pub struct JsBlindedSignature { + inner: BlindedSignature, +} + +impl Deref for JsBlindedSignature { + type Target = BlindedSignature; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +#[wasm_bindgen(js_class = BlindedSignature)] +impl JsBlindedSignature { + #[allow(clippy::new_without_default)] + #[wasm_bindgen(constructor)] + pub fn new(id: JsId, amount: JsAmount, c: JsPublicKey) -> Self { + Self { + inner: BlindedSignature { + id: id.deref().clone(), + amount: amount.deref().clone(), + c: c.deref().clone(), + }, + } + } +} diff --git a/bindings/cashu-js/src/nuts/nut00/mod.rs b/bindings/cashu-js/src/nuts/nut00/mod.rs new file mode 100644 index 00000000..4647b944 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut00/mod.rs @@ -0,0 +1,10 @@ +mod blinded_message; +mod blinded_messages; +mod blinded_signature; +mod proof; +mod token; + +pub use blinded_message::JsBlindedMessage; +pub use blinded_messages::JsBlindedMessages; +pub use blinded_signature::JsBlindedSignature; +pub use proof::JsProof; diff --git a/bindings/cashu-js/src/nuts/nut00/proof.rs b/bindings/cashu-js/src/nuts/nut00/proof.rs new file mode 100644 index 00000000..85a5a9d2 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut00/proof.rs @@ -0,0 +1,63 @@ +use std::ops::Deref; + +use cashu::nuts::nut00::Proof; +use wasm_bindgen::prelude::*; + +use crate::{nuts::nut01::JsPublicKey, nuts::nut02::JsId, types::JsAmount, types::JsSecret}; + +#[wasm_bindgen(js_name = Token)] +pub struct JsProof { + inner: Proof, +} + +impl Deref for JsProof { + type Target = Proof; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsProof { + fn from(inner: Proof) -> JsProof { + JsProof { inner } + } +} + +#[wasm_bindgen(js_class = Proof)] +impl JsProof { + #[wasm_bindgen(constructor)] + pub fn new(amount: JsAmount, secret: JsSecret, c: JsPublicKey, id: Option) -> JsProof { + Self { + inner: Proof { + amount: amount.deref().clone(), + secret: secret.deref().clone(), + c: c.deref().clone(), + id: id.map(|i| *i.deref()), + }, + } + } + + /// Amount + #[wasm_bindgen(getter)] + pub fn amount(&self) -> JsAmount { + self.inner.amount.into() + } + + /// Secret + #[wasm_bindgen(getter)] + pub fn secret(&self) -> JsSecret { + self.inner.secret.clone().into() + } + + /// C + #[wasm_bindgen(getter)] + pub fn c(&self) -> JsPublicKey { + self.inner.c.clone().into() + } + + /// Id + #[wasm_bindgen(getter)] + pub fn id(&self) -> Option { + self.inner.id.map(|id| id.into()) + } +} diff --git a/bindings/cashu-js/src/nuts/nut00/token.rs b/bindings/cashu-js/src/nuts/nut00/token.rs new file mode 100644 index 00000000..52b851ca --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut00/token.rs @@ -0,0 +1,59 @@ +use std::{ops::Deref, str::FromStr}; + +use cashu::{nuts::nut00::wallet::Token, url::UncheckedUrl}; +use wasm_bindgen::prelude::*; + +use crate::error::{into_err, Result}; + +#[wasm_bindgen(js_name = Token)] +pub struct JsToken { + inner: Token, +} + +impl Deref for JsToken { + type Target = Token; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsToken { + fn from(inner: Token) -> JsToken { + JsToken { inner } + } +} + +#[wasm_bindgen(js_class = Token)] +impl JsToken { + // TODO: Simply passing a string for proofs is not ideal + #[wasm_bindgen(constructor)] + pub fn new(mint: String, proofs: String, memo: Option) -> Result { + let mint = UncheckedUrl::from_str(&mint).map_err(into_err)?; + let proofs = serde_json::from_str(&proofs).map_err(into_err)?; + Ok(Self { + inner: Token::new(mint, proofs, memo).map_err(into_err)?, + }) + } + + /// Memo + #[wasm_bindgen(getter)] + pub fn memo(&self) -> Option { + self.inner.memo.clone() + } + + /// From String + #[wasm_bindgen(js_name = fromString)] + pub fn from_string(token: String) -> Result { + Ok(JsToken { + inner: Token::from_str(&token).map_err(into_err)?, + }) + } + + /// As String + #[wasm_bindgen(js_name = asString)] + pub fn as_string(&self) -> Result { + Ok(self.inner.convert_to_string().map_err(into_err)?) + } + + // TODO: Getter mint proofs +} diff --git a/bindings/cashu-js/src/nuts/nut01/key_pair.rs b/bindings/cashu-js/src/nuts/nut01/key_pair.rs new file mode 100644 index 00000000..76c9ff94 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut01/key_pair.rs @@ -0,0 +1,47 @@ +use std::ops::Deref; + +use cashu::nuts::nut01::mint::KeyPair; +use wasm_bindgen::prelude::*; + +use super::{JsPublicKey, JsSecretKey}; + +#[wasm_bindgen(js_name = KeyPair)] +pub struct JsKeyPair { + inner: KeyPair, +} + +impl Deref for JsKeyPair { + type Target = KeyPair; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsKeyPair { + fn from(inner: KeyPair) -> JsKeyPair { + JsKeyPair { inner } + } +} + +#[wasm_bindgen(js_class = KeyPair)] +impl JsKeyPair { + /// From Hex + #[wasm_bindgen(js_name = fromSecretKey)] + pub fn from_secret_key(secret_key: JsSecretKey) -> JsKeyPair { + Self { + inner: KeyPair::from_secret_key(secret_key.deref().clone()), + } + } + + /// Secret Key + #[wasm_bindgen(getter)] + pub fn secret_key(&self) -> JsSecretKey { + self.inner.secret_key.clone().into() + } + + /// Public Key + #[wasm_bindgen(getter)] + pub fn public_key(&self) -> JsPublicKey { + self.inner.public_key.clone().into() + } +} diff --git a/bindings/cashu-js/src/nuts/nut01/keys.rs b/bindings/cashu-js/src/nuts/nut01/keys.rs new file mode 100644 index 00000000..97e1026f --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut01/keys.rs @@ -0,0 +1,54 @@ +use std::ops::Deref; + +use cashu::nuts::nut01::Keys; +use wasm_bindgen::prelude::*; + +use crate::{ + error::{into_err, Result}, + types::JsAmount, +}; + +use super::JsPublicKey; + +#[wasm_bindgen(js_name = Keys)] +pub struct JsKeys { + inner: Keys, +} + +impl Deref for JsKeys { + type Target = Keys; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsKeys { + fn from(inner: Keys) -> JsKeys { + JsKeys { inner } + } +} + +#[wasm_bindgen(js_class = Keys)] +impl JsKeys { + /// From Hex + #[wasm_bindgen(constructor)] + pub fn new(keys: String) -> Result { + let keys = serde_json::from_str(&keys).map_err(into_err)?; + + Ok(JsKeys { + inner: Keys::new(keys), + }) + } + + /// Keys + #[wasm_bindgen(js_name = keys)] + pub fn keys(&self) -> Result { + Ok(serde_json::to_string(&self.inner.keys()).map_err(into_err)?) + } + + /// Amount Key + #[wasm_bindgen(js_name = amountKey)] + pub fn amount_key(&self, amount: JsAmount) -> Option { + self.inner.amount_key(*amount.deref()).map(|k| k.into()) + } +} diff --git a/bindings/cashu-js/src/nuts/nut01/mod.rs b/bindings/cashu-js/src/nuts/nut01/mod.rs new file mode 100644 index 00000000..facd8f32 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut01/mod.rs @@ -0,0 +1,9 @@ +mod key_pair; +mod keys; +mod public_key; +mod secret_key; + +pub use key_pair::JsKeyPair; +pub use keys::JsKeys; +pub use public_key::JsPublicKey; +pub use secret_key::JsSecretKey; diff --git a/bindings/cashu-js/src/nuts/nut01/public_key.rs b/bindings/cashu-js/src/nuts/nut01/public_key.rs new file mode 100644 index 00000000..3a41e808 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut01/public_key.rs @@ -0,0 +1,41 @@ +use std::ops::Deref; + +use cashu::nuts::nut01::PublicKey; +use wasm_bindgen::prelude::*; + +use crate::error::{into_err, Result}; + +#[wasm_bindgen(js_name = PublicKey)] +pub struct JsPublicKey { + inner: PublicKey, +} + +impl Deref for JsPublicKey { + type Target = PublicKey; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsPublicKey { + fn from(inner: PublicKey) -> JsPublicKey { + JsPublicKey { inner } + } +} + +#[wasm_bindgen(js_class = PublicKey)] +impl JsPublicKey { + /// From Hex + #[wasm_bindgen(js_name = fromHex)] + pub fn from_hex(hex: String) -> Result { + Ok(Self { + inner: PublicKey::from_hex(hex).map_err(into_err)?, + }) + } + + /// To Hex + #[wasm_bindgen(js_name = toHex)] + pub fn to_hex(&self) -> String { + self.inner.to_hex() + } +} diff --git a/bindings/cashu-js/src/nuts/nut01/secret_key.rs b/bindings/cashu-js/src/nuts/nut01/secret_key.rs new file mode 100644 index 00000000..8d453312 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut01/secret_key.rs @@ -0,0 +1,31 @@ +use std::ops::Deref; + +use cashu::nuts::nut01::SecretKey; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(js_name = PublicKey)] +pub struct JsSecretKey { + inner: SecretKey, +} + +impl Deref for JsSecretKey { + type Target = SecretKey; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsSecretKey { + fn from(inner: SecretKey) -> JsSecretKey { + JsSecretKey { inner } + } +} + +#[wasm_bindgen(js_class = SecretKey)] +impl JsSecretKey { + /// To Hex + #[wasm_bindgen(js_name = toHex)] + pub fn to_hex(&self) -> String { + self.inner.to_hex() + } +} diff --git a/bindings/cashu-js/src/nuts/nut02/id.rs b/bindings/cashu-js/src/nuts/nut02/id.rs new file mode 100644 index 00000000..d3ab3dcf --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut02/id.rs @@ -0,0 +1,41 @@ +use std::ops::Deref; + +use cashu::nuts::nut02::Id; +use wasm_bindgen::prelude::*; + +use crate::error::{into_err, Result}; + +#[wasm_bindgen(js_name = Id)] +pub struct JsId { + inner: Id, +} + +impl Deref for JsId { + type Target = Id; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsId { + fn from(inner: Id) -> JsId { + JsId { inner } + } +} + +#[wasm_bindgen(js_class = Id)] +impl JsId { + /// Try From Base 64 String + #[wasm_bindgen(js_name = tryFromBase64)] + pub fn try_from_base64(id: String) -> Result { + Ok(JsId { + inner: Id::try_from_base64(&id).map_err(into_err)?, + }) + } + + /// As String + #[wasm_bindgen(js_name = asString)] + pub fn as_string(&self) -> String { + self.inner.to_string() + } +} diff --git a/bindings/cashu-js/src/nuts/nut02/mod.rs b/bindings/cashu-js/src/nuts/nut02/mod.rs new file mode 100644 index 00000000..b9b76874 --- /dev/null +++ b/bindings/cashu-js/src/nuts/nut02/mod.rs @@ -0,0 +1,3 @@ +pub mod id; + +pub use id::JsId; diff --git a/bindings/cashu-js/src/types/amount.rs b/bindings/cashu-js/src/types/amount.rs new file mode 100644 index 00000000..41d61fed --- /dev/null +++ b/bindings/cashu-js/src/types/amount.rs @@ -0,0 +1,68 @@ +use std::ops::Deref; + +use cashu::Amount; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(js_name = Amount)] +pub struct JsAmount { + inner: Amount, +} + +impl Deref for JsAmount { + type Target = Amount; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsAmount { + fn from(inner: Amount) -> JsAmount { + JsAmount { inner } + } +} + +#[wasm_bindgen(js_class = Amount)] +impl JsAmount { + #[wasm_bindgen(constructor)] + pub fn new(sats: u64) -> Self { + Self { + inner: Amount::from_sat(sats), + } + } + + /// From Sats + #[wasm_bindgen(js_name = fromSat)] + pub fn from_sat(sats: u64) -> Self { + Self { + inner: Amount::from_sat(sats), + } + } + + /// From Msats + #[wasm_bindgen(js_name = fromMSat)] + pub fn from_msat(msats: u64) -> Self { + Self { + inner: Amount::from_msat(msats), + } + } + + /// Get as sats + #[wasm_bindgen(js_name = toSat)] + pub fn to_sat(&self) -> u64 { + self.inner.to_sat() + } + + /// Get as msats + #[wasm_bindgen(js_name = toMSat)] + pub fn to_msat(&self) -> u64 { + self.inner.to_msat() + } + + /// Split amount returns sat vec of sats + // REVIEW: https://github.com/rustwasm/wasm-bindgen/issues/111 + #[wasm_bindgen(js_name = split)] + pub fn split(&self) -> Vec { + let split = self.inner.split(); + split.into_iter().map(|a| a.to_sat()).collect() + } +} diff --git a/bindings/cashu-js/src/types/bolt11_invoice.rs b/bindings/cashu-js/src/types/bolt11_invoice.rs new file mode 100644 index 00000000..d587e27b --- /dev/null +++ b/bindings/cashu-js/src/types/bolt11_invoice.rs @@ -0,0 +1,49 @@ +use std::{ops::Deref, str::FromStr}; + +use cashu::Bolt11Invoice; +use wasm_bindgen::prelude::*; + +use crate::error::{into_err, Result}; +use crate::types::JsAmount; + +#[wasm_bindgen(js_name = Bolt11Invoice)] +pub struct JsBolt11Invoice { + inner: Bolt11Invoice, +} + +impl Deref for JsBolt11Invoice { + type Target = Bolt11Invoice; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsBolt11Invoice { + fn from(inner: Bolt11Invoice) -> JsBolt11Invoice { + JsBolt11Invoice { inner } + } +} + +#[wasm_bindgen(js_class = Bolt11Invoice)] +impl JsBolt11Invoice { + #[wasm_bindgen(constructor)] + pub fn new(invoice: String) -> Result { + Ok(JsBolt11Invoice { + inner: Bolt11Invoice::from_str(&invoice).map_err(into_err)?, + }) + } + + /// Amount + #[wasm_bindgen(getter)] + pub fn amount(&self) -> Option { + self.inner + .amount_milli_satoshis() + .map(|a| JsAmount::from_msat(a)) + } + + /// Invoice as string + #[wasm_bindgen(js_name = asString)] + pub fn as_string(&self) -> String { + self.inner.to_string() + } +} diff --git a/bindings/cashu-js/src/types/mod.rs b/bindings/cashu-js/src/types/mod.rs new file mode 100644 index 00000000..23bbf66d --- /dev/null +++ b/bindings/cashu-js/src/types/mod.rs @@ -0,0 +1,7 @@ +pub mod amount; +pub mod bolt11_invoice; +pub mod secret; + +pub use amount::JsAmount; +pub use bolt11_invoice::JsBolt11Invoice; +pub use secret::JsSecret; diff --git a/bindings/cashu-js/src/types/proofs_status.rs b/bindings/cashu-js/src/types/proofs_status.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/bindings/cashu-js/src/types/proofs_status.rs @@ -0,0 +1 @@ + diff --git a/bindings/cashu-js/src/types/secret.rs b/bindings/cashu-js/src/types/secret.rs new file mode 100644 index 00000000..33ebacb2 --- /dev/null +++ b/bindings/cashu-js/src/types/secret.rs @@ -0,0 +1,38 @@ +use std::ops::Deref; + +use cashu::secret::Secret; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(js_name = Secret)] +pub struct JsSecret { + inner: Secret, +} + +impl Deref for JsSecret { + type Target = Secret; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsSecret { + fn from(inner: Secret) -> JsSecret { + JsSecret { inner } + } +} + +#[wasm_bindgen(js_class = Secret)] +impl JsSecret { + #[wasm_bindgen(constructor)] + pub fn new() -> JsSecret { + Self { + inner: Secret::new(), + } + } + + /// As Bytes + #[wasm_bindgen(js_name = asBytes)] + pub fn as_bytes(&self) -> Vec { + self.inner.as_bytes().to_vec() + } +} diff --git a/crates/cashu/src/nuts/nut02.rs b/crates/cashu/src/nuts/nut02.rs index 6533d416..a1b5f2f8 100644 --- a/crates/cashu/src/nuts/nut02.rs +++ b/crates/cashu/src/nuts/nut02.rs @@ -18,6 +18,8 @@ pub enum Error { Length, } +impl std::error::Error for Error {} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self {