wallet.rs 8.7 KB

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