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 reference to [`EthApiError`], if this 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::FeeCapTooLow => {
89                    Some(EthSimulateError::BaseFeePerGasTooLow)
90                }
91                RpcInvalidTransactionError::GasTooLow => Some(EthSimulateError::IntrinsicGasTooLow),
92                RpcInvalidTransactionError::InsufficientFunds { cost, balance } => {
93                    Some(EthSimulateError::InsufficientFunds { cost: *cost, balance: *balance })
94                }
95                RpcInvalidTransactionError::SenderNoEOA => Some(EthSimulateError::SenderNotEOA),
96                RpcInvalidTransactionError::MaxInitCodeSizeExceeded => {
97                    Some(EthSimulateError::MaxInitCodeSizeExceeded)
98                }
99                _ => None,
100            },
101            _ => None,
102        }
103    }
104}
105
106impl AsEthApiError for EthApiError {
107    fn as_err(&self) -> Option<&EthApiError> {
108        Some(self)
109    }
110}
111
112/// Helper trait to convert from revm errors.
113pub trait FromEvmError<Evm: ConfigureEvm>:
114    From<EvmErrorFor<Evm, EvmDatabaseError<ProviderError>>>
115    + FromEvmHalt<HaltReasonFor<Evm>>
116    + FromRevert
117{
118    /// Converts from EVM error to this type.
119    fn from_evm_err(err: EvmErrorFor<Evm, EvmDatabaseError<ProviderError>>) -> Self {
120        err.into()
121    }
122
123    /// Ensures the execution result is successful or returns an error,
124    fn ensure_success(result: ExecutionResult<HaltReasonFor<Evm>>) -> Result<Bytes, Self> {
125        match result {
126            ExecutionResult::Success { output, .. } => Ok(output.into_data()),
127            ExecutionResult::Revert { output, .. } => Err(Self::from_revert(output)),
128            ExecutionResult::Halt { reason, gas_used } => {
129                Err(Self::from_evm_halt(reason, gas_used))
130            }
131        }
132    }
133}
134
135impl<T, Evm> FromEvmError<Evm> for T
136where
137    T: From<EvmErrorFor<Evm, EvmDatabaseError<ProviderError>>>
138        + FromEvmHalt<HaltReasonFor<Evm>>
139        + FromRevert,
140    Evm: ConfigureEvm,
141{
142}
143
144/// Helper trait to convert from revm errors.
145pub trait FromEvmHalt<Halt> {
146    /// Converts from EVM halt to this type.
147    fn from_evm_halt(halt: Halt, gas_limit: u64) -> Self;
148}
149
150impl FromEvmHalt<HaltReason> for EthApiError {
151    fn from_evm_halt(halt: HaltReason, gas_limit: u64) -> Self {
152        RpcInvalidTransactionError::halt(halt, gas_limit).into()
153    }
154}
155
156/// Helper trait to construct errors from unexpected reverts.
157pub trait FromRevert {
158    /// Constructs an error from revert bytes.
159    ///
160    /// This is only invoked when revert was unexpected (`eth_call`, `eth_estimateGas`, etc).
161    fn from_revert(output: Bytes) -> Self;
162}
163
164impl FromRevert for EthApiError {
165    fn from_revert(output: Bytes) -> Self {
166        RpcInvalidTransactionError::Revert(RevertError::new(output)).into()
167    }
168}