use super::{Error, MetadataError};
use crate::metadata::Metadata;
use alloc::{
borrow::Cow,
string::{String, ToString},
vec::Vec,
};
use codec::{Decode, Encode};
use core::{fmt::Debug, marker::PhantomData};
use derive_more::From;
use log::*;
use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType, TypeResolver};
#[derive(Debug, From, PartialEq)]
pub enum DispatchError {
Other,
CannotLookup,
BadOrigin,
Module(ModuleError),
ConsumerRemaining,
NoProviders,
TooManyConsumers,
Token(TokenError),
Arithmetic(ArithmeticError),
Transactional(TransactionalError),
Exhausted,
Corruption,
Unavailable,
RootNotAllowed,
}
impl DispatchError {
pub fn decode_from<'a>(
bytes: impl Into<Cow<'a, [u8]>>,
metadata: &Metadata,
) -> Result<Self, Error> {
let bytes = bytes.into();
let dispatch_error_ty_id =
metadata.dispatch_error_ty().ok_or(MetadataError::DispatchErrorNotFound)?;
#[derive(DecodeAsType)]
enum DecodedDispatchError {
Other,
CannotLookup,
BadOrigin,
Module(DecodedModuleErrorBytes),
ConsumerRemaining,
NoProviders,
TooManyConsumers,
Token(TokenError),
Arithmetic(ArithmeticError),
Transactional(TransactionalError),
Exhausted,
Corruption,
Unavailable,
RootNotAllowed,
}
struct DecodedModuleErrorBytes(Vec<u8>);
struct DecodedModuleErrorBytesVisitor<R: TypeResolver>(PhantomData<R>);
impl<R: TypeResolver> scale_decode::Visitor for DecodedModuleErrorBytesVisitor<R> {
type Error = scale_decode::Error;
type Value<'scale, 'info> = DecodedModuleErrorBytes;
type TypeResolver = R;
fn unchecked_decode_as_type<'scale, 'info>(
self,
input: &mut &'scale [u8],
_type_id: R::TypeId,
_types: &'info R,
) -> DecodeAsTypeResult<Self, Result<Self::Value<'scale, 'info>, Self::Error>> {
DecodeAsTypeResult::Decoded(Ok(DecodedModuleErrorBytes(input.to_vec())))
}
}
impl scale_decode::IntoVisitor for DecodedModuleErrorBytes {
type AnyVisitor<R: TypeResolver> = DecodedModuleErrorBytesVisitor<R>;
fn into_visitor<R: TypeResolver>() -> DecodedModuleErrorBytesVisitor<R> {
DecodedModuleErrorBytesVisitor(PhantomData)
}
}
let decoded_dispatch_err = DecodedDispatchError::decode_as_type(
&mut &*bytes,
dispatch_error_ty_id,
metadata.types(),
)?;
let dispatch_error = match decoded_dispatch_err {
DecodedDispatchError::Other => DispatchError::Other,
DecodedDispatchError::CannotLookup => DispatchError::CannotLookup,
DecodedDispatchError::BadOrigin => DispatchError::BadOrigin,
DecodedDispatchError::ConsumerRemaining => DispatchError::ConsumerRemaining,
DecodedDispatchError::NoProviders => DispatchError::NoProviders,
DecodedDispatchError::TooManyConsumers => DispatchError::TooManyConsumers,
DecodedDispatchError::Token(val) => DispatchError::Token(val),
DecodedDispatchError::Arithmetic(val) => DispatchError::Arithmetic(val),
DecodedDispatchError::Transactional(val) => DispatchError::Transactional(val),
DecodedDispatchError::Exhausted => DispatchError::Exhausted,
DecodedDispatchError::Corruption => DispatchError::Corruption,
DecodedDispatchError::Unavailable => DispatchError::Unavailable,
DecodedDispatchError::RootNotAllowed => DispatchError::RootNotAllowed,
DecodedDispatchError::Module(module_bytes) => {
let module_bytes = module_bytes.0;
let raw = if module_bytes.len() == 2 {
RawModuleError {
pallet_index: module_bytes[0],
error: [module_bytes[1], 0, 0, 0],
}
} else if module_bytes.len() == 5 {
RawModuleError {
pallet_index: module_bytes[0],
error: [module_bytes[1], module_bytes[2], module_bytes[3], module_bytes[4]],
}
} else {
warn!("Can't decode error sp_runtime::DispatchError: bytes do not match known shapes");
return Err(Error::Unknown(bytes.to_vec()))
};
let pallet_metadata = metadata.pallet_by_index_err(raw.pallet_index)?;
let error_details = pallet_metadata
.error_variant_by_index(raw.error[0])
.ok_or(MetadataError::ErrorNotFound(raw.pallet_index, raw.error[0]))?;
DispatchError::Module(ModuleError {
pallet: pallet_metadata.name().to_string(),
error: error_details.name.clone(),
description: error_details.docs.clone(),
raw,
})
},
};
Ok(dispatch_error)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, DecodeAsType)]
pub enum TokenError {
FundsUnavailable,
OnlyProvider,
BelowMinimum,
CannotCreate,
UnknownAsset,
Frozen,
Unsupported,
CannotCreateHold,
NotExpendable,
Blocked,
}
#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, DecodeAsType)]
pub enum ArithmeticError {
Underflow,
Overflow,
DivisionByZero,
}
#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, DecodeAsType)]
pub enum TransactionalError {
LimitReached,
NoLayer,
}
#[derive(Clone, Debug)]
pub struct ModuleError {
pub pallet: String,
pub error: String,
pub description: Vec<String>,
pub raw: RawModuleError,
}
impl PartialEq for ModuleError {
fn eq(&self, other: &Self) -> bool {
self.raw == other.raw
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RawModuleError {
pub pallet_index: u8,
pub error: [u8; 4],
}
impl RawModuleError {
pub fn error_index(&self) -> u8 {
self.error[0]
}
}