Skip to main content

revm_context_interface/
result.rs

1//! Result of the EVM execution. Containing both execution result, state and errors.
2//!
3//! [`ExecutionResult`] is the result of the EVM execution.
4//!
5//! [`InvalidTransaction`] is the error that is returned when the transaction is invalid.
6//!
7//! [`InvalidHeader`] is the error that is returned when the header is invalid.
8//!
9//! [`SuccessReason`] is the reason that the transaction successfully completed.
10use crate::{context::ContextError, transaction::TransactionError};
11use core::fmt::{self, Debug};
12use database_interface::DBErrorMarker;
13use primitives::{Address, Bytes, Log, U256};
14use state::EvmState;
15use std::{borrow::Cow, boxed::Box, string::String, sync::Arc, vec::Vec};
16
17/// Trait for the halt reason.
18pub trait HaltReasonTr: Clone + Debug + PartialEq + Eq + From<HaltReason> {}
19
20impl<T> HaltReasonTr for T where T: Clone + Debug + PartialEq + Eq + From<HaltReason> {}
21
22/// Tuple containing evm execution result and state.s
23#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct ExecResultAndState<R, S = EvmState> {
26    /// Execution result
27    pub result: R,
28    /// Output State.
29    pub state: S,
30}
31
32/// Type alias for backwards compatibility.
33pub type ResultAndState<H = HaltReason, S = EvmState> = ExecResultAndState<ExecutionResult<H>, S>;
34
35/// Tuple containing multiple execution results and state.
36pub type ResultVecAndState<R, S> = ExecResultAndState<Vec<R>, S>;
37
38impl<R, S> ExecResultAndState<R, S> {
39    /// Creates new ResultAndState.
40    pub fn new(result: R, state: S) -> Self {
41        Self { result, state }
42    }
43}
44
45/// Gas accounting result from transaction execution.
46///
47/// Self-contained gas snapshot with all values needed for downstream consumers.
48///
49/// ## Stored values
50///
51/// | Getter            | Source                             | Description                               |
52/// |-------------------|------------------------------------|-------------------------------------------|
53/// | [`limit()`]       | `Gas::limit()`                     | Transaction gas limit                     |
54/// | [`spent()`]       | `Gas::spent()` = limit − remaining | Total gas consumed before refund          |
55/// | [`inner_refunded()`] | `Gas::refunded()` as u64        | Gas refunded (capped per EIP-3529)        |
56/// | [`floor_gas()`]   | `InitialAndFloorGas::floor_gas`    | EIP-7623 floor gas (0 if not applicable)  |
57/// | [`intrinsic_gas()`] | `InitialAndFloorGas::initial_gas`| Initial tx overhead gas (0 for system calls) |
58///
59/// [`limit()`]: ResultGas::limit
60/// [`spent()`]: ResultGas::spent
61/// [`inner_refunded()`]: ResultGas::inner_refunded
62/// [`floor_gas()`]: ResultGas::floor_gas
63/// [`intrinsic_gas()`]: ResultGas::intrinsic_gas
64///
65/// ## Derived values
66///
67/// - [`used()`](ResultGas::used) = `max(spent − refunded, floor_gas)` (the value that goes into receipts)
68/// - [`remaining()`](ResultGas::remaining) = `limit − spent`
69/// - [`spent_sub_refunded()`](ResultGas::spent_sub_refunded) = `spent − refunded` (before floor gas check)
70/// - [`final_refunded()`](ResultGas::final_refunded) = `refunded` when floor gas is inactive, `0` when floor gas kicks in
71#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73pub struct ResultGas {
74    /// Transaction gas limit.
75    limit: u64,
76    /// Gas consumed before final refund (limit − remaining).
77    /// For actual gas used, use [`used()`](ResultGas::used).
78    #[cfg_attr(feature = "serde", serde(rename = "gas_spent"))]
79    spent: u64,
80    /// Gas refund amount (capped per EIP-3529).
81    ///
82    /// Note: This is the raw refund before EIP-7623 floor gas adjustment.
83    /// Use [`final_refunded()`](ResultGas::final_refunded) for the effective refund.
84    #[cfg_attr(feature = "serde", serde(rename = "gas_refunded"))]
85    refunded: u64,
86    /// EIP-7623 floor gas. Zero when not applicable.
87    floor_gas: u64,
88    /// Intrinsic gas: the initial transaction overhead (calldata, access list, etc.).
89    /// Zero for system calls.
90    intrinsic_gas: u64,
91}
92
93impl ResultGas {
94    /// Creates a new `ResultGas`.
95    #[inline]
96    pub const fn new(
97        limit: u64,
98        spent: u64,
99        refunded: u64,
100        floor_gas: u64,
101        intrinsic_gas: u64,
102    ) -> Self {
103        Self {
104            limit,
105            spent,
106            refunded,
107            floor_gas,
108            intrinsic_gas,
109        }
110    }
111
112    /// Returns the transaction gas limit.
113    #[inline]
114    pub const fn limit(&self) -> u64 {
115        self.limit
116    }
117
118    /// Returns the gas spent inside execution before any refund.
119    ///
120    /// If you want final gas used, use [`used()`](ResultGas::used).
121    #[inline]
122    pub const fn spent(&self) -> u64 {
123        self.spent
124    }
125
126    /// Returns the EIP-7623 floor gas.
127    #[inline]
128    pub const fn floor_gas(&self) -> u64 {
129        self.floor_gas
130    }
131
132    /// Returns the intrinsic gas.
133    #[inline]
134    pub const fn intrinsic_gas(&self) -> u64 {
135        self.intrinsic_gas
136    }
137
138    /// Sets the `limit` field.
139    #[inline]
140    pub const fn with_limit(mut self, limit: u64) -> Self {
141        self.limit = limit;
142        self
143    }
144
145    /// Sets the `spent` field.
146    #[inline]
147    pub const fn with_spent(mut self, spent: u64) -> Self {
148        self.spent = spent;
149        self
150    }
151
152    /// Sets the `refunded` field.
153    #[inline]
154    pub const fn with_refunded(mut self, refunded: u64) -> Self {
155        self.refunded = refunded;
156        self
157    }
158
159    /// Sets the `floor_gas` field.
160    #[inline]
161    pub const fn with_floor_gas(mut self, floor_gas: u64) -> Self {
162        self.floor_gas = floor_gas;
163        self
164    }
165
166    /// Sets the `intrinsic_gas` field.
167    #[inline]
168    pub const fn with_intrinsic_gas(mut self, intrinsic_gas: u64) -> Self {
169        self.intrinsic_gas = intrinsic_gas;
170        self
171    }
172
173    /// Sets the `limit` field by mutable reference.
174    #[inline]
175    pub fn set_limit(&mut self, limit: u64) {
176        self.limit = limit;
177    }
178
179    /// Sets the `spent` field by mutable reference.
180    #[inline]
181    pub fn set_spent(&mut self, spent: u64) {
182        self.spent = spent;
183    }
184
185    /// Sets the `refunded` field by mutable reference.
186    #[inline]
187    pub fn set_refunded(&mut self, refunded: u64) {
188        self.refunded = refunded;
189    }
190
191    /// Sets the `floor_gas` field by mutable reference.
192    #[inline]
193    pub fn set_floor_gas(&mut self, floor_gas: u64) {
194        self.floor_gas = floor_gas;
195    }
196
197    /// Sets the `intrinsic_gas` field by mutable reference.
198    #[inline]
199    pub fn set_intrinsic_gas(&mut self, intrinsic_gas: u64) {
200        self.intrinsic_gas = intrinsic_gas;
201    }
202
203    /// Returns the final gas used: `max(spent - refunded, floor_gas)`.
204    ///
205    /// This is the value used for receipt `cumulative_gas_used` accumulation
206    /// and the per-transaction gas charge.
207    #[inline]
208    pub const fn used(&self) -> u64 {
209        // EIP-7623: Increase calldata cost
210        // spend at least a gas_floor amount of gas.
211        let spent_sub_refunded = self.spent_sub_refunded();
212        if spent_sub_refunded < self.floor_gas {
213            return self.floor_gas;
214        }
215        spent_sub_refunded
216    }
217
218    /// Returns the gas spent minus the refunded gas.
219    ///
220    /// This does not take into account EIP-7623 floor gas. If you want to get the gas used in
221    /// receipt, use [`used()`](ResultGas::used) instead.
222    #[inline]
223    pub const fn spent_sub_refunded(&self) -> u64 {
224        self.spent.saturating_sub(self.refunded)
225    }
226
227    /// Returns the remaining gas: `limit - spent`.
228    #[inline]
229    pub const fn remaining(&self) -> u64 {
230        self.limit.saturating_sub(self.spent)
231    }
232
233    /// Returns the raw refund from EVM execution, before EIP-7623 floor gas adjustment.
234    ///
235    /// This is the `refunded` field value (capped per EIP-3529 but not adjusted for floor gas).
236    /// See [`final_refunded()`](ResultGas::final_refunded) for the effective refund.
237    #[inline]
238    pub const fn inner_refunded(&self) -> u64 {
239        self.refunded
240    }
241
242    /// Returns the effective refund after EIP-7623 floor gas adjustment.
243    ///
244    /// When floor gas kicks in (`spent - refunded < floor_gas`), the refund is zero
245    /// because the floor gas charge absorbs it entirely. Otherwise returns the raw refund.
246    #[inline]
247    pub const fn final_refunded(&self) -> u64 {
248        if self.spent_sub_refunded() < self.floor_gas {
249            0
250        } else {
251            self.refunded
252        }
253    }
254}
255
256impl fmt::Display for ResultGas {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        write!(
259            f,
260            "gas used: {}, limit: {}, spent: {}",
261            self.used(),
262            self.limit,
263            self.spent
264        )?;
265        if self.refunded > 0 {
266            write!(f, ", refunded: {}", self.refunded)?;
267        }
268        if self.floor_gas > 0 {
269            write!(f, ", floor: {}", self.floor_gas)?;
270        }
271        if self.intrinsic_gas > 0 {
272            write!(f, ", intrinsic: {}", self.intrinsic_gas)?;
273        }
274        Ok(())
275    }
276}
277
278/// Result of a transaction execution
279#[derive(Clone, Debug, PartialEq, Eq, Hash)]
280#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
281pub enum ExecutionResult<HaltReasonTy = HaltReason> {
282    /// Returned successfully
283    Success {
284        /// Reason for the success.
285        reason: SuccessReason,
286        /// Gas accounting for the transaction.
287        gas: ResultGas,
288        /// Logs emitted by the transaction.
289        logs: Vec<Log>,
290        /// Output of the transaction.
291        output: Output,
292    },
293    /// Reverted by `REVERT` opcode that doesn't spend all gas
294    Revert {
295        /// Gas accounting for the transaction.
296        gas: ResultGas,
297        /// Logs emitted before the revert.
298        logs: Vec<Log>,
299        /// Output of the transaction.
300        output: Bytes,
301    },
302    /// Reverted for various reasons and spend all gas
303    Halt {
304        /// Reason for the halt.
305        reason: HaltReasonTy,
306        /// Gas accounting for the transaction.
307        ///
308        /// For standard EVM halts, gas used typically equals the gas limit.
309        /// Some system- or L2-specific halts may intentionally report less gas used.
310        gas: ResultGas,
311        /// Logs emitted before the halt.
312        logs: Vec<Log>,
313    },
314}
315
316impl<HaltReasonTy> ExecutionResult<HaltReasonTy> {
317    /// Returns if transaction execution is successful.
318    ///
319    /// 1 indicates success, 0 indicates revert.
320    ///
321    /// <https://eips.ethereum.org/EIPS/eip-658>
322    pub fn is_success(&self) -> bool {
323        matches!(self, Self::Success { .. })
324    }
325
326    /// Maps a `DBError` to a new error type using the provided closure, leaving other variants unchanged.
327    pub fn map_haltreason<F, OHR>(self, op: F) -> ExecutionResult<OHR>
328    where
329        F: FnOnce(HaltReasonTy) -> OHR,
330    {
331        match self {
332            Self::Success {
333                reason,
334                gas,
335                logs,
336                output,
337            } => ExecutionResult::Success {
338                reason,
339                gas,
340                logs,
341                output,
342            },
343            Self::Revert { gas, logs, output } => ExecutionResult::Revert { gas, logs, output },
344            Self::Halt { reason, gas, logs } => ExecutionResult::Halt {
345                reason: op(reason),
346                gas,
347                logs,
348            },
349        }
350    }
351
352    /// Returns created address if execution is Create transaction
353    /// and Contract was created.
354    pub fn created_address(&self) -> Option<Address> {
355        match self {
356            Self::Success { output, .. } => output.address().cloned(),
357            _ => None,
358        }
359    }
360
361    /// Returns true if execution result is a Halt.
362    pub fn is_halt(&self) -> bool {
363        matches!(self, Self::Halt { .. })
364    }
365
366    /// Returns the output data of the execution.
367    ///
368    /// Returns [`None`] if the execution was halted.
369    pub fn output(&self) -> Option<&Bytes> {
370        match self {
371            Self::Success { output, .. } => Some(output.data()),
372            Self::Revert { output, .. } => Some(output),
373            _ => None,
374        }
375    }
376
377    /// Consumes the type and returns the output data of the execution.
378    ///
379    /// Returns [`None`] if the execution was halted.
380    pub fn into_output(self) -> Option<Bytes> {
381        match self {
382            Self::Success { output, .. } => Some(output.into_data()),
383            Self::Revert { output, .. } => Some(output),
384            _ => None,
385        }
386    }
387
388    /// Returns the logs emitted during execution.
389    pub fn logs(&self) -> &[Log] {
390        match self {
391            Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
392                logs.as_slice()
393            }
394        }
395    }
396
397    /// Consumes [`self`] and returns the logs emitted during execution.
398    pub fn into_logs(self) -> Vec<Log> {
399        match self {
400            Self::Success { logs, .. } | Self::Revert { logs, .. } | Self::Halt { logs, .. } => {
401                logs
402            }
403        }
404    }
405
406    /// Returns the gas accounting information.
407    pub fn gas(&self) -> &ResultGas {
408        match self {
409            Self::Success { gas, .. } | Self::Revert { gas, .. } | Self::Halt { gas, .. } => gas,
410        }
411    }
412
413    /// Returns the gas used.
414    pub fn gas_used(&self) -> u64 {
415        self.gas().used()
416    }
417}
418
419impl<HaltReasonTy: fmt::Display> fmt::Display for ExecutionResult<HaltReasonTy> {
420    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421        match self {
422            Self::Success {
423                reason,
424                gas,
425                logs,
426                output,
427            } => {
428                write!(f, "Success ({reason}): {gas}")?;
429                if !logs.is_empty() {
430                    write!(
431                        f,
432                        ", {} log{}",
433                        logs.len(),
434                        if logs.len() == 1 { "" } else { "s" }
435                    )?;
436                }
437                write!(f, ", {output}")
438            }
439            Self::Revert { gas, logs, output } => {
440                write!(f, "Revert: {gas}")?;
441                if !logs.is_empty() {
442                    write!(
443                        f,
444                        ", {} log{}",
445                        logs.len(),
446                        if logs.len() == 1 { "" } else { "s" }
447                    )?;
448                }
449                if !output.is_empty() {
450                    write!(f, ", {} bytes output", output.len())?;
451                }
452                Ok(())
453            }
454            Self::Halt { reason, gas, logs } => {
455                write!(f, "Halted: {reason} ({gas})")?;
456                if !logs.is_empty() {
457                    write!(
458                        f,
459                        ", {} log{}",
460                        logs.len(),
461                        if logs.len() == 1 { "" } else { "s" }
462                    )?;
463                }
464                Ok(())
465            }
466        }
467    }
468}
469
470/// Output of a transaction execution
471#[derive(Debug, Clone, PartialEq, Eq, Hash)]
472#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
473pub enum Output {
474    /// Output of a call.
475    Call(Bytes),
476    /// Output of a create.
477    Create(Bytes, Option<Address>),
478}
479
480impl Output {
481    /// Returns the output data of the execution output.
482    pub fn into_data(self) -> Bytes {
483        match self {
484            Output::Call(data) => data,
485            Output::Create(data, _) => data,
486        }
487    }
488
489    /// Returns the output data of the execution output.
490    pub fn data(&self) -> &Bytes {
491        match self {
492            Output::Call(data) => data,
493            Output::Create(data, _) => data,
494        }
495    }
496
497    /// Returns the created address, if any.
498    pub fn address(&self) -> Option<&Address> {
499        match self {
500            Output::Call(_) => None,
501            Output::Create(_, address) => address.as_ref(),
502        }
503    }
504}
505
506impl fmt::Display for Output {
507    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508        match self {
509            Output::Call(data) => {
510                if data.is_empty() {
511                    write!(f, "no output")
512                } else {
513                    write!(f, "{} bytes output", data.len())
514                }
515            }
516            Output::Create(data, Some(addr)) => {
517                if data.is_empty() {
518                    write!(f, "contract created at {}", addr)
519                } else {
520                    write!(f, "contract created at {} ({} bytes)", addr, data.len())
521                }
522            }
523            Output::Create(data, None) => {
524                if data.is_empty() {
525                    write!(f, "contract creation (no address)")
526                } else {
527                    write!(f, "contract creation (no address, {} bytes)", data.len())
528                }
529            }
530        }
531    }
532}
533
534/// Type-erased error type.
535#[derive(Debug, Clone)]
536pub struct AnyError(Arc<dyn core::error::Error + Send + Sync>);
537impl AnyError {
538    /// Creates a new [`AnyError`] from any error type.
539    pub fn new(err: impl core::error::Error + Send + Sync + 'static) -> Self {
540        Self(Arc::new(err))
541    }
542}
543
544impl PartialEq for AnyError {
545    fn eq(&self, other: &Self) -> bool {
546        Arc::ptr_eq(&self.0, &other.0)
547    }
548}
549impl Eq for AnyError {}
550impl core::hash::Hash for AnyError {
551    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
552        (Arc::as_ptr(&self.0) as *const ()).hash(state);
553    }
554}
555impl fmt::Display for AnyError {
556    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557        fmt::Display::fmt(&self.0, f)
558    }
559}
560impl core::error::Error for AnyError {
561    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
562        self.0.source()
563    }
564}
565
566#[cfg(feature = "serde")]
567impl serde::Serialize for AnyError {
568    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
569        serializer.collect_str(self)
570    }
571}
572
573#[derive(Debug)]
574struct StringError(String);
575impl fmt::Display for StringError {
576    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577        f.write_str(&self.0)
578    }
579}
580impl core::error::Error for StringError {}
581
582impl From<String> for AnyError {
583    fn from(value: String) -> Self {
584        Self::new(StringError(value))
585    }
586}
587impl From<&'static str> for AnyError {
588    fn from(s: &'static str) -> Self {
589        Self::new(StringError(s.into()))
590    }
591}
592
593#[cfg(feature = "serde")]
594impl<'de> serde::Deserialize<'de> for AnyError {
595    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
596        let s = String::deserialize(deserializer)?;
597        Ok(s.into())
598    }
599}
600
601/// Main EVM error
602#[derive(Debug, Clone, PartialEq, Eq)]
603#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
604pub enum EVMError<DBError, TransactionError = InvalidTransaction> {
605    /// Transaction validation error
606    Transaction(TransactionError),
607    /// Header validation error
608    Header(InvalidHeader),
609    /// Database error
610    Database(DBError),
611    /// Custom error for non-standard EVM failures.
612    ///
613    /// This includes `PrecompileError::Fatal` and `PrecompileError::FatalAny`
614    /// errors as well as any custom errors returned by handler registers.
615    Custom(String),
616    /// Custom error for non-standard EVM failures.
617    ///
618    /// This includes `PrecompileError::Fatal` and `PrecompileError::FatalAny`
619    /// errors as well as any custom errors returned by handler registers.
620    CustomAny(AnyError),
621}
622
623impl<DBError, TransactionValidationErrorT> From<ContextError<DBError>>
624    for EVMError<DBError, TransactionValidationErrorT>
625{
626    fn from(value: ContextError<DBError>) -> Self {
627        match value {
628            ContextError::Db(e) => Self::Database(e),
629            ContextError::Custom(e) => Self::Custom(e),
630        }
631    }
632}
633
634impl<DBError: DBErrorMarker, TX> From<DBError> for EVMError<DBError, TX> {
635    fn from(value: DBError) -> Self {
636        Self::Database(value)
637    }
638}
639
640/// Trait for converting a string to an [`EVMError::Custom`] error.
641pub trait FromStringError {
642    /// Converts a string to an [`EVMError::Custom`] error.
643    fn from_string(value: String) -> Self;
644}
645
646impl<DB, TX> FromStringError for EVMError<DB, TX> {
647    fn from_string(value: String) -> Self {
648        Self::Custom(value)
649    }
650}
651
652impl<DB, TXE: From<InvalidTransaction>> From<InvalidTransaction> for EVMError<DB, TXE> {
653    fn from(value: InvalidTransaction) -> Self {
654        Self::Transaction(TXE::from(value))
655    }
656}
657
658impl<DBError, TransactionValidationErrorT> EVMError<DBError, TransactionValidationErrorT> {
659    /// Maps a `DBError` to a new error type using the provided closure, leaving other variants unchanged.
660    pub fn map_db_err<F, E>(self, op: F) -> EVMError<E, TransactionValidationErrorT>
661    where
662        F: FnOnce(DBError) -> E,
663    {
664        match self {
665            Self::Transaction(e) => EVMError::Transaction(e),
666            Self::Header(e) => EVMError::Header(e),
667            Self::Database(e) => EVMError::Database(op(e)),
668            Self::Custom(e) => EVMError::Custom(e),
669            Self::CustomAny(e) => EVMError::CustomAny(e),
670        }
671    }
672}
673
674impl<DBError, TransactionValidationErrorT> core::error::Error
675    for EVMError<DBError, TransactionValidationErrorT>
676where
677    DBError: core::error::Error + 'static,
678    TransactionValidationErrorT: core::error::Error + 'static,
679{
680    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
681        match self {
682            Self::Transaction(e) => Some(e),
683            Self::Header(e) => Some(e),
684            Self::Database(e) => Some(e),
685            Self::Custom(_) => None,
686            Self::CustomAny(e) => Some(e.0.as_ref()),
687        }
688    }
689}
690
691impl<DBError, TransactionValidationErrorT> fmt::Display
692    for EVMError<DBError, TransactionValidationErrorT>
693where
694    DBError: fmt::Display,
695    TransactionValidationErrorT: fmt::Display,
696{
697    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
698        match self {
699            Self::Transaction(e) => write!(f, "transaction validation error: {e}"),
700            Self::Header(e) => write!(f, "header validation error: {e}"),
701            Self::Database(e) => write!(f, "database error: {e}"),
702            Self::Custom(e) => f.write_str(e),
703            Self::CustomAny(e) => write!(f, "{e}"),
704        }
705    }
706}
707
708impl<DBError, TransactionValidationErrorT> From<InvalidHeader>
709    for EVMError<DBError, TransactionValidationErrorT>
710{
711    fn from(value: InvalidHeader) -> Self {
712        Self::Header(value)
713    }
714}
715
716/// Transaction validation error.
717#[derive(Debug, Clone, PartialEq, Eq, Hash)]
718#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
719pub enum InvalidTransaction {
720    /// When using the EIP-1559 fee model introduced in the London upgrade, transactions specify two primary fee fields:
721    /// - `gas_max_fee`: The maximum total fee a user is willing to pay, inclusive of both base fee and priority fee.
722    /// - `gas_priority_fee`: The extra amount a user is willing to give directly to the miner, often referred to as the "tip".
723    ///
724    /// Provided `gas_priority_fee` exceeds the total `gas_max_fee`.
725    PriorityFeeGreaterThanMaxFee,
726    /// EIP-1559: `gas_price` is less than `basefee`.
727    GasPriceLessThanBasefee,
728    /// `gas_limit` in the tx is bigger than `block_gas_limit`.
729    CallerGasLimitMoreThanBlock,
730    /// Initial gas for a Call is bigger than `gas_limit`.
731    ///
732    /// Initial gas for a Call contains:
733    /// - initial stipend gas
734    /// - gas for access list and input data
735    CallGasCostMoreThanGasLimit {
736        /// Initial gas for a Call.
737        initial_gas: u64,
738        /// Gas limit for the transaction.
739        gas_limit: u64,
740    },
741    /// Gas floor calculated from EIP-7623 Increase calldata cost
742    /// is more than the gas limit.
743    ///
744    /// Tx data is too large to be executed.
745    GasFloorMoreThanGasLimit {
746        /// Gas floor calculated from EIP-7623 Increase calldata cost.
747        gas_floor: u64,
748        /// Gas limit for the transaction.
749        gas_limit: u64,
750    },
751    /// EIP-3607 Reject transactions from senders with deployed code
752    RejectCallerWithCode,
753    /// Transaction account does not have enough amount of ether to cover transferred value and gas_limit*gas_price.
754    LackOfFundForMaxFee {
755        /// Fee for the transaction.
756        fee: Box<U256>,
757        /// Balance of the sender.
758        balance: Box<U256>,
759    },
760    /// Overflow payment in transaction.
761    OverflowPaymentInTransaction,
762    /// Nonce overflows in transaction.
763    NonceOverflowInTransaction,
764    /// Nonce is too high.
765    NonceTooHigh {
766        /// Nonce of the transaction.
767        tx: u64,
768        /// Nonce of the state.
769        state: u64,
770    },
771    /// Nonce is too low.
772    NonceTooLow {
773        /// Nonce of the transaction.
774        tx: u64,
775        /// Nonce of the state.
776        state: u64,
777    },
778    /// EIP-3860: Limit and meter initcode
779    CreateInitCodeSizeLimit,
780    /// Transaction chain id does not match the config chain id.
781    InvalidChainId,
782    /// Missing chain id.
783    MissingChainId,
784    /// Transaction gas limit is greater than the cap.
785    TxGasLimitGreaterThanCap {
786        /// Transaction gas limit.
787        gas_limit: u64,
788        /// Gas limit cap.
789        cap: u64,
790    },
791    /// Access list is not supported for blocks before the Berlin hardfork.
792    AccessListNotSupported,
793    /// `max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.
794    MaxFeePerBlobGasNotSupported,
795    /// `blob_hashes`/`blob_versioned_hashes` is not supported for blocks before the Cancun hardfork.
796    BlobVersionedHashesNotSupported,
797    /// Block `blob_gas_price` is greater than tx-specified `max_fee_per_blob_gas` after Cancun.
798    BlobGasPriceGreaterThanMax {
799        /// Block `blob_gas_price`.
800        block_blob_gas_price: u128,
801        /// Tx-specified `max_fee_per_blob_gas`.
802        tx_max_fee_per_blob_gas: u128,
803    },
804    /// There should be at least one blob in Blob transaction.
805    EmptyBlobs,
806    /// Blob transaction can't be a create transaction.
807    ///
808    /// `to` must be present
809    BlobCreateTransaction,
810    /// Transaction has more then `max` blobs
811    TooManyBlobs {
812        /// Maximum number of blobs allowed.
813        max: usize,
814        /// Number of blobs in the transaction.
815        have: usize,
816    },
817    /// Blob transaction contains a versioned hash with an incorrect version
818    BlobVersionNotSupported,
819    /// EIP-7702 is not enabled.
820    AuthorizationListNotSupported,
821    /// EIP-7702 transaction has invalid fields set.
822    AuthorizationListInvalidFields,
823    /// Empty Authorization List is not allowed.
824    EmptyAuthorizationList,
825    /// EIP-2930 is not supported.
826    Eip2930NotSupported,
827    /// EIP-1559 is not supported.
828    Eip1559NotSupported,
829    /// EIP-4844 is not supported.
830    Eip4844NotSupported,
831    /// EIP-7702 is not supported.
832    Eip7702NotSupported,
833    /// EIP-7873 is not supported.
834    Eip7873NotSupported,
835    /// EIP-7873 initcode transaction should have `to` address.
836    Eip7873MissingTarget,
837    /// Custom string error for flexible error handling.
838    Str(Cow<'static, str>),
839}
840
841impl TransactionError for InvalidTransaction {}
842
843impl core::error::Error for InvalidTransaction {}
844
845impl fmt::Display for InvalidTransaction {
846    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847        match self {
848            Self::PriorityFeeGreaterThanMaxFee => {
849                write!(f, "priority fee is greater than max fee")
850            }
851            Self::GasPriceLessThanBasefee => {
852                write!(f, "gas price is less than basefee")
853            }
854            Self::CallerGasLimitMoreThanBlock => {
855                write!(f, "caller gas limit exceeds the block gas limit")
856            }
857            Self::TxGasLimitGreaterThanCap { gas_limit, cap } => {
858                write!(
859                    f,
860                    "transaction gas limit ({gas_limit}) is greater than the cap ({cap})"
861                )
862            }
863            Self::CallGasCostMoreThanGasLimit {
864                initial_gas,
865                gas_limit,
866            } => {
867                write!(
868                    f,
869                    "call gas cost ({initial_gas}) exceeds the gas limit ({gas_limit})"
870                )
871            }
872            Self::GasFloorMoreThanGasLimit {
873                gas_floor,
874                gas_limit,
875            } => {
876                write!(
877                    f,
878                    "gas floor ({gas_floor}) exceeds the gas limit ({gas_limit})"
879                )
880            }
881            Self::RejectCallerWithCode => {
882                write!(f, "reject transactions from senders with deployed code")
883            }
884            Self::LackOfFundForMaxFee { fee, balance } => {
885                write!(f, "lack of funds ({balance}) for max fee ({fee})")
886            }
887            Self::OverflowPaymentInTransaction => {
888                write!(f, "overflow payment in transaction")
889            }
890            Self::NonceOverflowInTransaction => {
891                write!(f, "nonce overflow in transaction")
892            }
893            Self::NonceTooHigh { tx, state } => {
894                write!(f, "nonce {tx} too high, expected {state}")
895            }
896            Self::NonceTooLow { tx, state } => {
897                write!(f, "nonce {tx} too low, expected {state}")
898            }
899            Self::CreateInitCodeSizeLimit => {
900                write!(f, "create initcode size limit")
901            }
902            Self::InvalidChainId => write!(f, "invalid chain ID"),
903            Self::MissingChainId => write!(f, "missing chain ID"),
904            Self::AccessListNotSupported => write!(f, "access list not supported"),
905            Self::MaxFeePerBlobGasNotSupported => {
906                write!(f, "max fee per blob gas not supported")
907            }
908            Self::BlobVersionedHashesNotSupported => {
909                write!(f, "blob versioned hashes not supported")
910            }
911            Self::BlobGasPriceGreaterThanMax {
912                block_blob_gas_price,
913                tx_max_fee_per_blob_gas,
914            } => {
915                write!(
916                    f,
917                    "blob gas price ({block_blob_gas_price}) is greater than max fee per blob gas ({tx_max_fee_per_blob_gas})"
918                )
919            }
920            Self::EmptyBlobs => write!(f, "empty blobs"),
921            Self::BlobCreateTransaction => write!(f, "blob create transaction"),
922            Self::TooManyBlobs { max, have } => {
923                write!(f, "too many blobs, have {have}, max {max}")
924            }
925            Self::BlobVersionNotSupported => write!(f, "blob version not supported"),
926            Self::AuthorizationListNotSupported => write!(f, "authorization list not supported"),
927            Self::AuthorizationListInvalidFields => {
928                write!(f, "authorization list tx has invalid fields")
929            }
930            Self::EmptyAuthorizationList => write!(f, "empty authorization list"),
931            Self::Eip2930NotSupported => write!(f, "Eip2930 is not supported"),
932            Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"),
933            Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"),
934            Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"),
935            Self::Eip7873NotSupported => write!(f, "Eip7873 is not supported"),
936            Self::Eip7873MissingTarget => {
937                write!(f, "Eip7873 initcode transaction should have `to` address")
938            }
939            Self::Str(msg) => f.write_str(msg),
940        }
941    }
942}
943
944/// Errors related to misconfiguration of a [`crate::Block`].
945#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
946#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
947pub enum InvalidHeader {
948    /// `prevrandao` is not set for Merge and above.
949    PrevrandaoNotSet,
950    /// `excess_blob_gas` is not set for Cancun and above.
951    ExcessBlobGasNotSet,
952}
953
954impl core::error::Error for InvalidHeader {}
955
956impl fmt::Display for InvalidHeader {
957    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
958        match self {
959            Self::PrevrandaoNotSet => write!(f, "`prevrandao` not set"),
960            Self::ExcessBlobGasNotSet => write!(f, "`excess_blob_gas` not set"),
961        }
962    }
963}
964
965/// Reason a transaction successfully completed.
966#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
967#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
968pub enum SuccessReason {
969    /// Stop [`state::bytecode::opcode::STOP`] opcode.
970    Stop,
971    /// Return [`state::bytecode::opcode::RETURN`] opcode.
972    Return,
973    /// Self destruct opcode.
974    SelfDestruct,
975}
976
977impl fmt::Display for SuccessReason {
978    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
979        match self {
980            Self::Stop => write!(f, "Stop"),
981            Self::Return => write!(f, "Return"),
982            Self::SelfDestruct => write!(f, "SelfDestruct"),
983        }
984    }
985}
986
987/// Indicates that the EVM has experienced an exceptional halt.
988///
989/// This causes execution to immediately end with all gas being consumed.
990#[derive(Debug, Clone, PartialEq, Eq, Hash)]
991#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
992pub enum HaltReason {
993    /// Out of gas error.
994    OutOfGas(OutOfGasError),
995    /// Opcode not found error.
996    OpcodeNotFound,
997    /// Invalid FE opcode error.
998    InvalidFEOpcode,
999    /// Invalid jump destination.
1000    InvalidJump,
1001    /// The feature or opcode is not activated in hardfork.
1002    NotActivated,
1003    /// Attempting to pop a value from an empty stack.
1004    StackUnderflow,
1005    /// Attempting to push a value onto a full stack.
1006    StackOverflow,
1007    /// Invalid memory or storage offset for [`state::bytecode::opcode::RETURNDATACOPY`].
1008    OutOfOffset,
1009    /// Address collision during contract creation.
1010    CreateCollision,
1011    /// Precompile error.
1012    PrecompileError,
1013    /// Precompile error with message from context.
1014    PrecompileErrorWithContext(String),
1015    /// Nonce overflow.
1016    NonceOverflow,
1017    /// Create init code size exceeds limit (runtime).
1018    CreateContractSizeLimit,
1019    /// Error on created contract that begins with EF
1020    CreateContractStartingWithEF,
1021    /// EIP-3860: Limit and meter initcode. Initcode size limit exceeded.
1022    CreateInitCodeSizeLimit,
1023
1024    /* Internal Halts that can be only found inside Inspector */
1025    /// Overflow payment. Not possible to happen on mainnet.
1026    OverflowPayment,
1027    /// State change during static call.
1028    StateChangeDuringStaticCall,
1029    /// Call not allowed inside static call.
1030    CallNotAllowedInsideStatic,
1031    /// Out of funds to pay for the call.
1032    OutOfFunds,
1033    /// Call is too deep.
1034    CallTooDeep,
1035}
1036
1037impl core::error::Error for HaltReason {}
1038
1039impl fmt::Display for HaltReason {
1040    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1041        match self {
1042            Self::OutOfGas(err) => write!(f, "{err}"),
1043            Self::OpcodeNotFound => write!(f, "opcode not found"),
1044            Self::InvalidFEOpcode => write!(f, "invalid 0xFE opcode"),
1045            Self::InvalidJump => write!(f, "invalid jump destination"),
1046            Self::NotActivated => write!(f, "feature or opcode not activated"),
1047            Self::StackUnderflow => write!(f, "stack underflow"),
1048            Self::StackOverflow => write!(f, "stack overflow"),
1049            Self::OutOfOffset => write!(f, "out of offset"),
1050            Self::CreateCollision => write!(f, "create collision"),
1051            Self::PrecompileError => write!(f, "precompile error"),
1052            Self::PrecompileErrorWithContext(msg) => write!(f, "precompile error: {msg}"),
1053            Self::NonceOverflow => write!(f, "nonce overflow"),
1054            Self::CreateContractSizeLimit => write!(f, "create contract size limit"),
1055            Self::CreateContractStartingWithEF => {
1056                write!(f, "create contract starting with 0xEF")
1057            }
1058            Self::CreateInitCodeSizeLimit => write!(f, "create initcode size limit"),
1059            Self::OverflowPayment => write!(f, "overflow payment"),
1060            Self::StateChangeDuringStaticCall => write!(f, "state change during static call"),
1061            Self::CallNotAllowedInsideStatic => write!(f, "call not allowed inside static call"),
1062            Self::OutOfFunds => write!(f, "out of funds"),
1063            Self::CallTooDeep => write!(f, "call too deep"),
1064        }
1065    }
1066}
1067
1068/// Out of gas errors.
1069#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1070#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1071pub enum OutOfGasError {
1072    /// Basic OOG error. Not enough gas to execute the opcode.
1073    Basic,
1074    /// Tried to expand past memory limit.
1075    MemoryLimit,
1076    /// Basic OOG error from memory expansion
1077    Memory,
1078    /// Precompile threw OOG error
1079    Precompile,
1080    /// When performing something that takes a U256 and casts down to a u64, if its too large this would fire
1081    /// i.e. in `as_usize_or_fail`
1082    InvalidOperand,
1083    /// When performing SSTORE the gasleft is less than or equal to 2300
1084    ReentrancySentry,
1085}
1086
1087impl core::error::Error for OutOfGasError {}
1088
1089impl fmt::Display for OutOfGasError {
1090    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1091        match self {
1092            Self::Basic => write!(f, "out of gas"),
1093            Self::MemoryLimit => write!(f, "out of gas: memory limit exceeded"),
1094            Self::Memory => write!(f, "out of gas: memory expansion"),
1095            Self::Precompile => write!(f, "out of gas: precompile"),
1096            Self::InvalidOperand => write!(f, "out of gas: invalid operand"),
1097            Self::ReentrancySentry => write!(f, "out of gas: reentrancy sentry"),
1098        }
1099    }
1100}
1101
1102/// Error that includes transaction index for batch transaction processing.
1103#[derive(Debug, Clone, PartialEq, Eq)]
1104#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1105pub struct TransactionIndexedError<Error> {
1106    /// The original error that occurred.
1107    pub error: Error,
1108    /// The index of the transaction that failed.
1109    pub transaction_index: usize,
1110}
1111
1112impl<Error> TransactionIndexedError<Error> {
1113    /// Create a new `TransactionIndexedError` with the given error and transaction index.
1114    #[must_use]
1115    pub fn new(error: Error, transaction_index: usize) -> Self {
1116        Self {
1117            error,
1118            transaction_index,
1119        }
1120    }
1121
1122    /// Get a reference to the underlying error.
1123    pub fn error(&self) -> &Error {
1124        &self.error
1125    }
1126
1127    /// Convert into the underlying error.
1128    #[must_use]
1129    pub fn into_error(self) -> Error {
1130        self.error
1131    }
1132}
1133
1134impl<Error: fmt::Display> fmt::Display for TransactionIndexedError<Error> {
1135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1136        write!(
1137            f,
1138            "transaction {} failed: {}",
1139            self.transaction_index, self.error
1140        )
1141    }
1142}
1143
1144impl<Error: core::error::Error + 'static> core::error::Error for TransactionIndexedError<Error> {
1145    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
1146        Some(&self.error)
1147    }
1148}
1149
1150impl From<&'static str> for InvalidTransaction {
1151    fn from(s: &'static str) -> Self {
1152        Self::Str(Cow::Borrowed(s))
1153    }
1154}
1155
1156impl From<String> for InvalidTransaction {
1157    fn from(s: String) -> Self {
1158        Self::Str(Cow::Owned(s))
1159    }
1160}
1161
1162#[cfg(test)]
1163mod tests {
1164    use super::*;
1165
1166    #[test]
1167    fn test_execution_result_display() {
1168        let result: ExecutionResult<HaltReason> = ExecutionResult::Success {
1169            reason: SuccessReason::Return,
1170            gas: ResultGas::new(100000, 26000, 5000, 0, 0),
1171            logs: vec![Log::default(), Log::default()],
1172            output: Output::Call(Bytes::from(vec![1, 2, 3])),
1173        };
1174        assert_eq!(
1175            result.to_string(),
1176            "Success (Return): gas used: 21000, limit: 100000, spent: 26000, refunded: 5000, 2 logs, 3 bytes output"
1177        );
1178
1179        let result: ExecutionResult<HaltReason> = ExecutionResult::Revert {
1180            gas: ResultGas::new(100000, 100000, 0, 0, 0),
1181            logs: vec![],
1182            output: Bytes::from(vec![1, 2, 3, 4]),
1183        };
1184        assert_eq!(
1185            result.to_string(),
1186            "Revert: gas used: 100000, limit: 100000, spent: 100000, 4 bytes output"
1187        );
1188
1189        let result: ExecutionResult<HaltReason> = ExecutionResult::Halt {
1190            reason: HaltReason::OutOfGas(OutOfGasError::Basic),
1191            gas: ResultGas::new(1000000, 1000000, 0, 0, 0),
1192            logs: vec![],
1193        };
1194        assert_eq!(
1195            result.to_string(),
1196            "Halted: out of gas (gas used: 1000000, limit: 1000000, spent: 1000000)"
1197        );
1198    }
1199
1200    #[test]
1201    fn test_result_gas_display() {
1202        // No refund, no floor
1203        assert_eq!(
1204            ResultGas::new(100000, 21000, 0, 0, 0).to_string(),
1205            "gas used: 21000, limit: 100000, spent: 21000"
1206        );
1207        // With refund
1208        assert_eq!(
1209            ResultGas::new(100000, 50000, 10000, 0, 0).to_string(),
1210            "gas used: 40000, limit: 100000, spent: 50000, refunded: 10000"
1211        );
1212        // With refund and floor
1213        assert_eq!(
1214            ResultGas::new(100000, 50000, 10000, 30000, 0).to_string(),
1215            "gas used: 40000, limit: 100000, spent: 50000, refunded: 10000, floor: 30000"
1216        );
1217    }
1218
1219    #[test]
1220    fn test_result_gas_used_and_remaining() {
1221        let gas = ResultGas::new(200, 100, 30, 0, 0);
1222        assert_eq!(gas.limit(), 200);
1223        assert_eq!(gas.spent(), 100);
1224        assert_eq!(gas.inner_refunded(), 30);
1225        assert_eq!(gas.used(), 70);
1226        assert_eq!(gas.remaining(), 100);
1227
1228        // Saturating: refunded > spent
1229        let gas = ResultGas::new(100, 10, 50, 0, 0);
1230        assert_eq!(gas.used(), 0);
1231        assert_eq!(gas.remaining(), 90);
1232    }
1233
1234    #[test]
1235    fn test_final_refunded_with_floor_gas() {
1236        // No floor gas: final_refunded == refunded
1237        let gas = ResultGas::new(100000, 50000, 10000, 0, 0);
1238        assert_eq!(gas.used(), 40000);
1239        assert_eq!(gas.final_refunded(), 10000);
1240
1241        // Floor gas active (spent_sub_refunded < floor_gas): final_refunded == 0
1242        // spent=50000, refunded=10000, spent_sub_refunded=40000 < floor_gas=45000
1243        let gas = ResultGas::new(100000, 50000, 10000, 45000, 0);
1244        assert_eq!(gas.used(), 45000);
1245        assert_eq!(gas.final_refunded(), 0);
1246
1247        // Floor gas inactive (spent_sub_refunded >= floor_gas): final_refunded == refunded
1248        // spent=50000, refunded=10000, spent_sub_refunded=40000 >= floor_gas=30000
1249        let gas = ResultGas::new(100000, 50000, 10000, 30000, 0);
1250        assert_eq!(gas.used(), 40000);
1251        assert_eq!(gas.final_refunded(), 10000);
1252
1253        // Edge case: spent_sub_refunded == floor_gas exactly
1254        // spent=50000, refunded=10000, spent_sub_refunded=40000 == floor_gas=40000
1255        let gas = ResultGas::new(100000, 50000, 10000, 40000, 0);
1256        assert_eq!(gas.used(), 40000);
1257        assert_eq!(gas.final_refunded(), 10000);
1258    }
1259}