breez_sdk_liquid/swapper/boltz/
client.rs1use crate::swapper::boltz::CONNECTION_TIMEOUT;
2use crate::{
3 bitcoin, elements,
4 model::{BlockchainExplorer, Config, BREEZ_LIQUID_ESPLORA_URL},
5};
6use boltz_client::{
7 error::Error,
8 network::{
9 esplora::{EsploraBitcoinClient, EsploraLiquidClient},
10 BitcoinChain, BitcoinClient as BoltzBitcoinClient, LiquidChain,
11 LiquidClient as BoltzLiquidClient,
12 },
13 reqwest,
14};
15use log::error;
16use sdk_macros::async_trait;
17
18pub(crate) enum LiquidClient {
19 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
20 Electrum(Box<boltz_client::network::electrum::ElectrumLiquidClient>),
21 Esplora(Box<EsploraLiquidClient>),
22}
23
24impl LiquidClient {
25 pub(crate) fn new(config: &Config) -> Result<Self, Error> {
26 Ok(match &config.liquid_explorer {
27 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
28 BlockchainExplorer::Electrum { url } => {
29 let (tls, validate_domain) = config.electrum_tls_options();
30 Self::Electrum(Box::new(
31 boltz_client::network::electrum::ElectrumLiquidClient::new(
32 config.network.into(),
33 url,
34 tls,
35 validate_domain,
36 CONNECTION_TIMEOUT.as_secs() as u8,
37 )?,
38 ))
39 }
40 BlockchainExplorer::Esplora { url, .. } => {
41 let mut builder = reqwest::Client::builder();
42 if url == BREEZ_LIQUID_ESPLORA_URL {
43 match &config.breez_api_key {
44 Some(api_key) => {
45 let mut headers = reqwest::header::HeaderMap::new();
46 let api_key = format!("Bearer {api_key}").parse().map_err(|err| {
47 Error::Generic(format!("Could not set api key header: {err}"))
48 })?;
49 headers.insert(reqwest::header::AUTHORIZATION, api_key);
50 builder = builder.default_headers(headers)
51 }
52 None => {
53 let err = "Cannot start Breez Esplora client: Breez API key is not set";
54 error!("{err}");
55 return Err(Error::Generic(err.to_string()));
56 }
57 };
58 }
59 let client = builder.build().map_err(|err| {
60 Error::Generic(format!("Could not initialize HTTP client: {err}"))
61 })?;
62
63 Self::Esplora(Box::new(EsploraLiquidClient::with_client(
64 client,
65 config.network.into(),
66 url,
67 CONNECTION_TIMEOUT.as_secs(),
68 )))
69 }
70 })
71 }
72}
73
74#[async_trait]
75impl BoltzLiquidClient for LiquidClient {
76 async fn get_address_utxo(
77 &self,
78 address: &elements::Address,
79 ) -> Result<Option<(elements::OutPoint, elements::TxOut)>, Error> {
80 match self {
81 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
82 Self::Electrum(c) => c.get_address_utxo(address).await,
83 Self::Esplora(c) => c.get_address_utxo(address).await,
84 }
85 }
86
87 async fn get_genesis_hash(&self) -> Result<elements::BlockHash, Error> {
88 match self {
89 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
90 Self::Electrum(c) => c.get_genesis_hash().await,
91 Self::Esplora(c) => c.get_genesis_hash().await,
92 }
93 }
94
95 async fn broadcast_tx(&self, signed_tx: &elements::Transaction) -> Result<String, Error> {
96 match self {
97 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
98 Self::Electrum(c) => c.broadcast_tx(signed_tx).await,
99 Self::Esplora(c) => c.broadcast_tx(signed_tx).await,
100 }
101 }
102
103 async fn get_tx(&self, txid: elements::Txid) -> Result<elements::Transaction, Error> {
104 match self {
105 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
106 Self::Electrum(c) => c.get_tx(txid).await,
107 Self::Esplora(c) => c.get_tx(txid).await,
108 }
109 }
110
111 fn network(&self) -> LiquidChain {
112 match self {
113 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
114 Self::Electrum(c) => c.network(),
115 Self::Esplora(c) => c.network(),
116 }
117 }
118}
119
120pub(crate) enum BitcoinClient {
121 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
122 Electrum(Box<boltz_client::network::electrum::ElectrumBitcoinClient>),
123 Esplora(Box<EsploraBitcoinClient>),
124}
125
126impl BitcoinClient {
127 pub(crate) fn new(config: &Config) -> Result<Self, Error> {
128 Ok(match &config.bitcoin_explorer {
129 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
130 BlockchainExplorer::Electrum { url } => {
131 let (tls, validate_domain) = config.electrum_tls_options();
132 Self::Electrum(Box::new(
133 boltz_client::network::electrum::ElectrumBitcoinClient::new(
134 config.network.as_bitcoin_chain(),
135 url,
136 tls,
137 validate_domain,
138 CONNECTION_TIMEOUT.as_secs() as u8,
139 )?,
140 ))
141 }
142 BlockchainExplorer::Esplora { url, .. } => {
143 Self::Esplora(Box::new(EsploraBitcoinClient::new(
144 config.network.as_bitcoin_chain(),
145 url,
146 CONNECTION_TIMEOUT.as_secs(),
147 )))
148 }
149 })
150 }
151}
152
153#[async_trait]
154impl BoltzBitcoinClient for BitcoinClient {
155 async fn get_address_balance(&self, address: &bitcoin::Address) -> Result<(u64, i64), Error> {
156 match self {
157 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
158 Self::Electrum(c) => c.get_address_balance(address).await,
159 Self::Esplora(c) => c.get_address_balance(address).await,
160 }
161 }
162
163 async fn get_address_utxos(
164 &self,
165 address: &bitcoin::Address,
166 ) -> Result<Vec<(bitcoin::OutPoint, bitcoin::TxOut)>, Error> {
167 match self {
168 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
169 Self::Electrum(c) => c.get_address_utxos(address).await,
170 Self::Esplora(c) => c.get_address_utxos(address).await,
171 }
172 }
173
174 async fn broadcast_tx(&self, signed_tx: &bitcoin::Transaction) -> Result<bitcoin::Txid, Error> {
175 match self {
176 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
177 Self::Electrum(c) => c.broadcast_tx(signed_tx).await,
178 Self::Esplora(c) => c.broadcast_tx(signed_tx).await,
179 }
180 }
181
182 async fn get_tx(&self, txid: bitcoin::Txid) -> Result<bitcoin::Transaction, Error> {
183 match self {
184 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
185 Self::Electrum(c) => c.get_tx(txid).await,
186 Self::Esplora(c) => c.get_tx(txid).await,
187 }
188 }
189
190 fn network(&self) -> BitcoinChain {
191 match self {
192 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
193 Self::Electrum(c) => c.network(),
194 Self::Esplora(c) => c.network(),
195 }
196 }
197}