1use std::net::SocketAddr;
2
3use lwk_jade::TIMEOUT;
4use lwk_wollet::UnvalidatedRecipient;
5use serde::de::DeserializeOwned;
6use serde::Serialize;
7use serde_json::value::to_raw_value;
8use serde_json::value::RawValue;
9use serde_json::Value;
10
11use crate::error::Error;
12use crate::method::Method;
13use crate::reqwest_transport::ReqwestHttpTransport;
14use crate::{request, response};
15
16pub struct Client {
17 client: jsonrpc::Client,
18}
19
20impl Client {
21 pub fn new(addr: SocketAddr) -> Result<Self, Error> {
22 let url = format!("http://{addr}");
23 let transport = ReqwestHttpTransport::new(url, TIMEOUT);
24 let client = jsonrpc::Client::with_transport(transport);
25 Ok(Self { client })
26 }
27
28 pub(crate) fn make_request<Req, Res>(
29 &self,
30 method: Method,
31 req: Option<Req>,
32 ) -> std::result::Result<Res, Error>
33 where
34 Req: Serialize,
35 Res: DeserializeOwned,
36 {
37 let params = req.map(|req| to_raw_value(&req)).transpose()?;
38 let method = method.to_string();
39 let request = self.client.build_request(&method, params.as_deref());
40 log::trace!("---> {}", serde_json::to_string(&request)?);
41 let response = self.client.send_request(request)?;
42 log::trace!("<--- {}", serde_json::to_string(&response)?);
43 match response.result.as_ref() {
44 Some(result) => Ok(serde_json::from_str(result.get())?),
45 None => match response.error {
46 Some(rpc_err) => Err(Error::RpcError(rpc_err)),
47 None => Err(Error::NeitherResultNorErrorSet),
48 },
49 }
50 }
51
52 pub fn version(&self) -> Result<response::Version, Error> {
53 self.make_request(Method::Version, None::<Box<RawValue>>)
54 }
55
56 pub fn signer_generate(&self) -> Result<response::SignerGenerate, Error> {
57 self.make_request(Method::SignerGenerate, None::<Box<RawValue>>)
58 }
59
60 pub fn signer_load_software(
61 &self,
62 name: String,
63 mnemonic: String,
64 persist: bool,
65 ) -> Result<response::Signer, Error> {
66 let req = request::SignerLoadSoftware {
67 name,
68 mnemonic,
69 persist,
70 };
71 self.make_request(Method::SignerLoadSoftware, Some(req))
72 }
73
74 pub fn signer_load_jade(
75 &self,
76 name: String,
77 id: String,
78 emulator: Option<SocketAddr>,
79 ) -> Result<response::Signer, Error> {
80 let req = request::SignerLoadJade { name, id, emulator };
81 self.make_request(Method::SignerLoadJade, Some(req))
82 }
83
84 pub fn signer_load_external(
85 &self,
86 name: String,
87 fingerprint: String,
88 ) -> Result<response::Signer, Error> {
89 let req = request::SignerLoadExternal { name, fingerprint };
90 self.make_request(Method::SignerLoadExternal, Some(req))
91 }
92
93 pub fn wallet_list(&self) -> Result<response::WalletList, Error> {
94 self.make_request(Method::WalletList, None::<Box<RawValue>>)
95 }
96
97 pub fn wallet_load(&self, descriptor: String, name: String) -> Result<response::Wallet, Error> {
98 let req = request::WalletLoad { descriptor, name };
99 self.make_request(Method::WalletLoad, Some(req))
100 }
101
102 pub fn wallet_unload(&self, name: String) -> Result<response::WalletUnload, Error> {
103 let req = request::WalletUnload { name };
104 self.make_request(Method::WalletUnload, Some(req))
105 }
106
107 pub fn signer_unload(&self, name: String) -> Result<response::SignerUnload, Error> {
108 let req = request::SignerUnload { name };
109 self.make_request(Method::SignerUnload, Some(req))
110 }
111
112 pub fn signer_list(&self) -> Result<response::SignerList, Error> {
113 self.make_request(Method::SignerList, None::<Box<RawValue>>)
114 }
115
116 pub fn wallet_balance(
117 &self,
118 name: String,
119 with_tickers: bool,
120 ) -> Result<response::WalletBalance, Error> {
121 let req = request::WalletBalance { name, with_tickers };
122 self.make_request(Method::WalletBalance, Some(req))
123 }
124
125 pub fn wallet_address(
126 &self,
127 name: String,
128 index: Option<u32>,
129 signer: Option<String>,
130 with_text_qr: bool,
131 with_uri_qr: Option<u8>,
132 ) -> Result<response::WalletAddress, Error> {
133 let req = request::WalletAddress {
134 name,
135 index,
136 signer,
137 with_text_qr,
138 with_uri_qr,
139 };
140 self.make_request(Method::WalletAddress, Some(req))
141 }
142
143 pub fn wallet_send_many(
144 &self,
145 name: String,
146 addressees: Vec<UnvalidatedRecipient>,
147 fee_rate: Option<f32>,
148 ) -> Result<response::Pset, Error> {
149 let req = request::WalletSendMany {
150 addressees: addressees.into_iter().map(unvalidate_addressee).collect(),
151 fee_rate,
152 name,
153 };
154 self.make_request(Method::WalletSendMany, Some(req))
155 }
156
157 pub fn wallet_drain(
158 &self,
159 name: String,
160 address: String,
161 fee_rate: Option<f32>,
162 ) -> Result<response::Pset, Error> {
163 let req = request::WalletDrain {
164 address,
165 fee_rate,
166 name,
167 };
168 self.make_request(Method::WalletDrain, Some(req))
169 }
170
171 pub fn signer_singlesig_descriptor(
172 &self,
173 name: String,
174 descriptor_blinding_key: String,
175 singlesig_kind: String,
176 ) -> Result<response::SignerSinglesigDescriptor, Error> {
177 let req = request::SignerSinglesigDescriptor {
178 name,
179 descriptor_blinding_key,
180 singlesig_kind,
181 };
182 self.make_request(Method::SignerSinglesigDescriptor, Some(req))
183 }
184
185 pub fn wallet_multisig_descriptor(
186 &self,
187 descriptor_blinding_key: String,
188 multisig_kind: String,
189 threshold: u32,
190 keyorigin_xpubs: Vec<String>,
191 ) -> Result<response::WalletMultisigDescriptor, Error> {
192 let req = request::WalletMultisigDescriptor {
193 descriptor_blinding_key,
194 multisig_kind,
195 threshold,
196 keyorigin_xpubs,
197 };
198 self.make_request(Method::WalletMultisigDescriptor, Some(req))
199 }
200
201 pub fn signer_xpub(
202 &self,
203 name: String,
204 xpub_kind: String,
205 ) -> Result<response::SignerXpub, Error> {
206 let req = request::SignerXpub { name, xpub_kind };
207 self.make_request(Method::SignerXpub, Some(req))
208 }
209
210 pub fn signer_register_multisig(
211 &self,
212 name: String,
213 wallet: String,
214 ) -> Result<response::Empty, Error> {
215 let req = request::SignerRegisterMultisig { name, wallet };
216 self.make_request(Method::SignerRegisterMultisig, Some(req))
217 }
218
219 pub fn signer_sign(&self, name: String, pset: String) -> Result<response::Pset, Error> {
220 let req = request::SignerSign { name, pset };
221 self.make_request(Method::SignerSign, Some(req))
222 }
223
224 pub fn wallet_broadcast(
225 &self,
226 name: String,
227 dry_run: bool,
228 pset: String,
229 ) -> Result<response::WalletBroadcast, Error> {
230 let req = request::WalletBroadcast {
231 name,
232 dry_run,
233 pset,
234 };
235 self.make_request(Method::WalletBroadcast, Some(req))
236 }
237
238 pub fn wallet_details(&self, name: String) -> Result<response::WalletDetails, Error> {
239 let req = request::WalletDetails { name };
240 self.make_request(Method::WalletDetails, Some(req))
241 }
242
243 pub fn signer_details(&self, name: String) -> Result<response::SignerDetails, Error> {
244 let req = request::SignerDetails { name };
245 self.make_request(Method::SignerDetails, Some(req))
246 }
247
248 pub fn wallet_combine(
249 &self,
250 name: String,
251 pset: Vec<String>,
252 ) -> Result<response::WalletCombine, Error> {
253 let req = request::WalletCombine { name, pset };
254 self.make_request(Method::WalletCombine, Some(req))
255 }
256
257 pub fn wallet_pset_details(
258 &self,
259 name: String,
260 pset: String,
261 with_tickers: bool,
262 ) -> Result<response::WalletPsetDetails, Error> {
263 let req = request::WalletPsetDetails {
264 name,
265 pset,
266 with_tickers,
267 };
268 self.make_request(Method::WalletPsetDetails, Some(req))
269 }
270
271 pub fn wallet_utxos(&self, name: String) -> Result<response::WalletUtxos, Error> {
272 let req = request::WalletUtxos { name };
273 self.make_request(Method::WalletUtxos, Some(req))
274 }
275
276 pub fn wallet_txs(
277 &self,
278 name: String,
279 with_tickers: bool,
280 ) -> Result<response::WalletTxs, Error> {
281 let req = request::WalletTxs { name, with_tickers };
282 self.make_request(Method::WalletTxs, Some(req))
283 }
284
285 pub fn wallet_tx(
286 &self,
287 name: String,
288 txid: String,
289 from_explorer: bool,
290 ) -> Result<response::WalletTx, Error> {
291 let req = request::WalletTx {
292 name,
293 txid,
294 from_explorer,
295 };
296 self.make_request(Method::WalletTx, Some(req))
297 }
298
299 pub fn wallet_set_tx_memo(
300 &self,
301 name: String,
302 txid: String,
303 memo: String,
304 ) -> Result<response::Empty, Error> {
305 let req = request::WalletSetTxMemo { name, txid, memo };
306 self.make_request(Method::WalletSetTxMemo, Some(req))
307 }
308
309 pub fn wallet_set_addr_memo(
310 &self,
311 name: String,
312 address: String,
313 memo: String,
314 ) -> Result<response::Empty, Error> {
315 let req = request::WalletSetAddrMemo {
316 name,
317 address,
318 memo,
319 };
320 self.make_request(Method::WalletSetAddrMemo, Some(req))
321 }
322
323 pub fn liquidex_make(
324 &self,
325 name: String,
326 txid: String,
327 vout: u32,
328 asset: String,
329 satoshi: u64,
330 ) -> Result<response::Pset, Error> {
331 let req = request::LiquidexMake {
332 name,
333 txid,
334 vout,
335 asset,
336 satoshi,
337 };
338 self.make_request(Method::LiquidexMake, Some(req))
339 }
340
341 pub fn liquidex_take(&self, name: String, proposal: String) -> Result<response::Pset, Error> {
342 let req = request::LiquidexTake { name, proposal };
343 self.make_request(Method::LiquidexTake, Some(req))
344 }
345
346 pub fn liquidex_to_proposal(&self, pset: String) -> Result<response::LiquidexProposal, Error> {
347 let req = request::LiquidexToProposal { pset };
348 self.make_request(Method::LiquidexToProposal, Some(req))
349 }
350
351 #[allow(clippy::too_many_arguments)]
352 pub fn wallet_issue(
353 &self,
354 name: String,
355 satoshi_asset: u64,
356 address_asset: Option<String>,
357 satoshi_token: u64,
358 address_token: Option<String>,
359 contract: Option<String>,
360 fee_rate: Option<f32>,
361 ) -> Result<response::Pset, Error> {
362 let req = request::WalletIssue {
363 name,
364 satoshi_asset,
365 address_asset,
366 satoshi_token,
367 address_token,
368 contract,
369 fee_rate,
370 };
371 self.make_request(Method::WalletIssue, Some(req))
372 }
373
374 pub fn wallet_reissue(
375 &self,
376 name: String,
377 asset: String,
378 satoshi_asset: u64,
379 address_asset: Option<String>,
380 fee_rate: Option<f32>,
381 ) -> Result<response::Pset, Error> {
382 let req = request::WalletReissue {
383 name,
384 asset,
385 satoshi_asset,
386 address_asset,
387 fee_rate,
388 };
389 self.make_request(Method::WalletReissue, Some(req))
390 }
391
392 pub fn wallet_burn(
393 &self,
394 name: String,
395 asset: String,
396 satoshi_asset: u64,
397 fee_rate: Option<f32>,
398 ) -> Result<response::Pset, Error> {
399 let req = request::WalletBurn {
400 name,
401 asset,
402 satoshi_asset,
403 fee_rate,
404 };
405 self.make_request(Method::WalletBurn, Some(req))
406 }
407
408 pub fn asset_contract(
409 &self,
410 domain: String,
411 issuer_pubkey: String,
412 name: String,
413 precision: u8,
414 ticker: String,
415 version: u8,
416 ) -> Result<response::AssetContract, Error> {
417 let req = request::AssetContract {
418 domain,
419 issuer_pubkey,
420 name,
421 precision,
422 ticker,
423 version,
424 };
425 self.make_request(Method::AssetContract, Some(req))
426 }
427
428 pub fn asset_details(&self, asset_id: String) -> Result<response::AssetDetails, Error> {
429 let req = request::AssetDetails { asset_id };
430 self.make_request(Method::AssetDetails, Some(req))
431 }
432
433 pub fn asset_list(&self) -> Result<response::AssetList, Error> {
434 self.make_request(Method::AssetList, None::<Box<RawValue>>)
435 }
436
437 pub fn asset_insert(
438 &self,
439 asset_id: String,
440 issuance_tx: String,
441 contract: String,
442 ) -> Result<response::Empty, Error> {
443 let req = request::AssetInsert {
444 asset_id,
445 issuance_tx,
446 contract,
447 };
448 self.make_request(Method::AssetInsert, Some(req))
449 }
450
451 pub fn asset_remove(&self, asset_id: String) -> Result<response::Empty, Error> {
452 let req = request::AssetRemove { asset_id };
453 self.make_request(Method::AssetRemove, Some(req))
454 }
455
456 pub fn asset_from_explorer(&self, asset_id: String) -> Result<response::Empty, Error> {
457 let req = request::AssetFromExplorer { asset_id };
458 self.make_request(Method::AssetFromExplorer, Some(req))
459 }
460
461 pub fn asset_publish(&self, asset_id: String) -> Result<response::AssetPublish, Error> {
462 let req = request::AssetPublish { asset_id };
463 self.make_request(Method::AssetPublish, Some(req))
464 }
465
466 pub fn amp2_descriptor(&self, name: String) -> Result<response::Amp2Descriptor, Error> {
467 let req = request::Amp2Descriptor { name };
468 self.make_request(Method::Amp2Descriptor, Some(req))
469 }
470
471 pub fn amp2_register(&self, name: String) -> Result<response::Amp2Register, Error> {
472 let req = request::Amp2Register { name };
473 self.make_request(Method::Amp2Register, Some(req))
474 }
475
476 pub fn amp2_cosign(&self, pset: String) -> Result<response::Amp2Cosign, Error> {
477 let req = request::Amp2Cosign { pset };
478 self.make_request(Method::Amp2Cosign, Some(req))
479 }
480
481 pub fn schema(&self, arg: Method, direction: request::Direction) -> Result<Value, Error> {
482 let req = request::Schema {
483 method: arg.to_string(),
484 direction,
485 };
486 self.make_request(Method::Schema, Some(req))
487 }
488
489 pub fn signer_jade_id(&self, emulator: Option<SocketAddr>) -> Result<Value, Error> {
490 let req = request::SignerJadeId { emulator };
491 self.make_request(Method::SignerJadeId, Some(req))
492 }
493
494 pub fn scan(&self) -> Result<Value, Error> {
495 self.make_request(Method::Scan, None::<Box<RawValue>>)
496 }
497
498 pub fn stop(&self) -> Result<Value, Error> {
499 let _: Result<Value, Error> = self.make_request(Method::Stop, None::<Box<RawValue>>);
501 Ok(Value::Null)
502 }
503}
504
505fn unvalidate_addressee(a: lwk_wollet::UnvalidatedRecipient) -> request::UnvalidatedAddressee {
506 request::UnvalidatedAddressee {
507 satoshi: a.satoshi,
508 address: a.address,
509 asset: a.asset,
510 }
511}