wallet.rs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. //! Wallet Js Bindings
  2. use std::ops::Deref;
  3. use std::sync::Arc;
  4. use cdk::amount::SplitTarget;
  5. use cdk::nuts::{Proofs, SecretKey};
  6. use cdk::wallet::{SendKind, Wallet};
  7. use cdk::Amount;
  8. use cdk_rexie::WalletRexieDatabase;
  9. use wasm_bindgen::prelude::*;
  10. use crate::error::{into_err, Result};
  11. use crate::nuts::nut01::JsSecretKey;
  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(mints_url: String, unit: JsCurrencyUnit, seed: Vec<u8>) -> Self {
  38. let db = WalletRexieDatabase::new().await.unwrap();
  39. Wallet::new(&mints_url, unit.into(), Arc::new(db), &seed, None).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<JsValue> {
  49. Ok(serde_wasm_bindgen::to_value(
  50. &self.inner.total_pending_balance().await.map_err(into_err)?,
  51. )?)
  52. }
  53. #[wasm_bindgen(js_name = checkAllPendingProofs)]
  54. pub async fn check_all_pending_proofs(&self) -> Result<JsAmount> {
  55. Ok(self
  56. .inner
  57. .check_all_pending_proofs()
  58. .await
  59. .map_err(into_err)?
  60. .into())
  61. }
  62. #[wasm_bindgen(js_name = getMintInfo)]
  63. pub async fn get_mint_info(&self) -> Result<Option<JsMintInfo>> {
  64. Ok(self
  65. .inner
  66. .get_mint_info()
  67. .await
  68. .map_err(into_err)?
  69. .map(|i| i.into()))
  70. }
  71. #[wasm_bindgen(js_name = mintQuote)]
  72. pub async fn mint_quote(&mut self, amount: u64) -> Result<JsMintQuote> {
  73. let quote = self
  74. .inner
  75. .mint_quote(amount.into())
  76. .await
  77. .map_err(into_err)?;
  78. Ok(quote.into())
  79. }
  80. #[wasm_bindgen(js_name = mintQuoteStatus)]
  81. pub async fn mint_quote_status(&self, quote_id: String) -> Result<JsMintQuoteBolt11Response> {
  82. let quote = self
  83. .inner
  84. .mint_quote_state(&quote_id)
  85. .await
  86. .map_err(into_err)?;
  87. Ok(quote.into())
  88. }
  89. #[wasm_bindgen(js_name = checkAllMintQuotes)]
  90. pub async fn check_all_mint_quotes(&self) -> Result<JsAmount> {
  91. let amount = self.inner.check_all_mint_quotes().await.map_err(into_err)?;
  92. Ok(amount.into())
  93. }
  94. #[wasm_bindgen(js_name = mint)]
  95. pub async fn mint(
  96. &mut self,
  97. quote_id: String,
  98. p2pk_condition: Option<JsP2PKSpendingConditions>,
  99. htlc_condition: Option<JsHTLCSpendingConditions>,
  100. split_target_amount: Option<JsAmount>,
  101. ) -> Result<JsAmount> {
  102. let target = split_target_amount
  103. .map(|a| SplitTarget::Value(*a.deref()))
  104. .unwrap_or_default();
  105. let conditions = match (p2pk_condition, htlc_condition) {
  106. (Some(_), Some(_)) => {
  107. return Err(JsValue::from_str(
  108. "Cannot define both p2pk and htlc conditions",
  109. ));
  110. }
  111. (None, Some(htlc_condition)) => Some(htlc_condition.deref().clone()),
  112. (Some(p2pk_condition), None) => Some(p2pk_condition.deref().clone()),
  113. (None, None) => None,
  114. };
  115. Ok(self
  116. .inner
  117. .mint(&quote_id, target, conditions)
  118. .await
  119. .map_err(into_err)?
  120. .into())
  121. }
  122. #[wasm_bindgen(js_name = meltQuote)]
  123. pub async fn melt_quote(
  124. &mut self,
  125. request: String,
  126. mpp_amount: Option<JsAmount>,
  127. ) -> Result<JsMeltQuote> {
  128. let melt_quote = self
  129. .inner
  130. .melt_quote(request, mpp_amount.map(|a| *a.deref()))
  131. .await
  132. .map_err(into_err)?;
  133. Ok(melt_quote.into())
  134. }
  135. #[wasm_bindgen(js_name = meltQuoteStatus)]
  136. pub async fn melt_quote_status(&self, quote_id: String) -> Result<JsMeltQuoteBolt11Response> {
  137. let quote = self
  138. .inner
  139. .melt_quote_status(&quote_id)
  140. .await
  141. .map_err(into_err)?;
  142. Ok(quote.into())
  143. }
  144. #[wasm_bindgen(js_name = melt)]
  145. pub async fn melt(&mut self, quote_id: String) -> Result<JsMelted> {
  146. let melted = self.inner.melt(&quote_id).await.map_err(into_err)?;
  147. Ok(melted.into())
  148. }
  149. #[wasm_bindgen(js_name = receive)]
  150. pub async fn receive(
  151. &mut self,
  152. encoded_token: String,
  153. signing_keys: Vec<JsSecretKey>,
  154. preimages: Vec<String>,
  155. ) -> Result<JsAmount> {
  156. let signing_keys: Vec<SecretKey> = signing_keys.iter().map(|s| s.deref().clone()).collect();
  157. Ok(self
  158. .inner
  159. .receive(
  160. &encoded_token,
  161. SplitTarget::default(),
  162. &signing_keys,
  163. &preimages,
  164. )
  165. .await
  166. .map_err(into_err)?
  167. .into())
  168. }
  169. #[allow(clippy::too_many_arguments)]
  170. #[wasm_bindgen(js_name = send)]
  171. pub async fn send(
  172. &mut self,
  173. memo: Option<String>,
  174. amount: u64,
  175. p2pk_condition: Option<JsP2PKSpendingConditions>,
  176. htlc_condition: Option<JsHTLCSpendingConditions>,
  177. split_target_amount: Option<JsAmount>,
  178. ) -> Result<String> {
  179. let conditions = match (p2pk_condition, htlc_condition) {
  180. (Some(_), Some(_)) => {
  181. return Err(JsValue::from_str(
  182. "Cannot define both p2pk and htlc conditions",
  183. ));
  184. }
  185. (None, Some(htlc_condition)) => Some(htlc_condition.deref().clone()),
  186. (Some(p2pk_condition), None) => Some(p2pk_condition.deref().clone()),
  187. (None, None) => None,
  188. };
  189. let target = split_target_amount
  190. .map(|a| SplitTarget::Value(*a.deref()))
  191. .unwrap_or_default();
  192. Ok(self
  193. .inner
  194. .send(
  195. Amount::from(amount),
  196. memo,
  197. conditions,
  198. &target,
  199. &SendKind::default(),
  200. false,
  201. )
  202. .await
  203. .map_err(into_err)?
  204. .to_string())
  205. }
  206. #[allow(clippy::too_many_arguments)]
  207. #[wasm_bindgen(js_name = swap)]
  208. pub async fn swap(
  209. &mut self,
  210. amount: u64,
  211. input_proofs: Vec<JsProof>,
  212. p2pk_condition: Option<JsP2PKSpendingConditions>,
  213. htlc_condition: Option<JsHTLCSpendingConditions>,
  214. split_target_amount: Option<JsAmount>,
  215. ) -> Result<JsValue> {
  216. let conditions = match (p2pk_condition, htlc_condition) {
  217. (Some(_), Some(_)) => {
  218. return Err(JsValue::from_str(
  219. "Cannot define both p2pk and htlc conditions",
  220. ));
  221. }
  222. (None, Some(htlc_condition)) => Some(htlc_condition.deref().clone()),
  223. (Some(p2pk_condition), None) => Some(p2pk_condition.deref().clone()),
  224. (None, None) => None,
  225. };
  226. let proofs: Proofs = input_proofs.iter().map(|p| p.deref()).cloned().collect();
  227. let target = split_target_amount
  228. .map(|a| SplitTarget::Value(*a.deref()))
  229. .unwrap_or_default();
  230. let post_swap_proofs = self
  231. .inner
  232. .swap(
  233. Some(Amount::from(amount)),
  234. target,
  235. proofs,
  236. conditions,
  237. false,
  238. )
  239. .await
  240. .map_err(into_err)?;
  241. Ok(serde_wasm_bindgen::to_value(&post_swap_proofs)?)
  242. }
  243. }