Skip to main content

reth_rpc_eth_types/error/
api.rs

1//! Helper traits to wrap generic l1 errors, in network specific error type configured in
2//! `reth_rpc_eth_api::EthApiTypes`.
3
4use crate::{simulate::EthSimulateError, EthApiError, RevertError};
5use alloy_primitives::Bytes;
6use reth_errors::ProviderError;
7use reth_evm::{ConfigureEvm, EvmErrorFor, HaltReasonFor};
8use reth_revm::db::bal::EvmDatabaseError;
9use revm::{context::result::ExecutionResult, context_interface::result::HaltReason};
10
11use super::RpcInvalidTransactionError;
12
13/// Helper trait to wrap core [`EthApiError`].
14pub trait FromEthApiError: From<EthApiError> {
15    /// Converts from error via [`EthApiError`].
16    fn from_eth_err<E>(err: E) -> Self
17    where
18        EthApiError: From<E>;
19}
20
21impl<T> FromEthApiError for T
22where
23    T: From<EthApiError>,
24{
25    fn from_eth_err<E>(err: E) -> Self
26    where
27        EthApiError: From<E>,
28    {
29        T::from(EthApiError::from(err))
30    }
31}
32
33/// Helper trait to wrap core [`EthApiError`].
34pub trait IntoEthApiError: Into<EthApiError> {
35    /// Converts into error via [`EthApiError`].
36    fn into_eth_err<E>(self) -> E
37    where
38        E: FromEthApiError;
39}
40
41impl<T> IntoEthApiError for T
42where
43    EthApiError: From<T>,
44{
45    fn into_eth_err<E>(self) -> E
46    where
47        E: FromEthApiError,
48    {
49        E::from_eth_err(self)
50    }
51}
52
53/// Helper trait to access wrapped core error.
54pub trait AsEthApiError {
55    /// Returns a reference to [`EthApiError`] if this is an error variant inherited from core
56    /// functionality.
57    fn as_err(&self) -> Option<&EthApiError>;
58
59    /// Returns `true` if error is
60    /// [`RpcInvalidTransactionError::GasTooHigh`].
61    fn is_gas_too_high(&self) -> bool {
62        if let Some(err) = self.as_err() {
63            return err.is_gas_too_high()
64        }
65
66        false
67    }
68
69    /// Returns `true` if error is
70    /// [`RpcInvalidTransactionError::GasTooLow`].
71    fn is_gas_too_low(&self) -> bool {
72        if let Some(err) = self.as_err() {
73            return err.is_gas_too_low()
74        }
75
76        false
77    }
78
79    /// Returns [`EthSimulateError`] if this error maps to a simulate-specific error code.
80    fn as_simulate_error(&self) -> Option<EthSimulateError> {
81        let err = self.as_err()?;
82        match err {
83            EthApiError::InvalidTransaction(tx_err) => match tx_err {
84                RpcInvalidTransactionError::NonceTooLow { tx, state } => {
85                    Some(EthSimulateError::NonceTooLow { tx: *tx, state: *state })
86                }
87                RpcInvalidTransactionError::NonceTooHigh => Some(EthSimulateError::NonceTooHigh),
88                RpcInvalidTransactionError::NonceMaxValue => Some(EthSimulateError::NonceMaxValue),
89                RpcInvalidTransactionError::FeeCapTooLow => {
90                    Some(EthSimulateError::BaseFeePerGasTooLow)
91                }
92                RpcInvalidTransactionError::GasTooLow => Some(EthSimulateError::IntrinsicGasTooLow),
93                RpcInvalidTransactionError::InsufficientFunds { cost, balance } => {
94                    Some(EthSimulateError::InsufficientFunds { cost: *cost, balance: *balance })
95                }
96                RpcInvalidTransactionError::SenderNoEOA => Some(EthSimulateError::SenderNotEOA),
97                RpcInvalidTransactionError::MaxInitCodeSizeExceeded => {
98                    Some(EthSimulateError::MaxInitCodeSizeExceeded)
99                }
100                _ => None,
101            },
102            _ => None,
103        }
104    }
105}
106
107impl AsEthApiError for EthApiError {
108    fn as_err(&self) -> Option<&EthApiError> {
109        Some(self)
110    }
111}
112
113/// Helper trait to convert from revm errors.
114pub trait FromEvmError<Evm: ConfigureEvm>:
115    From<EvmErrorFor<Evm, EvmDatabaseError<ProviderError>>>
116    + FromEvmHalt<HaltReasonFor<Evm>>
117    + FromRevert
118{
119    /// Converts from EVM error to this type.
120    fn from_evm_err(err: EvmErrorFor<Evm, EvmDatabaseError<ProviderError>>) -> Self {
121        err.into()
122    }
123
124    /// Ensures the execution result is successful or returns an error,
125    fn ensure_success(result: ExecutionResult<HaltReasonFor<Evm>>) -> Result<Bytes, Self> {
126        match result {
127            ExecutionResult::Success { output, .. } => Ok(output.into_data()),
128            ExecutionResult::Revert { output, .. } => Err(Self::from_revert(output)),
129            ExecutionResult::Halt { reason, gas, .. } => {
130                Err(Self::from_evm_halt(reason, gas.tx_gas_used()))
131            }
132        }
133    }
134}
135
136impl<T, Evm> FromEvmError<Evm> for T
137where
138    T: From<EvmErrorFor<Evm, EvmDatabaseError<ProviderError>>>
139        + FromEvmHalt<HaltReasonFor<Evm>>
140        + FromRevert,
141    Evm: ConfigureEvm,
142{
143}
144
145/// Helper trait to convert from revm errors.
146pub trait FromEvmHalt<Halt> {
147    /// Converts from EVM halt to this type.
148    fn from_evm_halt(halt: Halt, gas_limit: u64) -> Self;
149}
150
151impl FromEvmHalt<HaltReason> for EthApiError {
152    fn from_evm_halt(halt: HaltReason, gas_limit: u64) -> Self {
153        RpcInvalidTransactionError::halt(halt, gas_limit).into()
154    }
155}
156
157/// Helper trait to construct errors from unexpected reverts.
158pub trait FromRevert {
159    /// Constructs an error from revert bytes.
160    ///
161    /// This is only invoked when revert was unexpected (`eth_call`, `eth_estimateGas`, etc).
162    fn from_revert(output: Bytes) -> Self;
163}
164
165impl FromRevert for EthApiError {
166    fn from_revert(output: Bytes) -> Self {
167        RpcInvalidTransactionError::Revert(RevertError::new(output)).into()
168    }
169}