use std::net::SocketAddr;
use lwk_jade::TIMEOUT;
use lwk_wollet::UnvalidatedAddressee;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json::value::to_raw_value;
use serde_json::value::RawValue;
use serde_json::Value;
use crate::error::Error;
use crate::method::Method;
use crate::{request, response};
pub struct Client {
client: jsonrpc::Client,
}
impl Client {
pub fn new(addr: SocketAddr) -> Result<Self, Error> {
let url = addr.to_string();
let transport = jsonrpc::simple_http::Builder::new()
.timeout(TIMEOUT)
.url(&url)?
.build();
let client = jsonrpc::Client::with_transport(transport);
Ok(Self { client })
}
pub(crate) fn make_request<Req, Res>(
&self,
method: Method,
req: Option<Req>,
) -> std::result::Result<Res, Error>
where
Req: Serialize,
Res: DeserializeOwned,
{
let params = req.map(|req| to_raw_value(&req)).transpose()?;
let method = method.to_string();
let request = self.client.build_request(&method, params.as_deref());
tracing::trace!("---> {}", serde_json::to_string(&request)?);
let response = self.client.send_request(request)?;
tracing::trace!("<--- {}", serde_json::to_string(&response)?);
match response.result.as_ref() {
Some(result) => Ok(serde_json::from_str(result.get())?),
None => match response.error {
Some(rpc_err) => Err(Error::RpcError(rpc_err)),
None => Err(Error::NeitherResultNorErrorSet),
},
}
}
pub fn version(&self) -> Result<response::Version, Error> {
self.make_request(Method::Version, None::<Box<RawValue>>)
}
pub fn generate_signer(&self) -> Result<response::GenerateSigner, Error> {
self.make_request(Method::GenerateSigner, None::<Box<RawValue>>)
}
pub fn signer_load_software(
&self,
name: String,
mnemonic: String,
) -> Result<response::Signer, Error> {
let req = request::SignerLoadSoftware { name, mnemonic };
self.make_request(Method::SignerLoadSoftware, Some(req))
}
pub fn signer_load_jade(
&self,
name: String,
id: String,
emulator: Option<SocketAddr>,
) -> Result<response::Signer, Error> {
let req = request::SignerLoadJade { name, id, emulator };
self.make_request(Method::SignerLoadJade, Some(req))
}
pub fn signer_load_external(
&self,
name: String,
fingerprint: String,
) -> Result<response::Signer, Error> {
let req = request::SignerLoadExternal { name, fingerprint };
self.make_request(Method::SignerLoadExternal, Some(req))
}
pub fn list_wallets(&self) -> Result<response::ListWallets, Error> {
self.make_request(Method::ListWallets, None::<Box<RawValue>>)
}
pub fn load_wallet(&self, descriptor: String, name: String) -> Result<response::Wallet, Error> {
let req = request::LoadWallet { descriptor, name };
self.make_request(Method::LoadWallet, Some(req))
}
pub fn unload_wallet(&self, name: String) -> Result<response::UnloadWallet, Error> {
let req = request::UnloadWallet { name };
self.make_request(Method::UnloadWallet, Some(req))
}
pub fn unload_signer(&self, name: String) -> Result<response::UnloadSigner, Error> {
let req = request::UnloadSigner { name };
self.make_request(Method::UnloadSigner, Some(req))
}
pub fn list_signers(&self) -> Result<response::ListSigners, Error> {
self.make_request(Method::ListSigners, None::<Box<RawValue>>)
}
pub fn balance(&self, name: String, with_tickers: bool) -> Result<response::Balance, Error> {
let req = request::Balance { name, with_tickers };
self.make_request(Method::Balance, Some(req))
}
pub fn address(
&self,
name: String,
index: Option<u32>,
signer: Option<String>,
) -> Result<response::Address, Error> {
let req = request::Address {
name,
index,
signer,
};
self.make_request(Method::Address, Some(req))
}
pub fn send_many(
&self,
name: String,
addressees: Vec<UnvalidatedAddressee>,
fee_rate: Option<f32>,
) -> Result<response::Pset, Error> {
let req = request::Send {
addressees: addressees.into_iter().map(unvalidate_addressee).collect(),
fee_rate,
name,
};
self.make_request(Method::SendMany, Some(req))
}
pub fn singlesig_descriptor(
&self,
name: String,
descriptor_blinding_key: String,
singlesig_kind: String,
) -> Result<response::SinglesigDescriptor, Error> {
let req = request::SinglesigDescriptor {
name,
descriptor_blinding_key,
singlesig_kind,
};
self.make_request(Method::SinglesigDescriptor, Some(req))
}
pub fn multisig_descriptor(
&self,
descriptor_blinding_key: String,
multisig_kind: String,
threshold: u32,
keyorigin_xpubs: Vec<String>,
) -> Result<response::MultisigDescriptor, Error> {
let req = request::MultisigDescriptor {
descriptor_blinding_key,
multisig_kind,
threshold,
keyorigin_xpubs,
};
self.make_request(Method::MultisigDescriptor, Some(req))
}
pub fn xpub(&self, name: String, xpub_kind: String) -> Result<response::Xpub, Error> {
let req = request::Xpub { name, xpub_kind };
self.make_request(Method::Xpub, Some(req))
}
pub fn register_multisig(
&self,
name: String,
wallet: String,
) -> Result<response::Empty, Error> {
let req = request::RegisterMultisig { name, wallet };
self.make_request(Method::RegisterMultisig, Some(req))
}
pub fn sign(&self, name: String, pset: String) -> Result<response::Pset, Error> {
let req = request::Sign { name, pset };
self.make_request(Method::Sign, Some(req))
}
pub fn broadcast(
&self,
name: String,
dry_run: bool,
pset: String,
) -> Result<response::Broadcast, Error> {
let req = request::Broadcast {
name,
dry_run,
pset,
};
self.make_request(Method::Broadcast, Some(req))
}
pub fn wallet_details(&self, name: String) -> Result<response::WalletDetails, Error> {
let req = request::WalletDetails { name };
self.make_request(Method::WalletDetails, Some(req))
}
pub fn wallet_combine(
&self,
name: String,
pset: Vec<String>,
) -> Result<response::WalletCombine, Error> {
let req = request::WalletCombine { name, pset };
self.make_request(Method::WalletCombine, Some(req))
}
pub fn wallet_pset_details(
&self,
name: String,
pset: String,
with_tickers: bool,
) -> Result<response::WalletPsetDetails, Error> {
let req = request::WalletPsetDetails {
name,
pset,
with_tickers,
};
self.make_request(Method::WalletPsetDetails, Some(req))
}
pub fn wallet_utxos(&self, name: String) -> Result<response::WalletUtxos, Error> {
let req = request::WalletUtxos { name };
self.make_request(Method::WalletUtxos, Some(req))
}
pub fn wallet_txs(
&self,
name: String,
with_tickers: bool,
) -> Result<response::WalletTxs, Error> {
let req = request::WalletTxs { name, with_tickers };
self.make_request(Method::WalletTxs, Some(req))
}
#[allow(clippy::too_many_arguments)]
pub fn issue(
&self,
name: String,
satoshi_asset: u64,
address_asset: Option<String>,
satoshi_token: u64,
address_token: Option<String>,
contract: Option<String>,
fee_rate: Option<f32>,
) -> Result<response::Pset, Error> {
let req = request::Issue {
name,
satoshi_asset,
address_asset,
satoshi_token,
address_token,
contract,
fee_rate,
};
self.make_request(Method::Issue, Some(req))
}
pub fn reissue(
&self,
name: String,
asset: String,
satoshi_asset: u64,
address_asset: Option<String>,
fee_rate: Option<f32>,
) -> Result<response::Pset, Error> {
let req = request::Reissue {
name,
asset,
satoshi_asset,
address_asset,
fee_rate,
};
self.make_request(Method::Reissue, Some(req))
}
pub fn contract(
&self,
domain: String,
issuer_pubkey: String,
name: String,
precision: u8,
ticker: String,
version: u8,
) -> Result<response::Contract, Error> {
let req = request::Contract {
domain,
issuer_pubkey,
name,
precision,
ticker,
version,
};
self.make_request(Method::Contract, Some(req))
}
pub fn asset_details(&self, asset_id: String) -> Result<response::AssetDetails, Error> {
let req = request::AssetDetails { asset_id };
self.make_request(Method::AssetDetails, Some(req))
}
pub fn list_assets(&self) -> Result<response::ListAssets, Error> {
self.make_request(Method::ListAssets, None::<Box<RawValue>>)
}
pub fn asset_insert(
&self,
asset_id: String,
contract: String,
prev_txid: String,
prev_vout: u32,
is_confidential: Option<bool>,
) -> Result<response::Empty, Error> {
let req = request::AssetInsert {
asset_id,
contract,
prev_txid,
prev_vout,
is_confidential,
};
self.make_request(Method::AssetInsert, Some(req))
}
pub fn asset_remove(&self, asset_id: String) -> Result<response::Empty, Error> {
let req = request::AssetRemove { asset_id };
self.make_request(Method::AssetRemove, Some(req))
}
pub fn schema(&self, arg: Method, direction: request::Direction) -> Result<Value, Error> {
let req = request::Schema {
method: arg.to_string(),
direction,
};
self.make_request(Method::Schema, Some(req))
}
pub fn signer_jade_id(&self, emulator: Option<SocketAddr>) -> Result<Value, Error> {
let req = request::SignerJadeId { emulator };
self.make_request(Method::SignerJadeId, Some(req))
}
pub fn stop(&self) -> Result<Value, Error> {
let _: Result<Value, Error> = self.make_request(Method::Stop, None::<Box<RawValue>>);
Ok(Value::Null)
}
}
fn unvalidate_addressee(a: lwk_wollet::UnvalidatedAddressee) -> request::UnvalidatedAddressee {
request::UnvalidatedAddressee {
satoshi: a.satoshi,
address: a.address,
asset: a.asset,
}
}