breez_sdk_liquid/
error.rs1use anyhow::Error;
2use lwk_wollet::secp256k1;
3use sdk_common::{
4 lightning_with_bolt12::offers::parse::Bolt12SemanticError,
5 prelude::{LnUrlAuthError, LnUrlPayError, LnUrlWithdrawError},
6};
7
8use crate::payjoin::error::PayjoinError;
9
10pub type SdkResult<T, E = SdkError> = Result<T, E>;
11
12#[macro_export]
13macro_rules! ensure_sdk {
14 ($cond:expr, $err:expr) => {
15 if !$cond {
16 return Err($err);
17 }
18 };
19}
20
21#[derive(Debug, thiserror::Error)]
23pub enum SdkError {
24 #[error("Liquid SDK instance is already running")]
25 AlreadyStarted,
26
27 #[error("Error: {err}")]
28 Generic { err: String },
29
30 #[error("Network {network} is not currently supported")]
31 NetworkNotSupported { network: String },
32
33 #[error("Liquid SDK instance is not running")]
34 NotStarted,
35
36 #[error("Service connectivity: {err}")]
37 ServiceConnectivity { err: String },
38}
39impl SdkError {
40 pub fn generic<T: AsRef<str>>(err: T) -> Self {
41 Self::Generic {
42 err: err.as_ref().to_string(),
43 }
44 }
45
46 pub(crate) fn network_not_supported<T: ToString>(network: T) -> Self {
47 Self::NetworkNotSupported {
48 network: network.to_string(),
49 }
50 }
51}
52
53impl From<anyhow::Error> for SdkError {
54 fn from(e: Error) -> Self {
55 SdkError::generic(e.to_string())
56 }
57}
58
59impl From<boltz_client::error::Error> for SdkError {
60 fn from(err: boltz_client::error::Error) -> Self {
61 match err {
62 boltz_client::error::Error::HTTP(e) => {
63 SdkError::generic(format!("Could not contact servers: {e:?}"))
64 }
65 _ => SdkError::generic(format!("{err:?}")),
66 }
67 }
68}
69
70impl From<secp256k1::Error> for SdkError {
71 fn from(err: secp256k1::Error) -> Self {
72 SdkError::generic(format!("{err:?}"))
73 }
74}
75
76#[derive(thiserror::Error, Debug)]
77pub enum PaymentError {
78 #[error("The specified funds have already been claimed")]
79 AlreadyClaimed,
80
81 #[error("The specified funds have already been sent")]
82 AlreadyPaid,
83
84 #[error("The payment is already in progress")]
85 PaymentInProgress,
86
87 #[error("Amount must be between {min} and {max}")]
88 AmountOutOfRange { min: u64, max: u64 },
89
90 #[error("Amount is missing: {err}")]
91 AmountMissing { err: String },
92
93 #[error("Asset error: {err}")]
94 AssetError { err: String },
95
96 #[error("Invalid network: {err}")]
97 InvalidNetwork { err: String },
98
99 #[error("Generic error: {err}")]
100 Generic { err: String },
101
102 #[error("The provided fees have expired")]
103 InvalidOrExpiredFees,
104
105 #[error("Cannot pay: not enough funds")]
106 InsufficientFunds,
107
108 #[error("Invalid description: {err}")]
109 InvalidDescription { err: String },
110
111 #[error("The specified invoice is not valid: {err}")]
112 InvalidInvoice { err: String },
113
114 #[error("The generated preimage is not valid")]
115 InvalidPreimage,
116
117 #[error("Boltz did not return any pairs from the request")]
118 PairsNotFound,
119
120 #[error("Payment start could not be verified within the configured timeout")]
121 PaymentTimeout,
122
123 #[error("Could not store the swap details locally")]
124 PersistError,
125
126 #[error("Could not process the Receive Payment: {err}")]
127 ReceiveError { err: String },
128
129 #[error("The payment has been refunded. Reason for failure: {err}")]
130 Refunded { err: String, refund_tx_id: String },
131
132 #[error("The payment is a self-transfer, which is not supported")]
133 SelfTransferNotSupported,
134
135 #[error("Could not process the Send Payment: {err}")]
136 SendError { err: String },
137
138 #[error("Could not sign the transaction: {err}")]
139 SignerError { err: String },
140}
141impl PaymentError {
142 pub(crate) fn asset_error<S: AsRef<str>>(err: S) -> Self {
143 Self::AssetError {
144 err: err.as_ref().to_string(),
145 }
146 }
147
148 pub(crate) fn generic<S: AsRef<str>>(err: S) -> Self {
149 Self::Generic {
150 err: err.as_ref().to_string(),
151 }
152 }
153
154 pub(crate) fn invalid_invoice<S: AsRef<str>>(err: S) -> Self {
155 Self::InvalidInvoice {
156 err: err.as_ref().to_string(),
157 }
158 }
159
160 pub(crate) fn invalid_network<S: AsRef<str>>(err: S) -> Self {
161 Self::InvalidNetwork {
162 err: err.as_ref().to_string(),
163 }
164 }
165
166 pub(crate) fn receive_error<S: AsRef<str>>(err: S) -> Self {
167 Self::ReceiveError {
168 err: err.as_ref().to_string(),
169 }
170 }
171
172 pub(crate) fn amount_missing<S: AsRef<str>>(err: S) -> Self {
173 Self::AmountMissing {
174 err: err.as_ref().to_string(),
175 }
176 }
177}
178
179impl From<Bolt12SemanticError> for PaymentError {
180 fn from(err: Bolt12SemanticError) -> Self {
181 PaymentError::Generic {
182 err: format!("Failed to create BOLT12 invoice: {err:?}"),
183 }
184 }
185}
186
187impl From<boltz_client::error::Error> for PaymentError {
188 fn from(err: boltz_client::error::Error) -> Self {
189 match err {
190 boltz_client::error::Error::HTTP(e) => PaymentError::Generic {
191 err: format!("Could not contact servers: {e:?}"),
192 },
193 _ => PaymentError::Generic {
194 err: format!("{err:?}"),
195 },
196 }
197 }
198}
199
200impl From<boltz_client::bitcoin::hex::HexToArrayError> for PaymentError {
201 fn from(err: boltz_client::bitcoin::hex::HexToArrayError) -> Self {
202 PaymentError::Generic {
203 err: format!("{err:?}"),
204 }
205 }
206}
207
208impl From<lwk_wollet::Error> for PaymentError {
209 fn from(err: lwk_wollet::Error) -> Self {
210 match err {
211 lwk_wollet::Error::InsufficientFunds { .. } => PaymentError::InsufficientFunds,
212 _ => PaymentError::Generic {
213 err: format!("{err:?}"),
214 },
215 }
216 }
217}
218
219#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
220impl From<lwk_wollet::UrlError> for PaymentError {
221 fn from(err: lwk_wollet::UrlError) -> Self {
222 PaymentError::Generic {
223 err: format!("{err:?}"),
224 }
225 }
226}
227
228impl From<lwk_signer::SignerError> for PaymentError {
229 fn from(err: lwk_signer::SignerError) -> Self {
230 PaymentError::SignerError {
231 err: format!("{err:?}"),
232 }
233 }
234}
235
236impl From<anyhow::Error> for PaymentError {
237 fn from(err: anyhow::Error) -> Self {
238 Self::Generic {
239 err: err.to_string(),
240 }
241 }
242}
243
244impl From<PayjoinError> for PaymentError {
245 fn from(err: PayjoinError) -> Self {
246 match err {
247 PayjoinError::InsufficientFunds => PaymentError::InsufficientFunds,
248 _ => PaymentError::Generic {
249 err: format!("{err:?}"),
250 },
251 }
252 }
253}
254
255impl From<rusqlite::Error> for PaymentError {
256 fn from(_: rusqlite::Error) -> Self {
257 Self::PersistError
258 }
259}
260
261impl From<SdkError> for PaymentError {
262 fn from(err: SdkError) -> Self {
263 Self::Generic {
264 err: err.to_string(),
265 }
266 }
267}
268
269impl From<sdk_common::bitcoin::util::bip32::Error> for PaymentError {
270 fn from(err: sdk_common::bitcoin::util::bip32::Error) -> Self {
271 Self::SignerError {
272 err: err.to_string(),
273 }
274 }
275}
276
277impl From<secp256k1::Error> for PaymentError {
278 fn from(err: secp256k1::Error) -> Self {
279 Self::Generic {
280 err: err.to_string(),
281 }
282 }
283}
284
285impl From<PaymentError> for LnUrlAuthError {
286 fn from(err: PaymentError) -> Self {
287 Self::Generic {
288 err: err.to_string(),
289 }
290 }
291}
292
293impl From<PaymentError> for LnUrlPayError {
294 fn from(err: PaymentError) -> Self {
295 match err {
296 PaymentError::AlreadyPaid => Self::AlreadyPaid,
297 PaymentError::AmountOutOfRange { min, max } => Self::InvalidAmount {
298 err: format!("Amount must be between {min} and {max}"),
299 },
300 PaymentError::AmountMissing { err } => Self::InvalidAmount {
301 err: format!("Amount is missing: {err}"),
302 },
303 PaymentError::InvalidNetwork { err } => Self::InvalidNetwork { err },
304 PaymentError::InsufficientFunds => Self::InsufficientBalance { err: String::new() },
305 PaymentError::InvalidInvoice { err } => Self::InvalidInvoice { err },
306 PaymentError::PaymentTimeout => Self::PaymentTimeout { err: String::new() },
307 _ => Self::Generic {
308 err: err.to_string(),
309 },
310 }
311 }
312}
313
314impl From<PaymentError> for LnUrlWithdrawError {
315 fn from(err: PaymentError) -> Self {
316 Self::Generic {
317 err: err.to_string(),
318 }
319 }
320}
321
322pub(crate) fn is_txn_mempool_conflict_error(err: &Error) -> bool {
323 err.to_string().contains("txn-mempool-conflict")
324}