Skip to main content

RevertDecoder

Struct RevertDecoder 

Source
pub struct RevertDecoder { /* private fields */ }
Expand description

Decodes raw EVM revert data into a RevertReason.

The two standard Solidity built-ins — Error(string) and Panic(uint256) — are always recognized. Register additional contract-defined custom errors with with_error, register, or register_raw. Duplicate custom-error selectors keep the first registration; use try_register or try_register_raw when collisions should be handled as errors instead of warnings.

The decoder is cheap to Clone and is Send + Sync, so a configured decoder can be shared across parallel simulations.

Implementations§

Source§

impl RevertDecoder

Source

pub fn new() -> Self

Create a decoder that recognizes only the standard Solidity built-ins (Error(string) and Panic(uint256)) and no custom errors.

use evm_fork_cache::errors::RevertDecoder;

let decoder = RevertDecoder::new();
assert!(decoder.is_empty());
Source

pub fn with_error<E>(self) -> Self
where E: SolError + Debug + 'static,

Register a sol!-generated custom error type for decoding, consuming and returning self for builder-style chaining.

If the selector is already registered, the first registration is kept and a warning is emitted. Use try_register when duplicate selectors should fail configuration.

use alloy_sol_types::sol;
use evm_fork_cache::errors::RevertDecoder;

sol! {
    #[derive(Debug)]
    error SlippageExceeded(uint256 wanted, uint256 got);
    #[derive(Debug)]
    error Paused();
}

let decoder = RevertDecoder::new()
    .with_error::<SlippageExceeded>()
    .with_error::<Paused>();
assert_eq!(decoder.len(), 2);
Source

pub fn register<E>(&mut self) -> &mut Self
where E: SolError + Debug + 'static,

Register a sol!-generated custom error type for decoding.

If an error with the same selector is already registered, the first registration is kept and a warning is emitted. Use try_register to surface duplicates as errors.

Source

pub fn try_register<E>(&mut self) -> Result<&mut Self, DuplicateSelectorError>
where E: SolError + Debug + 'static,

Register a sol!-generated custom error type for decoding, returning an error when another custom error already owns the same selector.

Source

pub fn register_raw( &mut self, selector: [u8; 4], name: impl Into<Cow<'static, str>>, decode: impl Fn(&Bytes) -> Option<String> + Send + Sync + 'static, ) -> &mut Self

Register a custom error by raw selector, name, and parameter decoder.

Use this when there is no sol!-generated type to hand — for example when the selector and signature come from an ABI loaded at runtime. The decode closure receives the full revert bytes (selector included) and returns the formatted parameters, or None if it cannot decode them.

If the closure returns None, the selector still matches: the decode yields a RevertReason::Custom whose params is None. If an error with the same selector is already registered, the first registration is kept and a warning is emitted. Use try_register_raw to surface duplicates as errors.

use alloy_primitives::Bytes;
use evm_fork_cache::errors::{RevertDecoder, RevertReason};

let mut decoder = RevertDecoder::new();
// A closure that decodes the parameters when there is a payload byte,
// and otherwise reports a decode failure by returning `None`.
decoder.register_raw([0xde, 0xad, 0xbe, 0xef], "MyError(uint256)", |data| {
    (data.len() > 4).then(|| format!("payload {} bytes", data.len() - 4))
});

// Selector plus a payload byte: the closure decodes the parameters.
let with_params = Bytes::from(vec![0xde, 0xad, 0xbe, 0xef, 0x00]);
match decoder.decode(&with_params) {
    RevertReason::Custom(custom) => {
        assert_eq!(custom.name, "MyError(uint256)");
        assert_eq!(custom.params.as_deref(), Some("payload 1 bytes"));
    }
    other => panic!("expected Custom, got {other}"),
}

// Bare selector: the closure returns `None`, but the selector still
// matches, so the result is a `Custom` with `params == None`.
let bare = Bytes::from(vec![0xde, 0xad, 0xbe, 0xef]);
match decoder.decode(&bare) {
    RevertReason::Custom(custom) => assert!(custom.params.is_none()),
    other => panic!("expected Custom, got {other}"),
}
Source

pub fn try_register_raw( &mut self, selector: [u8; 4], name: impl Into<Cow<'static, str>>, decode: impl Fn(&Bytes) -> Option<String> + Send + Sync + 'static, ) -> Result<&mut Self, DuplicateSelectorError>

Register a custom error by raw selector, name, and parameter decoder, returning an error when another custom error already owns the selector.

The decode closure receives the full revert bytes (selector included) and returns formatted parameters, or None if the selector matched but the parameter payload could not be decoded.

Source

pub fn len(&self) -> usize

Number of registered custom errors. The two Solidity built-ins are always recognized and are not counted, so a freshly new decoder reports 0.

Source

pub fn is_empty(&self) -> bool

Returns true if no custom errors are registered. The built-ins are always recognized regardless, so this is true for a freshly new decoder.

Source

pub fn decode(&self, data: &Bytes) -> RevertReason

Decode raw EVM revert data into a RevertReason.

Resolution order: the two Solidity built-ins (Error(string) and Panic(uint256)), then registered custom errors by selector, then RevertReason::Unknown for anything else. Empty input decodes to RevertReason::Empty, and data shorter than 4 bytes decodes to RevertReason::Unknown with the selector right-padded with zeros.

use alloy_primitives::{Bytes, U256};
use alloy_sol_types::{Panic, SolError, sol};
use evm_fork_cache::errors::{RevertDecoder, RevertReason, ERROR_SELECTOR};

sol! {
    #[derive(Debug)]
    error Custom();
}

let decoder = RevertDecoder::new().with_error::<Custom>();

// Built-in `Error(string)` decodes natively, without registration.
// Layout: selector | offset(0x20) | length | utf8 bytes (padded).
let mut bytes = ERROR_SELECTOR.to_vec();
bytes.extend_from_slice(&{ let mut o = [0u8; 32]; o[31] = 0x20; o }); // offset
bytes.extend_from_slice(&{ let mut l = [0u8; 32]; l[31] = 2; l });    // length 2
bytes.extend_from_slice(b"hi");
bytes.extend_from_slice(&[0u8; 30]);                                  // pad to 32
assert_eq!(decoder.decode(&Bytes::from(bytes)), RevertReason::Error("hi".into()));

// Built-in `Panic(uint256)` decodes natively too.
let panic = Bytes::from(Panic { code: U256::from(0x11) }.abi_encode());
assert_eq!(decoder.decode(&panic), RevertReason::Panic(0x11));

// A registered selector resolves to `Custom`.
let raw = Bytes::from(Custom::SELECTOR.to_vec());
match decoder.decode(&raw) {
    RevertReason::Custom(err) => assert_eq!(err.name, "Custom()"),
    other => panic!("expected Custom, got {other}"),
}

// An unregistered selector falls through to `Unknown`.
let unknown = Bytes::from(vec![0xde, 0xad, 0xbe, 0xef]);
assert!(matches!(decoder.decode(&unknown), RevertReason::Unknown { .. }));

Trait Implementations§

Source§

impl Clone for RevertDecoder

Source§

fn clone(&self) -> RevertDecoder

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RevertDecoder

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for RevertDecoder

Source§

fn default() -> RevertDecoder

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> FmtForward for T

Source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
Source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
Source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
Source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
Source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
Source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
Source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
Source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
Source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
Source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
Source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more