wallet.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. use std::ops::Deref;
  2. use std::str::FromStr;
  3. use std::sync::Arc;
  4. use cdk::nuts::{Proofs, SigningKey};
  5. use cdk::url::UncheckedUrl;
  6. use cdk::wallet::Wallet;
  7. use cdk::{Amount, HttpClient};
  8. use cdk_rexie::RexieWalletDatabase;
  9. use wasm_bindgen::prelude::*;
  10. use crate::error::{into_err, Result};
  11. use crate::nuts::nut11::JsP2PKSpendingConditions;
  12. use crate::nuts::nut14::JsHTLCSpendingConditions;
  13. use crate::nuts::{JsCurrencyUnit, JsMintInfo, JsProof};
  14. use crate::types::melt_quote::JsMeltQuote;
  15. use crate::types::{JsAmount, JsMelted, JsMintQuote};
  16. #[wasm_bindgen(js_name = Wallet)]
  17. pub struct JsWallet {
  18. inner: Wallet,
  19. }
  20. impl Deref for JsWallet {
  21. type Target = Wallet;
  22. fn deref(&self) -> &Self::Target {
  23. &self.inner
  24. }
  25. }
  26. impl From<Wallet> for JsWallet {
  27. fn from(inner: Wallet) -> JsWallet {
  28. JsWallet { inner }
  29. }
  30. }
  31. #[wasm_bindgen(js_class = Wallet)]
  32. impl JsWallet {
  33. #[wasm_bindgen(constructor)]
  34. pub async fn new() -> Self {
  35. let client = HttpClient::new();
  36. let db = RexieWalletDatabase::new().await.unwrap();
  37. Wallet::new(client, Arc::new(db), None).await.into()
  38. }
  39. #[wasm_bindgen(js_name = totalBalance)]
  40. pub async fn total_balance(&self) -> Result<JsAmount> {
  41. Ok(self.inner.total_balance().await.map_err(into_err)?.into())
  42. }
  43. #[wasm_bindgen(js_name = mintBalances)]
  44. pub async fn mint_balances(&self) -> Result<JsValue> {
  45. let mint_balances = self.inner.mint_balances().await.map_err(into_err)?;
  46. Ok(serde_wasm_bindgen::to_value(&mint_balances)?)
  47. }
  48. #[wasm_bindgen(js_name = addMint)]
  49. pub async fn add_mint(&self, mint_url: String) -> Result<Option<JsMintInfo>> {
  50. let mint_url = UncheckedUrl::from_str(&mint_url).map_err(into_err)?;
  51. Ok(self
  52. .inner
  53. .add_mint(mint_url)
  54. .await
  55. .map_err(into_err)?
  56. .map(|i| i.into()))
  57. }
  58. #[wasm_bindgen(js_name = refreshMint)]
  59. pub async fn refresh_mint_keys(&self, mint_url: String) -> Result<()> {
  60. let mint_url = UncheckedUrl::from_str(&mint_url).map_err(into_err)?;
  61. self.inner
  62. .refresh_mint_keys(&mint_url)
  63. .await
  64. .map_err(into_err)?;
  65. Ok(())
  66. }
  67. #[wasm_bindgen(js_name = mintQuote)]
  68. pub async fn mint_quote(
  69. &mut self,
  70. mint_url: String,
  71. amount: u64,
  72. unit: JsCurrencyUnit,
  73. ) -> Result<JsMintQuote> {
  74. let mint_url = UncheckedUrl::from_str(&mint_url).map_err(into_err)?;
  75. let quote = self
  76. .inner
  77. .mint_quote(mint_url, amount.into(), unit.into())
  78. .await
  79. .map_err(into_err)?;
  80. Ok(quote.into())
  81. }
  82. #[wasm_bindgen(js_name = mint)]
  83. pub async fn mint(&mut self, mint_url: String, quote_id: String) -> Result<JsAmount> {
  84. let mint_url = UncheckedUrl::from_str(&mint_url).map_err(into_err)?;
  85. Ok(self
  86. .inner
  87. .mint(mint_url, &quote_id)
  88. .await
  89. .map_err(into_err)?
  90. .into())
  91. }
  92. #[wasm_bindgen(js_name = meltQuote)]
  93. pub async fn melt_quote(
  94. &mut self,
  95. mint_url: String,
  96. unit: JsCurrencyUnit,
  97. request: String,
  98. ) -> Result<JsMeltQuote> {
  99. let mint_url = UncheckedUrl::from_str(&mint_url).map_err(into_err)?;
  100. let melt_quote = self
  101. .inner
  102. .melt_quote(mint_url, unit.into(), request)
  103. .await
  104. .map_err(into_err)?;
  105. Ok(melt_quote.into())
  106. }
  107. #[wasm_bindgen(js_name = melt)]
  108. pub async fn melt(&mut self, mint_url: String, quote_id: String) -> Result<JsMelted> {
  109. let mint_url = UncheckedUrl::from_str(&mint_url).map_err(into_err)?;
  110. let melted = self
  111. .inner
  112. .melt(&mint_url, &quote_id)
  113. .await
  114. .map_err(into_err)?;
  115. Ok(melted.into())
  116. }
  117. #[wasm_bindgen(js_name = receive)]
  118. pub async fn receive(
  119. &mut self,
  120. encoded_token: String,
  121. signing_keys: JsValue,
  122. preimages: JsValue,
  123. ) -> Result<()> {
  124. let signing_keys: Option<Vec<SigningKey>> = serde_wasm_bindgen::from_value(signing_keys)?;
  125. let preimages: Option<Vec<String>> = serde_wasm_bindgen::from_value(preimages)?;
  126. self.inner
  127. .receive(&encoded_token, signing_keys, preimages)
  128. .await
  129. .map_err(into_err)?;
  130. Ok(())
  131. }
  132. #[wasm_bindgen(js_name = send)]
  133. pub async fn send(
  134. &mut self,
  135. mint_url: String,
  136. unit: JsCurrencyUnit,
  137. memo: Option<String>,
  138. amount: u64,
  139. p2pk_condition: Option<JsP2PKSpendingConditions>,
  140. htlc_condition: Option<JsHTLCSpendingConditions>,
  141. ) -> Result<String> {
  142. let conditions = match (p2pk_condition, htlc_condition) {
  143. (Some(_), Some(_)) => {
  144. return Err(JsValue::from_str(
  145. "Cannot define both p2pk and htlc conditions",
  146. ));
  147. }
  148. (None, Some(htlc_condition)) => Some(htlc_condition.deref().clone()),
  149. (Some(p2pk_condition), None) => Some(p2pk_condition.deref().clone()),
  150. (None, None) => None,
  151. };
  152. let mint_url = UncheckedUrl::from_str(&mint_url).map_err(into_err)?;
  153. self.inner
  154. .send(
  155. &mint_url,
  156. &unit.into(),
  157. memo,
  158. Amount::from(amount),
  159. conditions,
  160. )
  161. .await
  162. .map_err(into_err)
  163. }
  164. #[wasm_bindgen(js_name = swap)]
  165. pub async fn swap(
  166. &mut self,
  167. mint_url: String,
  168. unit: JsCurrencyUnit,
  169. amount: u64,
  170. input_proofs: Vec<JsProof>,
  171. p2pk_condition: Option<JsP2PKSpendingConditions>,
  172. htlc_condition: Option<JsHTLCSpendingConditions>,
  173. ) -> Result<JsValue> {
  174. let conditions = match (p2pk_condition, htlc_condition) {
  175. (Some(_), Some(_)) => {
  176. return Err(JsValue::from_str(
  177. "Cannot define both p2pk and htlc conditions",
  178. ));
  179. }
  180. (None, Some(htlc_condition)) => Some(htlc_condition.deref().clone()),
  181. (Some(p2pk_condition), None) => Some(p2pk_condition.deref().clone()),
  182. (None, None) => None,
  183. };
  184. let mint_url = UncheckedUrl::from_str(&mint_url).map_err(into_err)?;
  185. let proofs: Proofs = input_proofs.iter().map(|p| p.deref()).cloned().collect();
  186. let post_swap_proofs = self
  187. .inner
  188. .swap(
  189. &mint_url,
  190. &unit.into(),
  191. Some(Amount::from(amount)),
  192. proofs,
  193. conditions,
  194. )
  195. .await
  196. .map_err(into_err)?;
  197. Ok(serde_wasm_bindgen::to_value(&post_swap_proofs)?)
  198. }
  199. }