wallet.rs 9.7 KB

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