gloo_client.rs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. //! gloo wasm http Client
  2. use async_trait::async_trait;
  3. #[cfg(feature = "nut09")]
  4. use cashu::nuts::nut09::{RestoreRequest, RestoreResponse};
  5. use cashu::nuts::{
  6. BlindedMessage, MeltBolt11Request, MeltBolt11Response, MintBolt11Request, MintBolt11Response,
  7. MintInfo, PreMintSecrets, Proof, SwapRequest, SwapResponse, *,
  8. };
  9. #[cfg(feature = "nut07")]
  10. use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse, PublicKey};
  11. use cashu::{Amount, Bolt11Invoice};
  12. use gloo::net::http::Request;
  13. use serde_json::Value;
  14. use url::Url;
  15. use super::join_url;
  16. use crate::client::{Client, Error};
  17. #[derive(Debug, Clone)]
  18. pub struct HttpClient {}
  19. #[async_trait(?Send)]
  20. impl Client for HttpClient {
  21. /// Get Mint Keys [NUT-01]
  22. async fn get_mint_keys(&self, mint_url: Url) -> Result<Vec<KeySet>, Error> {
  23. let url = join_url(mint_url, &["v1", "keys"])?;
  24. let keys = Request::get(url.as_str())
  25. .send()
  26. .await
  27. .map_err(|err| Error::Gloo(err.to_string()))?
  28. .json::<Value>()
  29. .await
  30. .map_err(|err| Error::Gloo(err.to_string()))?;
  31. let keys: KeysResponse = serde_json::from_str(&keys.to_string())?;
  32. Ok(keys.keysets)
  33. }
  34. /// Get Keysets [NUT-02]
  35. async fn get_mint_keysets(&self, mint_url: Url) -> Result<KeysetResponse, Error> {
  36. let url = join_url(mint_url, &["v1", "keysets"])?;
  37. let res = Request::get(url.as_str())
  38. .send()
  39. .await
  40. .map_err(|err| Error::Gloo(err.to_string()))?
  41. .json::<Value>()
  42. .await
  43. .map_err(|err| Error::Gloo(err.to_string()))?;
  44. let response: Result<KeysetResponse, serde_json::Error> =
  45. serde_json::from_value(res.clone());
  46. match response {
  47. Ok(res) => Ok(res),
  48. Err(_) => Err(Error::from_json(&res.to_string())?),
  49. }
  50. }
  51. /// Mint Quote [NUT-04]
  52. async fn post_mint_quote(
  53. &self,
  54. mint_url: Url,
  55. amount: Amount,
  56. unit: CurrencyUnit,
  57. ) -> Result<MintQuoteBolt11Response, Error> {
  58. let url = join_url(mint_url, &["v1", "mint", "quote", "bolt11"])?;
  59. let request = MintQuoteBolt11Request { amount, unit };
  60. let res = Request::post(url.as_str())
  61. .json(&request)
  62. .map_err(|err| Error::Gloo(err.to_string()))?
  63. .send()
  64. .await
  65. .map_err(|err| Error::Gloo(err.to_string()))?
  66. .json::<Value>()
  67. .await
  68. .map_err(|err| Error::Gloo(err.to_string()))?;
  69. let response: Result<MintQuoteBolt11Response, serde_json::Error> =
  70. serde_json::from_value(res.clone());
  71. match response {
  72. Ok(res) => Ok(res),
  73. Err(_) => Err(Error::from_json(&res.to_string())?),
  74. }
  75. }
  76. /// Mint Tokens [NUT-04]
  77. async fn post_mint(
  78. &self,
  79. mint_url: Url,
  80. quote: &str,
  81. premint_secrets: PreMintSecrets,
  82. ) -> Result<MintBolt11Response, Error> {
  83. let url = join_url(mint_url, &["v1", "mint", "bolt11"])?;
  84. let request = MintBolt11Request {
  85. quote: quote.to_string(),
  86. outputs: premint_secrets.blinded_messages(),
  87. };
  88. let res = Request::post(url.as_str())
  89. .json(&request)
  90. .map_err(|err| Error::Gloo(err.to_string()))?
  91. .send()
  92. .await
  93. .map_err(|err| Error::Gloo(err.to_string()))?
  94. .json::<Value>()
  95. .await
  96. .map_err(|err| Error::Gloo(err.to_string()))?;
  97. let response: Result<MintBolt11Response, serde_json::Error> =
  98. serde_json::from_value(res.clone());
  99. match response {
  100. Ok(res) => Ok(res),
  101. Err(_) => Err(Error::from_json(&res.to_string())?),
  102. }
  103. }
  104. /// Melt [NUT-05]
  105. async fn post_melt_quote(
  106. &self,
  107. mint_url: Url,
  108. unit: CurrencyUnit,
  109. request: Bolt11Invoice,
  110. ) -> Result<MeltQuoteBolt11Response, Error> {
  111. let url = join_url(mint_url, &["v1", "melt", "quote", "bolt11"])?;
  112. let request = MeltQuoteBolt11Request { unit, request };
  113. let res = Request::post(url.as_str())
  114. .json(&request)
  115. .map_err(|err| Error::Gloo(err.to_string()))?
  116. .send()
  117. .await
  118. .map_err(|err| Error::Gloo(err.to_string()))?
  119. .json::<Value>()
  120. .await
  121. .map_err(|err| Error::Gloo(err.to_string()))?;
  122. let response: Result<MeltQuoteBolt11Response, serde_json::Error> =
  123. serde_json::from_value(res.clone());
  124. match response {
  125. Ok(res) => Ok(res),
  126. Err(_) => Err(Error::from_json(&res.to_string())?),
  127. }
  128. }
  129. /// [Nut-08] Lightning fee return if outputs defined
  130. async fn post_melt(
  131. &self,
  132. mint_url: Url,
  133. quote: String,
  134. inputs: Vec<Proof>,
  135. outputs: Option<Vec<BlindedMessage>>,
  136. ) -> Result<MeltBolt11Response, Error> {
  137. let url = join_url(mint_url, &["v1", "melt", "bolt11"])?;
  138. let request = MeltBolt11Request {
  139. quote,
  140. inputs,
  141. outputs,
  142. };
  143. let value = Request::post(url.as_str())
  144. .json(&request)
  145. .map_err(|err| Error::Gloo(err.to_string()))?
  146. .send()
  147. .await
  148. .map_err(|err| Error::Gloo(err.to_string()))?
  149. .json::<Value>()
  150. .await
  151. .map_err(|err| Error::Gloo(err.to_string()))?;
  152. let response: Result<MeltBolt11Response, serde_json::Error> =
  153. serde_json::from_value(value.clone());
  154. match response {
  155. Ok(res) => Ok(res),
  156. Err(_) => Err(Error::from_json(&value.to_string())?),
  157. }
  158. }
  159. /// Split Token [NUT-06]
  160. async fn post_swap(
  161. &self,
  162. mint_url: Url,
  163. split_request: SwapRequest,
  164. ) -> Result<SwapResponse, Error> {
  165. let url = join_url(mint_url, &["v1", "split"])?;
  166. let res = Request::post(url.as_str())
  167. .json(&split_request)
  168. .map_err(|err| Error::Gloo(err.to_string()))?
  169. .send()
  170. .await
  171. .map_err(|err| Error::Gloo(err.to_string()))?
  172. .json::<Value>()
  173. .await
  174. .map_err(|err| Error::Gloo(err.to_string()))?;
  175. let response: Result<SwapResponse, serde_json::Error> = serde_json::from_value(res.clone());
  176. match response {
  177. Ok(res) => Ok(res),
  178. Err(_) => Err(Error::from_json(&res.to_string())?),
  179. }
  180. }
  181. /// Spendable check [NUT-07]
  182. #[cfg(feature = "nut07")]
  183. async fn post_check_state(
  184. &self,
  185. mint_url: Url,
  186. ys: Vec<PublicKey>,
  187. ) -> Result<CheckStateResponse, Error> {
  188. let url = join_url(mint_url, &["v1", "check"])?;
  189. let request = CheckSpendableRequest { ys };
  190. let res = Request::post(url.as_str())
  191. .json(&request)
  192. .map_err(|err| Error::Gloo(err.to_string()))?
  193. .send()
  194. .await
  195. .map_err(|err| Error::Gloo(err.to_string()))?
  196. .json::<Value>()
  197. .await
  198. .map_err(|err| Error::Gloo(err.to_string()))?;
  199. let response: Result<CheckSpendableResponse, serde_json::Error> =
  200. serde_json::from_value(res.clone());
  201. match response {
  202. Ok(res) => Ok(res),
  203. Err(_) => Err(Error::from_json(&res.to_string())?),
  204. }
  205. }
  206. /// Get Mint Info [NUT-09]
  207. async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error> {
  208. let url = join_url(mint_url, &["v1", "info"])?;
  209. let res = Request::get(url.as_str())
  210. .send()
  211. .await
  212. .map_err(|err| Error::Gloo(err.to_string()))?
  213. .json::<Value>()
  214. .await
  215. .map_err(|err| Error::Gloo(err.to_string()))?;
  216. let response: Result<MintInfo, serde_json::Error> = serde_json::from_value(res.clone());
  217. match response {
  218. Ok(res) => Ok(res),
  219. Err(_) => Err(Error::from_json(&res.to_string())?),
  220. }
  221. }
  222. /// Restore [NUT-09]
  223. #[cfg(feature = "nut09")]
  224. async fn post_check_state(
  225. &self,
  226. mint_url: Url,
  227. request: RestoreRequest,
  228. ) -> Result<CheckStateResponse, Error> {
  229. let url = join_url(mint_url, &["v1", "check"])?;
  230. let res = Request::post(url.as_str())
  231. .json(&request)
  232. .map_err(|err| Error::Gloo(err.to_string()))?
  233. .send()
  234. .await
  235. .map_err(|err| Error::Gloo(err.to_string()))?
  236. .json::<Value>()
  237. .await
  238. .map_err(|err| Error::Gloo(err.to_string()))?;
  239. let response: Result<RestoreRequest, serde_json::Error> =
  240. serde_json::from_value(res.clone());
  241. match response {
  242. Ok(res) => Ok(res),
  243. Err(_) => Err(Error::from_json(&res.to_string())?),
  244. }
  245. }
  246. }