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
impl RevertDecoder
Sourcepub fn new() -> Self
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());Sourcepub fn with_error<E>(self) -> Self
pub fn with_error<E>(self) -> Self
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);Sourcepub fn register<E>(&mut self) -> &mut Self
pub fn register<E>(&mut self) -> &mut Self
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.
Sourcepub fn try_register<E>(&mut self) -> Result<&mut Self, DuplicateSelectorError>
pub fn try_register<E>(&mut self) -> Result<&mut Self, DuplicateSelectorError>
Register a sol!-generated custom error type for decoding, returning an
error when another custom error already owns the same selector.
Sourcepub 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
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}"),
}Sourcepub 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>
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.
Sourcepub fn len(&self) -> usize
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.
Sourcepub fn is_empty(&self) -> bool
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.
Sourcepub fn decode(&self, data: &Bytes) -> RevertReason
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
impl Clone for RevertDecoder
Source§fn clone(&self) -> RevertDecoder
fn clone(&self) -> RevertDecoder
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for RevertDecoder
impl Debug for RevertDecoder
Source§impl Default for RevertDecoder
impl Default for RevertDecoder
Source§fn default() -> RevertDecoder
fn default() -> RevertDecoder
Auto Trait Implementations§
impl !RefUnwindSafe for RevertDecoder
impl !UnwindSafe for RevertDecoder
impl Freeze for RevertDecoder
impl Send for RevertDecoder
impl Sync for RevertDecoder
impl Unpin for RevertDecoder
impl UnsafeUnpin for RevertDecoder
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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 moreSource§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
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
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
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
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.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
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.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
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.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
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.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
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.