pezkuwi_subxt/error/
mod.rs

1// Copyright 2019-2025 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5//! Types representing the errors that can be returned.
6
7mod dispatch_error;
8mod hex;
9
10crate::macros::cfg_unstable_light_client! {
11	pub use pezkuwi_subxt_lightclient::LightClientError;
12}
13
14// Re-export dispatch error types:
15pub use dispatch_error::{
16	ArithmeticError, DispatchError, ModuleError, TokenError, TransactionalError,
17};
18
19// Re-expose the errors we use from other crates here:
20pub use crate::Metadata;
21pub use hex::Hex;
22pub use pezkuwi_subxt_metadata::TryFromError as MetadataTryFromError;
23pub use scale_decode::Error as DecodeError;
24pub use scale_encode::Error as EncodeError;
25
26// Re-export core error types we're just reusing.
27pub use pezkuwi_subxt_core::error::{
28	ConstantError,
29	CustomValueError,
30	EventsError as CoreEventsError,
31	// These errors are exposed as-is:
32	ExtrinsicDecodeErrorAt,
33	// These errors are wrapped:
34	ExtrinsicError as CoreExtrinsicError,
35	RuntimeApiError as CoreRuntimeApiError,
36	StorageError as CoreStorageError,
37	StorageKeyError,
38	StorageValueError,
39	ViewFunctionError as CoreViewFunctionError,
40};
41
42/// A global error type. Any of the errors exposed here can convert into this
43/// error via `.into()`, but this error isn't itself exposed from anything.
44#[derive(Debug, thiserror::Error)]
45#[non_exhaustive]
46#[allow(missing_docs)]
47pub enum Error {
48	#[error(transparent)]
49	ExtrinsicDecodeErrorAt(#[from] ExtrinsicDecodeErrorAt),
50	#[error(transparent)]
51	ConstantError(#[from] ConstantError),
52	#[error(transparent)]
53	CustomValueError(#[from] CustomValueError),
54	#[error(transparent)]
55	StorageKeyError(#[from] StorageKeyError),
56	#[error(transparent)]
57	StorageValueError(#[from] StorageValueError),
58	#[error(transparent)]
59	BackendError(#[from] BackendError),
60	#[error(transparent)]
61	BlockError(#[from] BlockError),
62	#[error(transparent)]
63	AccountNonceError(#[from] AccountNonceError),
64	#[error(transparent)]
65	OnlineClientError(#[from] OnlineClientError),
66	#[error(transparent)]
67	RuntimeUpdaterError(#[from] RuntimeUpdaterError),
68	#[error(transparent)]
69	RuntimeUpdateeApplyError(#[from] RuntimeUpdateeApplyError),
70	#[error(transparent)]
71	RuntimeApiError(#[from] RuntimeApiError),
72	#[error(transparent)]
73	EventsError(#[from] EventsError),
74	#[error(transparent)]
75	ExtrinsicError(#[from] ExtrinsicError),
76	#[error(transparent)]
77	ViewFunctionError(#[from] ViewFunctionError),
78	#[error(transparent)]
79	TransactionProgressError(#[from] TransactionProgressError),
80	#[error(transparent)]
81	TransactionStatusError(#[from] TransactionStatusError),
82	#[error(transparent)]
83	TransactionEventsError(#[from] TransactionEventsError),
84	#[error(transparent)]
85	TransactionFinalizedSuccessError(#[from] TransactionFinalizedSuccessError),
86	#[error(transparent)]
87	ModuleErrorDetailsError(#[from] ModuleErrorDetailsError),
88	#[error(transparent)]
89	ModuleErrorDecodeError(#[from] ModuleErrorDecodeError),
90	#[error(transparent)]
91	DispatchErrorDecodeError(#[from] DispatchErrorDecodeError),
92	#[error(transparent)]
93	StorageError(#[from] StorageError),
94	// Dev note: Subxt doesn't directly return Raw* errors. These exist so that when
95	// users use common crates (like parity-scale-codec and subxt-rpcs), errors returned
96	// there can be handled automatically using ? when the expected error is pezkuwi_subxt::Error.
97	#[error("Other RPC client error: {0}")]
98	OtherRpcClientError(#[from] pezkuwi_subxt_rpcs::Error),
99	#[error("Other codec error: {0}")]
100	OtherCodecError(#[from] codec::Error),
101	#[cfg(feature = "unstable-light-client")]
102	#[error("Other light client error: {0}")]
103	OtherLightClientError(#[from] pezkuwi_subxt_lightclient::LightClientError),
104	#[cfg(feature = "unstable-light-client")]
105	#[error("Other light client RPC error: {0}")]
106	OtherLightClientRpcError(#[from] pezkuwi_subxt_lightclient::LightClientRpcError),
107	// Dev note: Nothing in subxt should ever emit this error. It can instead be used
108	// to easily map other errors into a pezkuwi_subxt::Error for convenience. Some From impls
109	// make this automatic for common "other" error types.
110	#[error("Other error: {0}")]
111	Other(Box<dyn std::error::Error + Send + Sync + 'static>),
112}
113
114impl From<std::convert::Infallible> for Error {
115	fn from(value: std::convert::Infallible) -> Self {
116		match value {}
117	}
118}
119
120impl Error {
121	/// Create a generic error. This is a quick workaround when you are using
122	/// [`Error`] and have a non-Subxt error to return.
123	pub fn other<E: std::error::Error + Send + Sync + 'static>(error: E) -> Error {
124		Error::Other(Box::new(error))
125	}
126
127	/// Create a generic error from a string. This is a quick workaround when you are using
128	/// [`Error`] and have a non-Subxt error to return.
129	pub fn other_str(error: impl Into<String>) -> Error {
130		#[derive(thiserror::Error, Debug, Clone)]
131		#[error("{0}")]
132		struct StrError(String);
133		Error::Other(Box::new(StrError(error.into())))
134	}
135
136	/// Checks whether the error was caused by a RPC re-connection.
137	pub fn is_disconnected_will_reconnect(&self) -> bool {
138		matches!(
139			self.backend_error(),
140			Some(BackendError::Rpc(RpcError::ClientError(
141				pezkuwi_subxt_rpcs::Error::DisconnectedWillReconnect(_)
142			)))
143		)
144	}
145
146	/// Checks whether the error was caused by a RPC request being rejected.
147	pub fn is_rpc_limit_reached(&self) -> bool {
148		matches!(self.backend_error(), Some(BackendError::Rpc(RpcError::LimitReached)))
149	}
150
151	fn backend_error(&self) -> Option<&BackendError> {
152		match self {
153			Error::BlockError(e) => e.backend_error(),
154			Error::AccountNonceError(e) => e.backend_error(),
155			Error::OnlineClientError(e) => e.backend_error(),
156			Error::RuntimeUpdaterError(e) => e.backend_error(),
157			Error::RuntimeApiError(e) => e.backend_error(),
158			Error::EventsError(e) => e.backend_error(),
159			Error::ExtrinsicError(e) => e.backend_error(),
160			Error::ViewFunctionError(e) => e.backend_error(),
161			Error::TransactionProgressError(e) => e.backend_error(),
162			Error::TransactionEventsError(e) => e.backend_error(),
163			Error::TransactionFinalizedSuccessError(e) => e.backend_error(),
164			Error::StorageError(e) => e.backend_error(),
165			// Any errors that **don't** return a BackendError anywhere will return None:
166			_ => None,
167		}
168	}
169}
170
171#[derive(Debug, thiserror::Error)]
172#[non_exhaustive]
173#[allow(missing_docs)]
174pub enum BackendError {
175	#[error("Backend error: RPC error: {0}")]
176	Rpc(#[from] RpcError),
177	#[error("Backend error: Could not find metadata version {0}")]
178	MetadataVersionNotFound(u32),
179	#[error("Backend error: Could not codec::Decode Runtime API response: {0}")]
180	CouldNotScaleDecodeRuntimeResponse(codec::Error),
181	#[error(
182		"Backend error: Could not codec::Decode metadata bytes into pezkuwi_subxt::Metadata: {0}"
183	)]
184	CouldNotDecodeMetadata(codec::Error),
185	// This is for errors in `Backend` implementations which aren't any of the "pre-defined" set
186	// above:
187	#[error("Custom backend error: {0}")]
188	Other(String),
189}
190
191impl BackendError {
192	/// Checks whether the error was caused by a RPC re-connection.
193	pub fn is_disconnected_will_reconnect(&self) -> bool {
194		matches!(
195			self,
196			BackendError::Rpc(RpcError::ClientError(
197				pezkuwi_subxt_rpcs::Error::DisconnectedWillReconnect(_)
198			))
199		)
200	}
201
202	/// Checks whether the error was caused by a RPC request being rejected.
203	pub fn is_rpc_limit_reached(&self) -> bool {
204		matches!(self, BackendError::Rpc(RpcError::LimitReached))
205	}
206}
207
208impl From<pezkuwi_subxt_rpcs::Error> for BackendError {
209	fn from(value: pezkuwi_subxt_rpcs::Error) -> Self {
210		BackendError::Rpc(RpcError::ClientError(value))
211	}
212}
213
214/// An RPC error. Since we are generic over the RPC client that is used,
215/// the error is boxed and could be casted.
216#[derive(Debug, thiserror::Error)]
217#[non_exhaustive]
218pub enum RpcError {
219	/// Error related to the RPC client.
220	#[error("RPC error: {0}")]
221	ClientError(#[from] pezkuwi_subxt_rpcs::Error),
222	/// This error signals that we got back a
223	/// [`pezkuwi_subxt_rpcs::methods::chain_head::MethodResponse::LimitReached`], which is not
224	/// technically an RPC error but is treated as an error in our own APIs.
225	#[error("RPC error: limit reached")]
226	LimitReached,
227	/// The RPC subscription was dropped.
228	#[error("RPC error: subscription dropped.")]
229	SubscriptionDropped,
230}
231
232/// Block error
233#[derive(Debug, thiserror::Error)]
234#[non_exhaustive]
235#[allow(missing_docs)]
236pub enum BlockError {
237	#[error(
238		"Could not find the block body with hash {block_hash} (perhaps it was on a non-finalized fork?)"
239	)]
240	BlockNotFound { block_hash: Hex },
241	#[error("Could not download the block header with hash {block_hash}: {reason}")]
242	CouldNotGetBlockHeader { block_hash: Hex, reason: BackendError },
243	#[error("Could not download the latest block header: {0}")]
244	CouldNotGetLatestBlock(BackendError),
245	#[error("Could not subscribe to all blocks: {0}")]
246	CouldNotSubscribeToAllBlocks(BackendError),
247	#[error("Could not subscribe to best blocks: {0}")]
248	CouldNotSubscribeToBestBlocks(BackendError),
249	#[error("Could not subscribe to finalized blocks: {0}")]
250	CouldNotSubscribeToFinalizedBlocks(BackendError),
251	#[error("Error getting account nonce at block {block_hash}")]
252	AccountNonceError { block_hash: Hex, account_id: Hex, reason: AccountNonceError },
253}
254
255impl BlockError {
256	fn backend_error(&self) -> Option<&BackendError> {
257		match self {
258			BlockError::CouldNotGetBlockHeader { reason: e, .. }
259			| BlockError::CouldNotGetLatestBlock(e)
260			| BlockError::CouldNotSubscribeToAllBlocks(e)
261			| BlockError::CouldNotSubscribeToBestBlocks(e)
262			| BlockError::CouldNotSubscribeToFinalizedBlocks(e) => Some(e),
263			_ => None,
264		}
265	}
266}
267
268#[derive(Debug, thiserror::Error)]
269#[non_exhaustive]
270#[allow(missing_docs)]
271pub enum AccountNonceError {
272	#[error("Could not retrieve account nonce: {0}")]
273	CouldNotRetrieve(#[from] BackendError),
274	#[error("Could not decode account nonce: {0}")]
275	CouldNotDecode(#[from] codec::Error),
276	#[error("Wrong number of account nonce bytes returned: {0} (expected 2, 4 or 8)")]
277	WrongNumberOfBytes(usize),
278}
279
280impl AccountNonceError {
281	fn backend_error(&self) -> Option<&BackendError> {
282		match self {
283			AccountNonceError::CouldNotRetrieve(e) => Some(e),
284			_ => None,
285		}
286	}
287}
288
289#[derive(Debug, thiserror::Error)]
290#[non_exhaustive]
291#[allow(missing_docs)]
292pub enum OnlineClientError {
293	#[error("Cannot construct OnlineClient: {0}")]
294	RpcError(#[from] pezkuwi_subxt_rpcs::Error),
295	#[error(
296		"Cannot construct OnlineClient: Cannot fetch latest finalized block to obtain init details from: {0}"
297	)]
298	CannotGetLatestFinalizedBlock(BackendError),
299	#[error("Cannot construct OnlineClient: Cannot fetch genesis hash: {0}")]
300	CannotGetGenesisHash(BackendError),
301	#[error("Cannot construct OnlineClient: Cannot fetch current runtime version: {0}")]
302	CannotGetCurrentRuntimeVersion(BackendError),
303	#[error("Cannot construct OnlineClient: Cannot fetch metadata: {0}")]
304	CannotFetchMetadata(BackendError),
305}
306
307impl OnlineClientError {
308	fn backend_error(&self) -> Option<&BackendError> {
309		match self {
310			OnlineClientError::CannotGetLatestFinalizedBlock(e)
311			| OnlineClientError::CannotGetGenesisHash(e)
312			| OnlineClientError::CannotGetCurrentRuntimeVersion(e)
313			| OnlineClientError::CannotFetchMetadata(e) => Some(e),
314			_ => None,
315		}
316	}
317}
318
319#[derive(Debug, thiserror::Error)]
320#[non_exhaustive]
321#[allow(missing_docs)]
322pub enum RuntimeUpdaterError {
323	#[error("Error subscribing to runtime updates: The update stream ended unexpectedly")]
324	UnexpectedEndOfUpdateStream,
325	#[error("Error subscribing to runtime updates: The finalized block stream ended unexpectedly")]
326	UnexpectedEndOfBlockStream,
327	#[error("Error subscribing to runtime updates: Can't stream runtime version: {0}")]
328	CannotStreamRuntimeVersion(BackendError),
329	#[error("Error subscribing to runtime updates: Can't get next runtime version in stream: {0}")]
330	CannotGetNextRuntimeVersion(BackendError),
331	#[error("Error subscribing to runtime updates: Cannot stream finalized blocks: {0}")]
332	CannotStreamFinalizedBlocks(BackendError),
333	#[error(
334		"Error subscribing to runtime updates: Cannot get next finalized block in stream: {0}"
335	)]
336	CannotGetNextFinalizedBlock(BackendError),
337	#[error("Cannot fetch new metadata for runtime update: {0}")]
338	CannotFetchNewMetadata(BackendError),
339	#[error(
340		"Error subscribing to runtime updates: Cannot find the System.LastRuntimeUpgrade storage entry"
341	)]
342	CantFindSystemLastRuntimeUpgrade,
343	#[error("Error subscribing to runtime updates: Cannot fetch last runtime upgrade: {0}")]
344	CantFetchLastRuntimeUpgrade(StorageError),
345	#[error("Error subscribing to runtime updates: Cannot decode last runtime upgrade: {0}")]
346	CannotDecodeLastRuntimeUpgrade(StorageValueError),
347}
348
349impl RuntimeUpdaterError {
350	fn backend_error(&self) -> Option<&BackendError> {
351		match self {
352			RuntimeUpdaterError::CannotStreamRuntimeVersion(e)
353			| RuntimeUpdaterError::CannotGetNextRuntimeVersion(e)
354			| RuntimeUpdaterError::CannotStreamFinalizedBlocks(e)
355			| RuntimeUpdaterError::CannotGetNextFinalizedBlock(e)
356			| RuntimeUpdaterError::CannotFetchNewMetadata(e) => Some(e),
357			_ => None,
358		}
359	}
360}
361
362/// Error that can occur during upgrade.
363#[non_exhaustive]
364#[derive(Debug, thiserror::Error)]
365#[allow(missing_docs)]
366pub enum RuntimeUpdateeApplyError {
367	#[error("The proposed runtime update is the same as the current version")]
368	SameVersion,
369}
370
371/// Error working with Runtime APIs
372#[non_exhaustive]
373#[derive(Debug, thiserror::Error)]
374#[allow(missing_docs)]
375pub enum RuntimeApiError {
376	#[error(
377		"Cannot access Runtime APIs at latest block: Cannot fetch latest finalized block: {0}"
378	)]
379	CannotGetLatestFinalizedBlock(BackendError),
380	#[error("{0}")]
381	OfflineError(#[from] CoreRuntimeApiError),
382	#[error("Cannot call the Runtime API: {0}")]
383	CannotCallApi(BackendError),
384}
385
386impl RuntimeApiError {
387	fn backend_error(&self) -> Option<&BackendError> {
388		match self {
389			RuntimeApiError::CannotGetLatestFinalizedBlock(e)
390			| RuntimeApiError::CannotCallApi(e) => Some(e),
391			_ => None,
392		}
393	}
394}
395
396/// Error working with events.
397#[non_exhaustive]
398#[derive(Debug, thiserror::Error)]
399#[allow(missing_docs)]
400pub enum EventsError {
401	#[error("{0}")]
402	OfflineError(#[from] CoreEventsError),
403	#[error("Cannot access events at latest block: Cannot fetch latest finalized block: {0}")]
404	CannotGetLatestFinalizedBlock(BackendError),
405	#[error("Cannot fetch event bytes: {0}")]
406	CannotFetchEventBytes(BackendError),
407}
408
409impl EventsError {
410	fn backend_error(&self) -> Option<&BackendError> {
411		match self {
412			EventsError::CannotGetLatestFinalizedBlock(e)
413			| EventsError::CannotFetchEventBytes(e) => Some(e),
414			_ => None,
415		}
416	}
417}
418
419/// Error working with extrinsics.
420#[non_exhaustive]
421#[derive(Debug, thiserror::Error)]
422#[allow(missing_docs)]
423pub enum ExtrinsicError {
424	#[error("{0}")]
425	OfflineError(#[from] CoreExtrinsicError),
426	#[error("Could not download block body to extract extrinsics from: {0}")]
427	CannotGetBlockBody(BackendError),
428	#[error("Block not found: {0}")]
429	BlockNotFound(Hex),
430	#[error("{0}")]
431	CouldNotDecodeExtrinsics(#[from] ExtrinsicDecodeErrorAt),
432	#[error(
433		"Extrinsic submission error: Cannot get latest finalized block to grab account nonce at: {0}"
434	)]
435	CannotGetLatestFinalizedBlock(BackendError),
436	#[error("Cannot find block header for block {block_hash}")]
437	CannotFindBlockHeader { block_hash: Hex },
438	#[error("Error getting account nonce at block {block_hash}")]
439	AccountNonceError { block_hash: Hex, account_id: Hex, reason: AccountNonceError },
440	#[error("Cannot submit extrinsic: {0}")]
441	ErrorSubmittingTransaction(BackendError),
442	#[error("A transaction status error was returned while submitting the extrinsic: {0}")]
443	TransactionStatusError(TransactionStatusError),
444	#[error(
445		"The transaction status stream encountered an error while submitting the extrinsic: {0}"
446	)]
447	TransactionStatusStreamError(BackendError),
448	#[error(
449		"The transaction status stream unexpectedly ended, so we don't know the status of the submitted extrinsic"
450	)]
451	UnexpectedEndOfTransactionStatusStream,
452	#[error("Cannot get fee info from Runtime API: {0}")]
453	CannotGetFeeInfo(BackendError),
454	#[error("Cannot get validation info from Runtime API: {0}")]
455	CannotGetValidationInfo(BackendError),
456	#[error("Cannot decode ValidationResult bytes: {0}")]
457	CannotDecodeValidationResult(codec::Error),
458	#[error("ValidationResult bytes could not be decoded")]
459	UnexpectedValidationResultBytes(Vec<u8>),
460}
461
462impl ExtrinsicError {
463	fn backend_error(&self) -> Option<&BackendError> {
464		match self {
465			ExtrinsicError::CannotGetBlockBody(e)
466			| ExtrinsicError::CannotGetLatestFinalizedBlock(e)
467			| ExtrinsicError::ErrorSubmittingTransaction(e)
468			| ExtrinsicError::TransactionStatusStreamError(e)
469			| ExtrinsicError::CannotGetFeeInfo(e)
470			| ExtrinsicError::CannotGetValidationInfo(e) => Some(e),
471			ExtrinsicError::AccountNonceError { reason, .. } => reason.backend_error(),
472			_ => None,
473		}
474	}
475}
476
477/// Error working with View Functions.
478#[non_exhaustive]
479#[derive(Debug, thiserror::Error)]
480#[allow(missing_docs)]
481pub enum ViewFunctionError {
482	#[error("{0}")]
483	OfflineError(#[from] CoreViewFunctionError),
484	#[error(
485		"Cannot access View Functions at latest block: Cannot fetch latest finalized block: {0}"
486	)]
487	CannotGetLatestFinalizedBlock(BackendError),
488	#[error("Cannot call the View Function Runtime API: {0}")]
489	CannotCallApi(BackendError),
490}
491
492impl ViewFunctionError {
493	fn backend_error(&self) -> Option<&BackendError> {
494		match self {
495			ViewFunctionError::CannotGetLatestFinalizedBlock(e)
496			| ViewFunctionError::CannotCallApi(e) => Some(e),
497			_ => None,
498		}
499	}
500}
501
502/// Error during the transaction progress.
503#[non_exhaustive]
504#[derive(Debug, thiserror::Error)]
505#[allow(missing_docs)]
506pub enum TransactionProgressError {
507	#[error("Cannot get the next transaction progress update: {0}")]
508	CannotGetNextProgressUpdate(BackendError),
509	#[error("Error during transaction progress: {0}")]
510	TransactionStatusError(#[from] TransactionStatusError),
511	#[error(
512		"The transaction status stream unexpectedly ended, so we have no further transaction progress updates"
513	)]
514	UnexpectedEndOfTransactionStatusStream,
515}
516
517impl TransactionProgressError {
518	fn backend_error(&self) -> Option<&BackendError> {
519		match self {
520			TransactionProgressError::CannotGetNextProgressUpdate(e) => Some(e),
521			TransactionProgressError::TransactionStatusError(_) => None,
522			TransactionProgressError::UnexpectedEndOfTransactionStatusStream => None,
523		}
524	}
525}
526
527/// An error emitted as the result of a transaction progress update.
528#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
529#[non_exhaustive]
530#[allow(missing_docs)]
531pub enum TransactionStatusError {
532	/// An error happened on the node that the transaction was submitted to.
533	#[error("Error handling transaction: {0}")]
534	Error(String),
535	/// The transaction was deemed invalid.
536	#[error("The transaction is not valid: {0}")]
537	Invalid(String),
538	/// The transaction was dropped.
539	#[error("The transaction was dropped: {0}")]
540	Dropped(String),
541}
542
543/// Error fetching events for a just-submitted transaction
544#[derive(Debug, thiserror::Error)]
545#[non_exhaustive]
546#[allow(missing_docs)]
547pub enum TransactionEventsError {
548	#[error(
549		"The block containing the submitted transaction ({block_hash}) could not be downloaded: {error}"
550	)]
551	CannotFetchBlockBody { block_hash: Hex, error: BackendError },
552	#[error(
553		"Cannot find the the submitted transaction (hash: {transaction_hash}) in the block (hash: {block_hash}) it is supposed to be in."
554	)]
555	CannotFindTransactionInBlock { block_hash: Hex, transaction_hash: Hex },
556	#[error("The block containing the submitted transaction ({block_hash}) could not be found")]
557	BlockNotFound { block_hash: Hex },
558	#[error(
559		"Could not decode event at index {event_index} for the submitted transaction at block {block_hash}: {error}"
560	)]
561	CannotDecodeEventInBlock { event_index: usize, block_hash: Hex, error: EventsError },
562	#[error("Could not fetch events for the submitted transaction: {error}")]
563	CannotFetchEventsForTransaction { block_hash: Hex, transaction_hash: Hex, error: EventsError },
564	#[error("The transaction led to a DispatchError, but we failed to decode it: {error}")]
565	CannotDecodeDispatchError { error: DispatchErrorDecodeError, bytes: Vec<u8> },
566	#[error("The transaction failed with the following dispatch error: {0}")]
567	ExtrinsicFailed(#[from] DispatchError),
568}
569
570impl TransactionEventsError {
571	fn backend_error(&self) -> Option<&BackendError> {
572		match self {
573			TransactionEventsError::CannotFetchBlockBody { error, .. } => Some(error),
574			TransactionEventsError::CannotDecodeEventInBlock { error, .. }
575			| TransactionEventsError::CannotFetchEventsForTransaction { error, .. } => error.backend_error(),
576			_ => None,
577		}
578	}
579}
580
581/// Error waiting for the transaction to be finalized and successful.
582#[derive(Debug, thiserror::Error)]
583#[non_exhaustive]
584#[allow(missing_docs, clippy::large_enum_variant)]
585pub enum TransactionFinalizedSuccessError {
586	#[error("Could not finalize the transaction: {0}")]
587	FinalizationError(#[from] TransactionProgressError),
588	#[error("The transaction did not succeed: {0}")]
589	SuccessError(#[from] TransactionEventsError),
590}
591
592impl TransactionFinalizedSuccessError {
593	fn backend_error(&self) -> Option<&BackendError> {
594		match self {
595			TransactionFinalizedSuccessError::FinalizationError(e) => e.backend_error(),
596			TransactionFinalizedSuccessError::SuccessError(e) => e.backend_error(),
597		}
598	}
599}
600
601/// Error decoding the [`DispatchError`]
602#[derive(Debug, thiserror::Error)]
603#[non_exhaustive]
604#[allow(missing_docs)]
605pub enum ModuleErrorDetailsError {
606	#[error(
607		"Could not get details for the DispatchError: could not find pallet index {pallet_index}"
608	)]
609	PalletNotFound { pallet_index: u8 },
610	#[error(
611		"Could not get details for the DispatchError: could not find error index {error_index} in pallet {pallet_name}"
612	)]
613	ErrorVariantNotFound { pallet_name: String, error_index: u8 },
614}
615
616/// Error decoding the [`ModuleError`]
617#[derive(Debug, thiserror::Error)]
618#[non_exhaustive]
619#[allow(missing_docs)]
620#[error("Could not decode the DispatchError::Module payload into the given type: {0}")]
621pub struct ModuleErrorDecodeError(scale_decode::Error);
622
623/// Error decoding the [`DispatchError`]
624#[derive(Debug, thiserror::Error)]
625#[non_exhaustive]
626#[allow(missing_docs)]
627pub enum DispatchErrorDecodeError {
628	#[error(
629		"Could not decode the DispatchError: could not find the corresponding type ID in the metadata"
630	)]
631	DispatchErrorTypeIdNotFound,
632	#[error("Could not decode the DispatchError: {0}")]
633	CouldNotDecodeDispatchError(scale_decode::Error),
634	#[error("Could not decode the DispatchError::Module variant")]
635	CouldNotDecodeModuleError {
636		/// The bytes corresponding to the Module variant we were unable to decode:
637		bytes: Vec<u8>,
638	},
639}
640
641/// Error working with storage.
642#[derive(Debug, thiserror::Error)]
643#[non_exhaustive]
644#[allow(missing_docs)]
645pub enum StorageError {
646	#[error("{0}")]
647	Offline(#[from] CoreStorageError),
648	#[error("Cannot access storage at latest block: Cannot fetch latest finalized block: {0}")]
649	CannotGetLatestFinalizedBlock(BackendError),
650	#[error(
651		"No storage value found at the given address, and no default value to fall back to using."
652	)]
653	NoValueFound,
654	#[error("Cannot fetch the storage value: {0}")]
655	CannotFetchValue(BackendError),
656	#[error("Cannot iterate storage values: {0}")]
657	CannotIterateValues(BackendError),
658	#[error("Encountered an error iterating over storage values: {0}")]
659	StreamFailure(BackendError),
660	#[error("Cannot decode the storage version for a given entry: {0}")]
661	CannotDecodeStorageVersion(codec::Error),
662}
663
664impl StorageError {
665	fn backend_error(&self) -> Option<&BackendError> {
666		match self {
667			StorageError::CannotGetLatestFinalizedBlock(e)
668			| StorageError::CannotFetchValue(e)
669			| StorageError::CannotIterateValues(e)
670			| StorageError::StreamFailure(e) => Some(e),
671			_ => None,
672		}
673	}
674}