1use super::{DispatchErrorDecodeError, ModuleErrorDecodeError, ModuleErrorDetailsError};
9use crate::metadata::Metadata;
10use core::fmt::Debug;
11use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType, TypeResolver};
12use std::{borrow::Cow, marker::PhantomData};
13
14#[derive(Debug, thiserror::Error, PartialEq, Eq)]
16#[allow(clippy::large_enum_variant)]
17#[non_exhaustive]
18pub enum DispatchError {
19 #[error("Some unknown error occurred.")]
21 Other,
22 #[error("Failed to lookup some data.")]
24 CannotLookup,
25 #[error("Bad origin.")]
27 BadOrigin,
28 #[error("Pallet error: {0}")]
30 Module(ModuleError),
31 #[error("At least one consumer is remaining so the account cannot be destroyed.")]
33 ConsumerRemaining,
34 #[error("There are no providers so the account cannot be created.")]
36 NoProviders,
37 #[error("There are too many consumers so the account cannot be created.")]
39 TooManyConsumers,
40 #[error("Token error: {0}")]
42 Token(TokenError),
43 #[error("Arithmetic error: {0}")]
45 Arithmetic(ArithmeticError),
46 #[error("Transactional error: {0}")]
48 Transactional(TransactionalError),
49 #[error(
51 "Resources exhausted, e.g. attempt to read/write data which is too large to manipulate."
52 )]
53 Exhausted,
54 #[error("The state is corrupt; this is generally not going to fix itself.")]
56 Corruption,
57 #[error(
59 "Some resource (e.g. a preimage) is unavailable right now. This might fix itself later."
60 )]
61 Unavailable,
62 #[error("Root origin is not allowed.")]
64 RootNotAllowed,
65}
66
67#[derive(scale_decode::DecodeAsType, Debug, thiserror::Error, PartialEq, Eq)]
69#[non_exhaustive]
70pub enum TokenError {
71 #[error("Funds are unavailable.")]
73 FundsUnavailable,
74 #[error(
77 "Some part of the balance gives the only provider reference to the account and thus cannot be (re)moved."
78 )]
79 OnlyProvider,
80 #[error("Account cannot exist with the funds that would be given.")]
82 BelowMinimum,
83 #[error("Account cannot be created.")]
85 CannotCreate,
86 #[error("The asset in question is unknown.")]
88 UnknownAsset,
89 #[error("Funds exist but are frozen.")]
91 Frozen,
92 #[error("Operation is not supported by the asset.")]
94 Unsupported,
95 #[error("Account cannot be created for a held balance.")]
97 CannotCreateHold,
98 #[error("Withdrawal would cause unwanted loss of account.")]
100 NotExpendable,
101 #[error("Account cannot receive the assets.")]
103 Blocked,
104}
105
106#[derive(scale_decode::DecodeAsType, Debug, thiserror::Error, PartialEq, Eq)]
108#[non_exhaustive]
109pub enum ArithmeticError {
110 #[error("Underflow.")]
112 Underflow,
113 #[error("Overflow.")]
115 Overflow,
116 #[error("Division by zero.")]
118 DivisionByZero,
119}
120
121#[derive(scale_decode::DecodeAsType, Debug, thiserror::Error, PartialEq, Eq)]
123#[non_exhaustive]
124pub enum TransactionalError {
125 #[error("Too many transactional layers have been spawned.")]
127 LimitReached,
128 #[error("A transactional layer was expected, but does not exist.")]
130 NoLayer,
131}
132
133#[derive(Clone, thiserror::Error)]
135#[non_exhaustive]
136pub struct ModuleError {
137 metadata: Metadata,
138 bytes: [u8; 5],
143}
144
145impl PartialEq for ModuleError {
146 fn eq(&self, other: &Self) -> bool {
147 self.bytes == other.bytes
149 }
150}
151
152impl Eq for ModuleError {}
153
154impl Debug for ModuleError {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 let details = self.details_string();
159 write!(f, "ModuleError(<{details}>)")
160 }
161}
162
163impl std::fmt::Display for ModuleError {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 let details = self.details_string();
166 write!(f, "{details}")
167 }
168}
169
170impl ModuleError {
171 pub fn details(&self) -> Result<ModuleErrorDetails<'_>, ModuleErrorDetailsError> {
173 let pallet = self
174 .metadata
175 .pallet_by_error_index(self.pallet_index())
176 .ok_or(ModuleErrorDetailsError::PalletNotFound { pallet_index: self.pallet_index() })?;
177
178 let variant = pallet.error_variant_by_index(self.error_index()).ok_or_else(|| {
179 ModuleErrorDetailsError::ErrorVariantNotFound {
180 pallet_name: pallet.name().into(),
181 error_index: self.error_index(),
182 }
183 })?;
184
185 Ok(ModuleErrorDetails { pallet, variant })
186 }
187
188 pub fn details_string(&self) -> String {
190 match self.details() {
191 Ok(details) => format!(
192 "{pallet_name}::{variant_name}",
193 pallet_name = details.pallet.name(),
194 variant_name = details.variant.name,
195 ),
196 Err(_) => format!(
197 "Unknown pallet error '{bytes:?}' (pallet and error details cannot be retrieved)",
198 bytes = self.bytes
199 ),
200 }
201 }
202
203 pub fn bytes(&self) -> [u8; 5] {
205 self.bytes
206 }
207
208 pub fn pallet_index(&self) -> u8 {
210 self.bytes[0]
211 }
212
213 pub fn error_index(&self) -> u8 {
215 self.bytes[1]
216 }
217
218 pub fn as_root_error<E: DecodeAsType>(&self) -> Result<E, ModuleErrorDecodeError> {
220 let decoded = E::decode_as_type(
221 &mut &self.bytes[..],
222 self.metadata.outer_enums().error_enum_ty(),
223 self.metadata.types(),
224 )
225 .map_err(ModuleErrorDecodeError)?;
226
227 Ok(decoded)
228 }
229}
230
231pub struct ModuleErrorDetails<'a> {
233 pub pallet: pezkuwi_subxt_metadata::PalletMetadata<'a>,
235 pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
237}
238
239impl DispatchError {
240 #[doc(hidden)]
242 pub fn decode_from<'a>(
243 bytes: impl Into<Cow<'a, [u8]>>,
244 metadata: Metadata,
245 ) -> Result<Self, DispatchErrorDecodeError> {
246 let bytes = bytes.into();
247 let dispatch_error_ty_id = metadata
248 .dispatch_error_ty()
249 .ok_or(DispatchErrorDecodeError::DispatchErrorTypeIdNotFound)?;
250
251 #[derive(scale_decode::DecodeAsType)]
255 enum DecodedDispatchError {
256 Other,
257 CannotLookup,
258 BadOrigin,
259 Module(DecodedModuleErrorBytes),
260 ConsumerRemaining,
261 NoProviders,
262 TooManyConsumers,
263 Token(TokenError),
264 Arithmetic(ArithmeticError),
265 Transactional(TransactionalError),
266 Exhausted,
267 Corruption,
268 Unavailable,
269 RootNotAllowed,
270 }
271
272 struct DecodedModuleErrorBytes(Vec<u8>);
276 struct DecodedModuleErrorBytesVisitor<R: TypeResolver>(PhantomData<R>);
277 impl<R: TypeResolver> scale_decode::Visitor for DecodedModuleErrorBytesVisitor<R> {
278 type Error = scale_decode::Error;
279 type Value<'scale, 'info> = DecodedModuleErrorBytes;
280 type TypeResolver = R;
281
282 fn unchecked_decode_as_type<'scale, 'info>(
283 self,
284 input: &mut &'scale [u8],
285 _type_id: R::TypeId,
286 _types: &'info R,
287 ) -> DecodeAsTypeResult<Self, Result<Self::Value<'scale, 'info>, Self::Error>> {
288 DecodeAsTypeResult::Decoded(Ok(DecodedModuleErrorBytes(input.to_vec())))
289 }
290 }
291
292 impl scale_decode::IntoVisitor for DecodedModuleErrorBytes {
293 type AnyVisitor<R: TypeResolver> = DecodedModuleErrorBytesVisitor<R>;
294 fn into_visitor<R: TypeResolver>() -> DecodedModuleErrorBytesVisitor<R> {
295 DecodedModuleErrorBytesVisitor(PhantomData)
296 }
297 }
298
299 let decoded_dispatch_err = DecodedDispatchError::decode_as_type(
301 &mut &*bytes,
302 dispatch_error_ty_id,
303 metadata.types(),
304 )
305 .map_err(DispatchErrorDecodeError::CouldNotDecodeDispatchError)?;
306
307 let dispatch_error = match decoded_dispatch_err {
309 DecodedDispatchError::Other => DispatchError::Other,
311 DecodedDispatchError::CannotLookup => DispatchError::CannotLookup,
312 DecodedDispatchError::BadOrigin => DispatchError::BadOrigin,
313 DecodedDispatchError::ConsumerRemaining => DispatchError::ConsumerRemaining,
314 DecodedDispatchError::NoProviders => DispatchError::NoProviders,
315 DecodedDispatchError::TooManyConsumers => DispatchError::TooManyConsumers,
316 DecodedDispatchError::Token(val) => DispatchError::Token(val),
317 DecodedDispatchError::Arithmetic(val) => DispatchError::Arithmetic(val),
318 DecodedDispatchError::Transactional(val) => DispatchError::Transactional(val),
319 DecodedDispatchError::Exhausted => DispatchError::Exhausted,
320 DecodedDispatchError::Corruption => DispatchError::Corruption,
321 DecodedDispatchError::Unavailable => DispatchError::Unavailable,
322 DecodedDispatchError::RootNotAllowed => DispatchError::RootNotAllowed,
323 DecodedDispatchError::Module(module_bytes) => {
326 let module_bytes = module_bytes.0;
327
328 let bytes = if module_bytes.len() == 2 {
331 [module_bytes[0], module_bytes[1], 0, 0, 0]
332 } else if module_bytes.len() == 5 {
333 [
334 module_bytes[0],
335 module_bytes[1],
336 module_bytes[2],
337 module_bytes[3],
338 module_bytes[4],
339 ]
340 } else {
341 tracing::warn!(
342 "Can't decode error sp_runtime::DispatchError: bytes do not match known shapes"
343 );
344 return Err(DispatchErrorDecodeError::CouldNotDecodeModuleError {
346 bytes: bytes.to_vec(),
347 });
348 };
349
350 DispatchError::Module(ModuleError { metadata, bytes })
352 },
353 };
354
355 Ok(dispatch_error)
356 }
357}