1use crate::metadata::{DecodeWithMetadata, Metadata};
9use core::fmt::Debug;
10use scale_decode::visitor::DecodeAsTypeResult;
11use std::borrow::Cow;
12
13use super::{Error, MetadataError};
14use crate::error::RootError;
15
16#[derive(Debug, thiserror::Error, PartialEq, Eq)]
18#[non_exhaustive]
19pub enum DispatchError {
20 #[error("Some unknown error occurred.")]
22 Other,
23 #[error("Failed to lookup some data.")]
25 CannotLookup,
26 #[error("Bad origin.")]
28 BadOrigin,
29 #[error("Pallet error: {0}")]
31 Module(ModuleError),
32 #[error("At least one consumer is remaining so the account cannot be destroyed.")]
34 ConsumerRemaining,
35 #[error("There are no providers so the account cannot be created.")]
37 NoProviders,
38 #[error("There are too many consumers so the account cannot be created.")]
40 TooManyConsumers,
41 #[error("Token error: {0}")]
43 Token(TokenError),
44 #[error("Arithmetic error: {0}")]
46 Arithmetic(ArithmeticError),
47 #[error("Transactional error: {0}")]
49 Transactional(TransactionalError),
50 #[error(
52 "Resources exhausted, e.g. attempt to read/write data which is too large to manipulate."
53 )]
54 Exhausted,
55 #[error("The state is corrupt; this is generally not going to fix itself.")]
57 Corruption,
58 #[error(
60 "Some resource (e.g. a preimage) is unavailable right now. This might fix itself later."
61 )]
62 Unavailable,
63}
64
65#[derive(scale_decode::DecodeAsType, Debug, thiserror::Error, PartialEq, Eq)]
67#[non_exhaustive]
68pub enum TokenError {
69 #[error("Funds are unavailable.")]
71 FundsUnavailable,
72 #[error("Some part of the balance gives the only provider reference to the account and thus cannot be (re)moved.")]
74 OnlyProvider,
75 #[error("Account cannot exist with the funds that would be given.")]
77 BelowMinimum,
78 #[error("Account cannot be created.")]
80 CannotCreate,
81 #[error("The asset in question is unknown.")]
83 UnknownAsset,
84 #[error("Funds exist but are frozen.")]
86 Frozen,
87 #[error("Operation is not supported by the asset.")]
89 Unsupported,
90 #[error("Account cannot be created for a held balance.")]
92 CannotCreateHold,
93 #[error("Withdrawal would cause unwanted loss of account.")]
95 NotExpendable,
96}
97
98#[derive(scale_decode::DecodeAsType, Debug, thiserror::Error, PartialEq, Eq)]
100#[non_exhaustive]
101pub enum ArithmeticError {
102 #[error("Underflow.")]
104 Underflow,
105 #[error("Overflow.")]
107 Overflow,
108 #[error("Division by zero.")]
110 DivisionByZero,
111}
112
113#[derive(scale_decode::DecodeAsType, Debug, thiserror::Error, PartialEq, Eq)]
115#[non_exhaustive]
116pub enum TransactionalError {
117 #[error("Too many transactional layers have been spawned.")]
119 LimitReached,
120 #[error("A transactional layer was expected, but does not exist.")]
122 NoLayer,
123}
124
125#[derive(Clone, Debug, thiserror::Error)]
127#[non_exhaustive]
128pub struct ModuleError {
129 metadata: Metadata,
130 raw: RawModuleError,
131}
132
133impl PartialEq for ModuleError {
134 fn eq(&self, other: &Self) -> bool {
135 self.raw == other.raw
137 }
138}
139
140impl Eq for ModuleError {}
141
142impl std::fmt::Display for ModuleError {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 let Ok(details) = self.details() else {
145 return f.write_str("Unknown pallet error (pallet and error details cannot be retrieved)");
146 };
147
148 let pallet = details.pallet.name();
149 let error = &details.variant.name;
150 write!(f, "Pallet error {pallet}::{error}")
151 }
152}
153
154impl ModuleError {
155 pub fn details(&self) -> Result<ModuleErrorDetails, MetadataError> {
157 let pallet = self.metadata.pallet_by_index_err(self.raw.pallet_index)?;
158 let variant = pallet
159 .error_variant_by_index(self.raw.error[0])
160 .ok_or_else(|| MetadataError::VariantIndexNotFound(self.raw.error[0]))?;
161
162 Ok(ModuleErrorDetails { pallet, variant })
163 }
164
165 pub fn raw(&self) -> RawModuleError {
167 self.raw
168 }
169
170 pub fn as_root_error<E: RootError>(&self) -> Result<E, Error> {
173 E::root_error(
174 &self.raw.error,
175 self.details()?.pallet.name(),
176 &self.metadata,
177 )
178 }
179}
180
181pub struct ModuleErrorDetails<'a> {
183 pub pallet: crate::metadata::types::PalletMetadata<'a>,
185 pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
187}
188
189#[derive(Clone, Copy, Debug, PartialEq, Eq)]
193pub struct RawModuleError {
194 pub pallet_index: u8,
196 pub error: [u8; 4],
198}
199
200impl RawModuleError {
201 pub fn error_index(&self) -> u8 {
203 self.error[0]
205 }
206}
207
208impl DispatchError {
209 #[doc(hidden)]
211 pub fn decode_from<'a>(
212 bytes: impl Into<Cow<'a, [u8]>>,
213 metadata: Metadata,
214 ) -> Result<Self, super::Error> {
215 let bytes = bytes.into();
216 let dispatch_error_ty_id = metadata
217 .dispatch_error_ty()
218 .ok_or(MetadataError::DispatchErrorNotFound)?;
219
220 #[derive(scale_decode::DecodeAsType)]
224 enum DecodedDispatchError {
225 Other,
226 CannotLookup,
227 BadOrigin,
228 Module(DecodedModuleErrorBytes),
229 ConsumerRemaining,
230 NoProviders,
231 TooManyConsumers,
232 Token(TokenError),
233 Arithmetic(ArithmeticError),
234 Transactional(TransactionalError),
235 Exhausted,
236 Corruption,
237 Unavailable,
238 }
239
240 struct DecodedModuleErrorBytes(Vec<u8>);
244 struct DecodedModuleErrorBytesVisitor;
245 impl scale_decode::Visitor for DecodedModuleErrorBytesVisitor {
246 type Error = scale_decode::Error;
247 type Value<'scale, 'info> = DecodedModuleErrorBytes;
248 fn unchecked_decode_as_type<'scale, 'info>(
249 self,
250 input: &mut &'scale [u8],
251 _type_id: scale_decode::visitor::TypeId,
252 _types: &'info scale_info::PortableRegistry,
253 ) -> DecodeAsTypeResult<Self, Result<Self::Value<'scale, 'info>, Self::Error>>
254 {
255 DecodeAsTypeResult::Decoded(Ok(DecodedModuleErrorBytes(input.to_vec())))
256 }
257 }
258 impl scale_decode::IntoVisitor for DecodedModuleErrorBytes {
259 type Visitor = DecodedModuleErrorBytesVisitor;
260 fn into_visitor() -> Self::Visitor {
261 DecodedModuleErrorBytesVisitor
262 }
263 }
264
265 let decoded_dispatch_err = DecodedDispatchError::decode_with_metadata(
267 &mut &*bytes,
268 dispatch_error_ty_id,
269 &metadata,
270 )?;
271
272 let dispatch_error = match decoded_dispatch_err {
274 DecodedDispatchError::Other => DispatchError::Other,
276 DecodedDispatchError::CannotLookup => DispatchError::CannotLookup,
277 DecodedDispatchError::BadOrigin => DispatchError::BadOrigin,
278 DecodedDispatchError::ConsumerRemaining => DispatchError::ConsumerRemaining,
279 DecodedDispatchError::NoProviders => DispatchError::NoProviders,
280 DecodedDispatchError::TooManyConsumers => DispatchError::TooManyConsumers,
281 DecodedDispatchError::Token(val) => DispatchError::Token(val),
282 DecodedDispatchError::Arithmetic(val) => DispatchError::Arithmetic(val),
283 DecodedDispatchError::Transactional(val) => DispatchError::Transactional(val),
284 DecodedDispatchError::Exhausted => DispatchError::Exhausted,
285 DecodedDispatchError::Corruption => DispatchError::Corruption,
286 DecodedDispatchError::Unavailable => DispatchError::Unavailable,
287 DecodedDispatchError::Module(module_bytes) => {
289 let module_bytes = module_bytes.0;
290
291 let raw = if module_bytes.len() == 2 {
294 RawModuleError {
295 pallet_index: module_bytes[0],
296 error: [module_bytes[1], 0, 0, 0],
297 }
298 } else if module_bytes.len() == 5 {
299 RawModuleError {
300 pallet_index: module_bytes[0],
301 error: [
302 module_bytes[1],
303 module_bytes[2],
304 module_bytes[3],
305 module_bytes[4],
306 ],
307 }
308 } else {
309 tracing::warn!("Can't decode error sp_runtime::DispatchError: bytes do not match known shapes");
310 return Err(super::Error::Unknown(bytes.to_vec()));
312 };
313
314 DispatchError::Module(ModuleError { metadata, raw })
316 }
317 };
318
319 Ok(dispatch_error)
320 }
321}