Skip to main content

reth_rpc_eth_types/error/
mod.rs

1//! Implementation specific Errors for the `eth_` namespace.
2
3pub mod api;
4use alloy_eips::BlockId;
5use alloy_evm::{call::CallError, overrides::StateOverrideError};
6use alloy_primitives::{Address, Bytes, B256, U256};
7use alloy_rpc_types_eth::{error::EthRpcErrorCode, request::TransactionInputError, BlockError};
8use alloy_sol_types::{ContractError, RevertReason};
9use alloy_transport::{RpcError, TransportErrorKind};
10pub use api::{AsEthApiError, FromEthApiError, FromEvmError, IntoEthApiError};
11use core::time::Duration;
12use reth_errors::{BlockExecutionError, BlockValidationError, RethError};
13use reth_primitives_traits::transaction::{error::InvalidTransactionError, signed::RecoveryError};
14use reth_revm::db::bal::EvmDatabaseError;
15use reth_rpc_convert::{CallFeesError, EthTxEnvError, TransactionConversionError};
16use reth_rpc_server_types::result::{
17    block_id_to_str, internal_rpc_err, invalid_params_rpc_err, rpc_err, rpc_error_with_code,
18};
19use reth_transaction_pool::error::{
20    Eip4844PoolTransactionError, Eip7702PoolTransactionError, InvalidPoolTransactionError,
21    PoolError, PoolErrorKind, PoolTransactionError,
22};
23use revm::{
24    context_interface::result::{
25        EVMError, HaltReason, InvalidHeader, InvalidTransaction, OutOfGasError,
26    },
27    state::bal::BalError,
28};
29use revm_inspectors::tracing::{DebugInspectorError, MuxError};
30use std::convert::Infallible;
31use tokio::sync::oneshot::error::RecvError;
32
33/// A trait to convert an error to an RPC error.
34pub trait ToRpcError: core::error::Error + Send + Sync + 'static {
35    /// Converts the error to a JSON-RPC error object.
36    fn to_rpc_error(&self) -> jsonrpsee_types::ErrorObject<'static>;
37}
38
39impl ToRpcError for jsonrpsee_types::ErrorObject<'static> {
40    fn to_rpc_error(&self) -> jsonrpsee_types::ErrorObject<'static> {
41        self.clone()
42    }
43}
44
45impl ToRpcError for RpcError<TransportErrorKind> {
46    fn to_rpc_error(&self) -> jsonrpsee_types::ErrorObject<'static> {
47        match self {
48            Self::ErrorResp(payload) => jsonrpsee_types::error::ErrorObject::owned(
49                payload.code as i32,
50                payload.message.clone(),
51                payload.data.clone(),
52            ),
53            err => internal_rpc_err(err.to_string()),
54        }
55    }
56}
57
58/// Result alias
59pub type EthResult<T> = Result<T, EthApiError>;
60
61/// Errors that can occur when interacting with the `eth_` namespace
62#[derive(Debug, thiserror::Error)]
63pub enum EthApiError {
64    /// When a raw transaction is empty
65    #[error("empty transaction data")]
66    EmptyRawTransactionData,
67    /// When decoding a signed transaction fails
68    #[error("failed to decode signed transaction")]
69    FailedToDecodeSignedTransaction,
70    /// When the transaction signature is invalid
71    #[error("invalid transaction signature")]
72    InvalidTransactionSignature,
73    /// Errors related to the transaction pool
74    #[error(transparent)]
75    PoolError(#[from] RpcPoolError),
76    /// Header not found for block hash/number/tag
77    #[error("header not found")]
78    HeaderNotFound(BlockId),
79    /// Header range not found for start block hash/number/tag to end block hash/number/tag
80    #[error("header range not found, start block {0:?}, end block {1:?}")]
81    HeaderRangeNotFound(BlockId, BlockId),
82    /// Thrown when historical data is not available because it has been pruned
83    ///
84    /// This error is intended for use as a standard response when historical data is
85    /// requested that has been pruned according to the node's data retention policy.
86    ///
87    /// See also <https://eips.ethereum.org/EIPS/eip-4444>
88    #[error("pruned history unavailable")]
89    PrunedHistoryUnavailable,
90    /// Receipts not found for block hash/number/tag
91    #[error("receipts not found")]
92    ReceiptsNotFound(BlockId),
93    /// Thrown when an unknown block or transaction index is encountered
94    #[error("unknown block or tx index")]
95    UnknownBlockOrTxIndex,
96    /// When an invalid block range is provided
97    #[error("invalid block range")]
98    InvalidBlockRange,
99    /// Requested block number is beyond the head block
100    #[error("request beyond head block: requested {requested}, head {head}")]
101    RequestBeyondHead {
102        /// The requested block number
103        requested: u64,
104        /// The current head block number
105        head: u64,
106    },
107    /// Thrown when the target block for proof computation exceeds the maximum configured window.
108    #[error("distance to target block exceeds maximum proof window")]
109    ExceedsMaxProofWindow,
110    /// An internal error where prevrandao is not set in the evm's environment
111    #[error("prevrandao not in the EVM's environment after merge")]
112    PrevrandaoNotSet,
113    /// `excess_blob_gas` is not set for Cancun and above
114    #[error("excess blob gas missing in the EVM's environment after Cancun")]
115    ExcessBlobGasNotSet,
116    /// Thrown when a call or transaction request (`eth_call`, `eth_estimateGas`,
117    /// `eth_sendTransaction`) contains conflicting fields (legacy, EIP-1559)
118    #[error("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")]
119    ConflictingFeeFieldsInRequest,
120    /// Errors related to invalid transactions
121    #[error(transparent)]
122    InvalidTransaction(#[from] RpcInvalidTransactionError),
123    /// Thrown when constructing an RPC block from primitive block data fails
124    #[error(transparent)]
125    InvalidBlockData(#[from] BlockError),
126    /// Thrown when an `AccountOverride` contains conflicting `state` and `stateDiff` fields
127    #[error("account {0:?} has both 'state' and 'stateDiff'")]
128    BothStateAndStateDiffInOverride(Address),
129    /// Other internal error
130    #[error(transparent)]
131    Internal(RethError),
132    /// Error related to signing
133    #[error(transparent)]
134    Signing(#[from] SignError),
135    /// Thrown when a requested transaction is not found
136    #[error("transaction not found")]
137    TransactionNotFound,
138    /// Some feature is unsupported
139    #[error("unsupported")]
140    Unsupported(&'static str),
141    /// General purpose error for invalid params
142    #[error("{0}")]
143    InvalidParams(String),
144    /// When the tracer config does not match the tracer
145    #[error("invalid tracer config")]
146    InvalidTracerConfig,
147    /// When the percentile array is invalid
148    #[error("invalid reward percentiles")]
149    InvalidRewardPercentiles,
150    /// Error thrown when a spawned blocking task failed to deliver an anticipated response.
151    ///
152    /// This only happens if the blocking task panics and is aborted before it can return a
153    /// response back to the request handler.
154    #[error("internal blocking task error")]
155    InternalBlockingTaskError,
156    /// Error thrown when a spawned blocking task failed to deliver an anticipated response
157    #[error("internal eth error")]
158    InternalEthError,
159    /// Error thrown when a (tracing) call exceeds the configured timeout
160    #[error("execution aborted (timeout = {0:?})")]
161    ExecutionTimedOut(Duration),
162    /// Internal Error thrown by the javascript tracer
163    #[error("{0}")]
164    InternalJsTracerError(String),
165    #[error(transparent)]
166    /// Call Input error when both `data` and `input` fields are set and not equal.
167    TransactionInputError(#[from] TransactionInputError),
168    /// Evm generic purpose error.
169    #[error("Revm error: {0}")]
170    EvmCustom(String),
171    /// Bytecode override is invalid.
172    ///
173    /// This can happen if bytecode provided in an
174    /// [`AccountOverride`](alloy_rpc_types_eth::state::AccountOverride) is malformed, e.g. invalid
175    /// 7702 bytecode.
176    #[error("Invalid bytecode: {0}")]
177    InvalidBytecode(String),
178    /// Error encountered when converting a transaction type
179    #[error(transparent)]
180    TransactionConversionError(#[from] TransactionConversionError),
181    /// Error thrown when tracing with a muxTracer fails
182    #[error(transparent)]
183    MuxTracerError(#[from] MuxError),
184    /// Error thrown when waiting for transaction confirmation times out
185    #[error(
186        "Transaction {hash} was added to the mempool but wasn't confirmed within {duration:?}."
187    )]
188    TransactionConfirmationTimeout {
189        /// Hash of the transaction that timed out
190        hash: B256,
191        /// Duration that was waited before timing out
192        duration: Duration,
193    },
194    /// Error thrown when batch tx response channel fails
195    #[error(transparent)]
196    BatchTxRecvError(#[from] RecvError),
197    /// Error thrown when batch tx send channel fails
198    #[error("Batch transaction sender channel closed")]
199    BatchTxSendError,
200    /// Error that occurred during `call_many` execution with bundle and transaction context
201    #[error("call_many error in bundle {bundle_index} and transaction {tx_index}: {}", .error.message())]
202    CallManyError {
203        /// Bundle index where the error occurred
204        bundle_index: usize,
205        /// Transaction index within the bundle where the error occurred
206        tx_index: usize,
207        /// The underlying error object
208        error: jsonrpsee_types::ErrorObject<'static>,
209    },
210    /// Error thrown when trying to access block access list for blocks before Amsterdam
211    #[error("Block access list not available for pre-Amsterdam blocks")]
212    BlockAccessListNotAvailablePreAmsterdam,
213    /// Any other error
214    #[error("{0}")]
215    Other(Box<dyn ToRpcError>),
216}
217
218impl EthApiError {
219    /// Creates a new [`EthApiError::Other`] variant.
220    pub fn other<E: ToRpcError>(err: E) -> Self {
221        Self::Other(Box::new(err))
222    }
223
224    /// Creates a new [`EthApiError::CallManyError`] variant.
225    pub const fn call_many_error(
226        bundle_index: usize,
227        tx_index: usize,
228        error: jsonrpsee_types::ErrorObject<'static>,
229    ) -> Self {
230        Self::CallManyError { bundle_index, tx_index, error }
231    }
232
233    /// Returns `true` if error is [`RpcInvalidTransactionError::GasTooHigh`]
234    pub const fn is_gas_too_high(&self) -> bool {
235        matches!(
236            self,
237            Self::InvalidTransaction(
238                RpcInvalidTransactionError::GasTooHigh |
239                    RpcInvalidTransactionError::GasLimitTooHigh
240            )
241        )
242    }
243
244    /// Returns `true` if error is [`RpcInvalidTransactionError::GasTooLow`]
245    pub const fn is_gas_too_low(&self) -> bool {
246        matches!(self, Self::InvalidTransaction(RpcInvalidTransactionError::GasTooLow))
247    }
248
249    /// Returns the [`RpcInvalidTransactionError`] if this is a [`EthApiError::InvalidTransaction`]
250    pub const fn as_invalid_transaction(&self) -> Option<&RpcInvalidTransactionError> {
251        match self {
252            Self::InvalidTransaction(e) => Some(e),
253            _ => None,
254        }
255    }
256
257    /// Converts the given [`StateOverrideError`] into a new [`EthApiError`] instance.
258    pub fn from_state_overrides_err<E>(err: StateOverrideError<E>) -> Self
259    where
260        E: Into<Self>,
261    {
262        err.into()
263    }
264
265    /// Converts the given [`CallError`] into a new [`EthApiError`] instance.
266    pub fn from_call_err<E>(err: CallError<E>) -> Self
267    where
268        E: Into<Self>,
269    {
270        err.into()
271    }
272
273    /// Converts this error into the rpc error object.
274    pub fn into_rpc_err(self) -> jsonrpsee_types::error::ErrorObject<'static> {
275        self.into()
276    }
277}
278
279impl From<EthApiError> for jsonrpsee_types::error::ErrorObject<'static> {
280    fn from(error: EthApiError) -> Self {
281        match error {
282            EthApiError::FailedToDecodeSignedTransaction |
283            EthApiError::InvalidTransactionSignature |
284            EthApiError::EmptyRawTransactionData |
285            EthApiError::InvalidBlockRange |
286            EthApiError::RequestBeyondHead { .. } |
287            EthApiError::ExceedsMaxProofWindow |
288            EthApiError::ConflictingFeeFieldsInRequest |
289            EthApiError::Signing(_) |
290            EthApiError::BothStateAndStateDiffInOverride(_) |
291            EthApiError::InvalidTracerConfig |
292            EthApiError::TransactionConversionError(_) |
293            EthApiError::InvalidRewardPercentiles |
294            EthApiError::InvalidBytecode(_) => invalid_params_rpc_err(error.to_string()),
295            EthApiError::InvalidTransaction(err) => err.into(),
296            EthApiError::PoolError(err) => err.into(),
297            EthApiError::PrevrandaoNotSet |
298            EthApiError::ExcessBlobGasNotSet |
299            EthApiError::InvalidBlockData(_) |
300            EthApiError::Internal(_) |
301            EthApiError::EvmCustom(_) => internal_rpc_err(error.to_string()),
302            EthApiError::UnknownBlockOrTxIndex | EthApiError::TransactionNotFound => {
303                rpc_error_with_code(EthRpcErrorCode::ResourceNotFound.code(), error.to_string())
304            }
305            EthApiError::HeaderNotFound(id) | EthApiError::ReceiptsNotFound(id) => {
306                rpc_error_with_code(
307                    EthRpcErrorCode::ResourceNotFound.code(),
308                    format!("block not found: {}", block_id_to_str(id)),
309                )
310            }
311            EthApiError::HeaderRangeNotFound(start_id, end_id) => rpc_error_with_code(
312                EthRpcErrorCode::ResourceNotFound.code(),
313                format!(
314                    "{error}: start block: {}, end block: {}",
315                    block_id_to_str(start_id),
316                    block_id_to_str(end_id),
317                ),
318            ),
319            err @ EthApiError::TransactionConfirmationTimeout { .. } => rpc_error_with_code(
320                EthRpcErrorCode::TransactionConfirmationTimeout.code(),
321                err.to_string(),
322            ),
323            EthApiError::Unsupported(msg) => internal_rpc_err(msg),
324            EthApiError::InternalJsTracerError(msg) => internal_rpc_err(msg),
325            EthApiError::InvalidParams(msg) => invalid_params_rpc_err(msg),
326            err @ EthApiError::ExecutionTimedOut(_) => rpc_error_with_code(
327                jsonrpsee_types::error::CALL_EXECUTION_FAILED_CODE,
328                err.to_string(),
329            ),
330            err @ (EthApiError::InternalBlockingTaskError | EthApiError::InternalEthError) => {
331                internal_rpc_err(err.to_string())
332            }
333            err @ EthApiError::TransactionInputError(_) => invalid_params_rpc_err(err.to_string()),
334            EthApiError::PrunedHistoryUnavailable => rpc_error_with_code(4444, error.to_string()),
335            EthApiError::Other(err) => err.to_rpc_error(),
336            EthApiError::MuxTracerError(msg) => internal_rpc_err(msg.to_string()),
337            EthApiError::BatchTxRecvError(err) => internal_rpc_err(err.to_string()),
338            EthApiError::BatchTxSendError => {
339                internal_rpc_err("Batch transaction sender channel closed".to_string())
340            }
341            EthApiError::CallManyError { bundle_index, tx_index, error } => {
342                jsonrpsee_types::error::ErrorObject::owned(
343                    error.code(),
344                    format!(
345                        "call_many error in bundle {bundle_index} and transaction {tx_index}: {}",
346                        error.message()
347                    ),
348                    error.data(),
349                )
350            }
351            EthApiError::BlockAccessListNotAvailablePreAmsterdam => {
352                rpc_error_with_code(4445, error.to_string())
353            }
354        }
355    }
356}
357
358impl<E> From<CallError<E>> for EthApiError
359where
360    E: Into<Self>,
361{
362    fn from(value: CallError<E>) -> Self {
363        match value {
364            CallError::Database(err) => err.into(),
365            CallError::InsufficientFunds(insufficient_funds_error) => {
366                Self::InvalidTransaction(RpcInvalidTransactionError::InsufficientFunds {
367                    cost: insufficient_funds_error.cost,
368                    balance: insufficient_funds_error.balance,
369                })
370            }
371        }
372    }
373}
374
375impl<E> From<StateOverrideError<E>> for EthApiError
376where
377    E: Into<Self>,
378{
379    fn from(value: StateOverrideError<E>) -> Self {
380        match value {
381            StateOverrideError::InvalidBytecode(bytecode_decode_error) => {
382                Self::InvalidBytecode(bytecode_decode_error.to_string())
383            }
384            StateOverrideError::BothStateAndStateDiff(address) => {
385                Self::BothStateAndStateDiffInOverride(address)
386            }
387            StateOverrideError::Database(err) => err.into(),
388        }
389    }
390}
391
392impl From<EthTxEnvError> for EthApiError {
393    fn from(value: EthTxEnvError) -> Self {
394        match value {
395            EthTxEnvError::CallFees(CallFeesError::BlobTransactionMissingBlobHashes) => {
396                Self::InvalidTransaction(
397                    RpcInvalidTransactionError::BlobTransactionMissingBlobHashes,
398                )
399            }
400            EthTxEnvError::CallFees(CallFeesError::FeeCapTooLow) => {
401                Self::InvalidTransaction(RpcInvalidTransactionError::FeeCapTooLow)
402            }
403            EthTxEnvError::CallFees(CallFeesError::ConflictingFeeFieldsInRequest) => {
404                Self::ConflictingFeeFieldsInRequest
405            }
406            EthTxEnvError::CallFees(CallFeesError::TipAboveFeeCap) => {
407                Self::InvalidTransaction(RpcInvalidTransactionError::TipAboveFeeCap)
408            }
409            EthTxEnvError::CallFees(CallFeesError::TipVeryHigh) => {
410                Self::InvalidTransaction(RpcInvalidTransactionError::TipVeryHigh)
411            }
412            EthTxEnvError::Input(err) => Self::TransactionInputError(err),
413        }
414    }
415}
416
417impl<E> From<EvmDatabaseError<E>> for EthApiError
418where
419    E: Into<Self>,
420{
421    fn from(value: EvmDatabaseError<E>) -> Self {
422        match value {
423            EvmDatabaseError::Bal(err) => err.into(),
424            EvmDatabaseError::Database(err) => err.into(),
425        }
426    }
427}
428
429impl From<BalError> for EthApiError {
430    fn from(err: BalError) -> Self {
431        Self::EvmCustom(format!("bal error: {:?}", err))
432    }
433}
434
435#[cfg(feature = "js-tracer")]
436impl From<revm_inspectors::tracing::js::JsInspectorError> for EthApiError {
437    fn from(error: revm_inspectors::tracing::js::JsInspectorError) -> Self {
438        match error {
439            err @ revm_inspectors::tracing::js::JsInspectorError::JsError(_) => {
440                Self::InternalJsTracerError(err.to_string())
441            }
442            err => Self::InvalidParams(err.to_string()),
443        }
444    }
445}
446
447impl<Err> From<DebugInspectorError<Err>> for EthApiError
448where
449    Err: core::error::Error + Send + Sync + 'static,
450{
451    fn from(error: DebugInspectorError<Err>) -> Self {
452        match error {
453            DebugInspectorError::InvalidTracerConfig => Self::InvalidTracerConfig,
454            DebugInspectorError::UnsupportedTracer => Self::Unsupported("unsupported tracer"),
455            DebugInspectorError::JsTracerNotEnabled => {
456                Self::Unsupported("JS Tracer is not enabled")
457            }
458            DebugInspectorError::MuxInspector(err) => err.into(),
459            DebugInspectorError::Database(err) => Self::Internal(RethError::other(err)),
460            #[cfg(feature = "js-tracer")]
461            DebugInspectorError::JsInspector(err) => err.into(),
462            #[allow(unreachable_patterns)]
463            _ => Self::Unsupported("unsupported tracer error"),
464        }
465    }
466}
467
468impl From<RethError> for EthApiError {
469    fn from(error: RethError) -> Self {
470        match error {
471            RethError::Provider(err) => err.into(),
472            err => Self::Internal(err),
473        }
474    }
475}
476
477impl From<BlockExecutionError> for EthApiError {
478    fn from(error: BlockExecutionError) -> Self {
479        match error {
480            BlockExecutionError::Validation(validation_error) => match validation_error {
481                BlockValidationError::InvalidTx { error, .. } => {
482                    if let Some(invalid_tx) = error.as_invalid_tx_err() {
483                        Self::InvalidTransaction(RpcInvalidTransactionError::from(
484                            invalid_tx.clone(),
485                        ))
486                    } else {
487                        Self::InvalidTransaction(RpcInvalidTransactionError::other(
488                            rpc_error_with_code(
489                                EthRpcErrorCode::TransactionRejected.code(),
490                                error.to_string(),
491                            ),
492                        ))
493                    }
494                }
495                _ => Self::Internal(RethError::Execution(BlockExecutionError::Validation(
496                    validation_error,
497                ))),
498            },
499            BlockExecutionError::Internal(internal_error) => {
500                Self::Internal(RethError::Execution(BlockExecutionError::Internal(internal_error)))
501            }
502        }
503    }
504}
505
506impl From<reth_errors::ProviderError> for EthApiError {
507    fn from(error: reth_errors::ProviderError) -> Self {
508        use reth_errors::ProviderError;
509        match error {
510            ProviderError::HeaderNotFound(hash) => Self::HeaderNotFound(hash.into()),
511            ProviderError::BlockHashNotFound(hash) | ProviderError::UnknownBlockHash(hash) => {
512                Self::HeaderNotFound(hash.into())
513            }
514            ProviderError::BestBlockNotFound => Self::HeaderNotFound(BlockId::latest()),
515            ProviderError::BlockNumberForTransactionIndexNotFound => Self::UnknownBlockOrTxIndex,
516            ProviderError::FinalizedBlockNotFound => Self::HeaderNotFound(BlockId::finalized()),
517            ProviderError::SafeBlockNotFound => Self::HeaderNotFound(BlockId::safe()),
518            ProviderError::BlockExpired { .. } => Self::PrunedHistoryUnavailable,
519            err => Self::Internal(err.into()),
520        }
521    }
522}
523
524impl From<InvalidHeader> for EthApiError {
525    fn from(value: InvalidHeader) -> Self {
526        match value {
527            InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet,
528            InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet,
529        }
530    }
531}
532
533impl<T, TxError> From<EVMError<T, TxError>> for EthApiError
534where
535    T: Into<Self>,
536    TxError: reth_evm::InvalidTxError,
537{
538    fn from(err: EVMError<T, TxError>) -> Self {
539        match err {
540            EVMError::Transaction(invalid_tx) => {
541                // Try to get the underlying InvalidTransaction if available
542                if let Some(eth_tx_err) = invalid_tx.as_invalid_tx_err() {
543                    // Handle the special NonceTooLow case
544                    match eth_tx_err {
545                        InvalidTransaction::NonceTooLow { tx, state } => {
546                            Self::InvalidTransaction(RpcInvalidTransactionError::NonceTooLow {
547                                tx: *tx,
548                                state: *state,
549                            })
550                        }
551                        _ => RpcInvalidTransactionError::from(eth_tx_err.clone()).into(),
552                    }
553                } else {
554                    // For custom transaction errors that don't wrap InvalidTransaction,
555                    // convert to a custom error message
556                    Self::EvmCustom(invalid_tx.to_string())
557                }
558            }
559            EVMError::Header(err) => err.into(),
560            EVMError::Database(err) => err.into(),
561            EVMError::Custom(err) => Self::EvmCustom(err),
562            EVMError::CustomAny(err) => Self::EvmCustom(err.to_string()),
563        }
564    }
565}
566
567impl From<RecoveryError> for EthApiError {
568    fn from(_: RecoveryError) -> Self {
569        Self::InvalidTransactionSignature
570    }
571}
572
573impl From<Infallible> for EthApiError {
574    fn from(_: Infallible) -> Self {
575        unreachable!()
576    }
577}
578
579/// An error due to invalid transaction.
580///
581/// The only reason this exists is to maintain compatibility with other clients de-facto standard
582/// error messages.
583///
584/// These error variants can be thrown when the transaction is checked prior to execution.
585///
586/// These variants also cover all errors that can be thrown by revm.
587///
588/// ## Nomenclature
589///
590/// This type is explicitly modeled after geth's error variants and uses
591///   `fee cap` for `max_fee_per_gas`
592///   `tip` for `max_priority_fee_per_gas`
593#[derive(thiserror::Error, Debug)]
594pub enum RpcInvalidTransactionError {
595    /// returned if the nonce of a transaction is lower than the one present in the local chain.
596    #[error("nonce too low: next nonce {state}, tx nonce {tx}")]
597    NonceTooLow {
598        /// The nonce of the transaction.
599        tx: u64,
600        /// The current state of the nonce in the local chain.
601        state: u64,
602    },
603    /// returned if the nonce of a transaction is higher than the next one expected based on the
604    /// local chain.
605    #[error("nonce too high")]
606    NonceTooHigh,
607    /// Returned if the nonce of a transaction is too high
608    /// Incrementing the nonce would lead to invalid state (overflow)
609    #[error("nonce has max value")]
610    NonceMaxValue,
611    /// thrown if the transaction sender doesn't have enough funds for a transfer
612    #[error("insufficient funds for transfer")]
613    InsufficientFundsForTransfer,
614    /// thrown if creation transaction provides the init code bigger than init code size limit.
615    #[error("max initcode size exceeded")]
616    MaxInitCodeSizeExceeded,
617    /// Represents the inability to cover max fee + value (account balance too low).
618    #[error("insufficient funds for gas * price + value: have {balance} want {cost}")]
619    InsufficientFunds {
620        /// Transaction cost.
621        cost: U256,
622        /// Current balance of transaction sender.
623        balance: U256,
624    },
625    /// This is similar to [`Self::InsufficientFunds`] but with a different error message and
626    /// exists for compatibility reasons.
627    ///
628    /// This error is used in `eth_estimateCall` when the highest available gas limit, capped with
629    /// the allowance of the caller is too low: [`Self::GasTooLow`].
630    #[error("gas required exceeds allowance ({gas_limit})")]
631    GasRequiredExceedsAllowance {
632        /// The gas limit the transaction was executed with.
633        gas_limit: u64,
634    },
635    /// Thrown when calculating gas usage
636    #[error("gas uint64 overflow")]
637    GasUintOverflow,
638    /// Thrown if the transaction is specified to use less gas than required to start the
639    /// invocation.
640    #[error("intrinsic gas too low")]
641    GasTooLow,
642    /// Thrown if the transaction gas exceeds the limit
643    #[error("intrinsic gas too high")]
644    GasTooHigh,
645    /// Thrown if the transaction gas limit exceeds the maximum
646    #[error("gas limit too high")]
647    GasLimitTooHigh,
648    /// Thrown if a transaction is not supported in the current network configuration.
649    #[error("transaction type not supported")]
650    TxTypeNotSupported,
651    /// Thrown to ensure no one is able to specify a transaction with a tip higher than the total
652    /// fee cap.
653    #[error("max priority fee per gas higher than max fee per gas")]
654    TipAboveFeeCap,
655    /// A sanity error to avoid huge numbers specified in the tip field.
656    #[error("max priority fee per gas higher than 2^256-1")]
657    TipVeryHigh,
658    /// A sanity error to avoid huge numbers specified in the fee cap field.
659    #[error("max fee per gas higher than 2^256-1")]
660    FeeCapVeryHigh,
661    /// Thrown post London if the transaction's fee is less than the base fee of the block
662    #[error("max fee per gas less than block base fee")]
663    FeeCapTooLow,
664    /// Thrown if the sender of a transaction is a contract.
665    #[error("sender is not an EOA")]
666    SenderNoEOA,
667    /// Gas limit was exceeded during execution.
668    /// Contains the gas limit.
669    #[error("out of gas: gas required exceeds: {0}")]
670    BasicOutOfGas(u64),
671    /// Gas limit was exceeded during memory expansion.
672    /// Contains the gas limit.
673    #[error("out of gas: gas exhausted during memory expansion: {0}")]
674    MemoryOutOfGas(u64),
675    /// Memory limit was exceeded during memory expansion.
676    #[error("out of memory: memory limit exceeded during memory expansion")]
677    MemoryLimitOutOfGas,
678    /// Gas limit was exceeded during precompile execution.
679    /// Contains the gas limit.
680    #[error("out of gas: gas exhausted during precompiled contract execution: {0}")]
681    PrecompileOutOfGas(u64),
682    /// An operand to an opcode was invalid or out of range.
683    /// Contains the gas limit.
684    #[error("out of gas: invalid operand to an opcode: {0}")]
685    InvalidOperandOutOfGas(u64),
686    /// Thrown if executing a transaction failed during estimate/call
687    #[error(transparent)]
688    Revert(RevertError),
689    /// Unspecific EVM halt error.
690    #[error("EVM error: {0:?}")]
691    EvmHalt(HaltReason),
692    /// Invalid chain id set for the transaction.
693    #[error("invalid chain ID")]
694    InvalidChainId,
695    /// The transaction is before Spurious Dragon and has a chain ID
696    #[error("transactions before Spurious Dragon should not have a chain ID")]
697    OldLegacyChainId,
698    /// The transaction is before Berlin and has access list
699    #[error("transactions before Berlin should not have access list")]
700    AccessListNotSupported,
701    /// `max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.
702    #[error("max_fee_per_blob_gas is not supported for blocks before the Cancun hardfork")]
703    MaxFeePerBlobGasNotSupported,
704    /// `blob_hashes`/`blob_versioned_hashes` is not supported for blocks before the Cancun
705    /// hardfork.
706    #[error("blob_versioned_hashes is not supported for blocks before the Cancun hardfork")]
707    BlobVersionedHashesNotSupported,
708    /// Block `blob_base_fee` is greater than tx-specified `max_fee_per_blob_gas` after Cancun.
709    #[error("max fee per blob gas less than block blob gas fee")]
710    BlobFeeCapTooLow,
711    /// Blob transaction has a versioned hash with an invalid blob
712    #[error("blob hash version mismatch")]
713    BlobHashVersionMismatch,
714    /// Blob transaction has no versioned hashes
715    #[error("blob transaction missing blob hashes")]
716    BlobTransactionMissingBlobHashes,
717    /// Blob transaction has too many blobs
718    #[error("blob transaction exceeds max blobs per block; got {have}")]
719    TooManyBlobs {
720        /// The number of blobs in the transaction.
721        have: usize,
722    },
723    /// Blob transaction is a create transaction
724    #[error("blob transaction is a create transaction")]
725    BlobTransactionIsCreate,
726    /// EIP-7702 is not enabled.
727    #[error("EIP-7702 authorization list not supported")]
728    AuthorizationListNotSupported,
729    /// EIP-7702 transaction has invalid fields set.
730    #[error("EIP-7702 authorization list has invalid fields")]
731    AuthorizationListInvalidFields,
732    /// Transaction priority fee is below the minimum required priority fee.
733    #[error("transaction priority fee below minimum required priority fee {minimum_priority_fee}")]
734    PriorityFeeBelowMinimum {
735        /// Minimum required priority fee.
736        minimum_priority_fee: u128,
737    },
738    /// Any other error
739    #[error("{0}")]
740    Other(Box<dyn ToRpcError>),
741}
742
743impl RpcInvalidTransactionError {
744    /// Creates a new [`RpcInvalidTransactionError::Other`] variant.
745    pub fn other<E: ToRpcError>(err: E) -> Self {
746        Self::Other(Box::new(err))
747    }
748
749    /// Returns the rpc error code for this error.
750    pub const fn error_code(&self) -> i32 {
751        match self {
752            Self::InvalidChainId |
753            Self::GasTooLow |
754            Self::GasTooHigh |
755            Self::GasRequiredExceedsAllowance { .. } |
756            Self::NonceTooLow { .. } |
757            Self::NonceTooHigh { .. } |
758            Self::FeeCapTooLow |
759            Self::FeeCapVeryHigh => EthRpcErrorCode::InvalidInput.code(),
760            Self::Revert(_) => EthRpcErrorCode::ExecutionError.code(),
761            _ => EthRpcErrorCode::TransactionRejected.code(),
762        }
763    }
764
765    /// Converts the halt error
766    ///
767    /// Takes the configured gas limit of the transaction which is attached to the error
768    pub fn halt(reason: HaltReason, gas_limit: u64) -> Self {
769        match reason {
770            HaltReason::OutOfGas(err) => Self::out_of_gas(err, gas_limit),
771            HaltReason::NonceOverflow => Self::NonceMaxValue,
772            err => Self::EvmHalt(err),
773        }
774    }
775
776    /// Converts the out of gas error
777    pub const fn out_of_gas(reason: OutOfGasError, gas_limit: u64) -> Self {
778        match reason {
779            OutOfGasError::Basic | OutOfGasError::ReentrancySentry => {
780                Self::BasicOutOfGas(gas_limit)
781            }
782            OutOfGasError::Memory => Self::MemoryOutOfGas(gas_limit),
783            OutOfGasError::MemoryLimit => Self::MemoryLimitOutOfGas,
784            OutOfGasError::Precompile => Self::PrecompileOutOfGas(gas_limit),
785            OutOfGasError::InvalidOperand => Self::InvalidOperandOutOfGas(gas_limit),
786        }
787    }
788
789    /// Converts this error into the rpc error object.
790    pub fn into_rpc_err(self) -> jsonrpsee_types::error::ErrorObject<'static> {
791        self.into()
792    }
793}
794
795impl From<RpcInvalidTransactionError> for jsonrpsee_types::error::ErrorObject<'static> {
796    fn from(err: RpcInvalidTransactionError) -> Self {
797        match err {
798            RpcInvalidTransactionError::Revert(revert) => {
799                // include out data if some
800                rpc_err(
801                    revert.error_code(),
802                    revert.to_string(),
803                    revert.output.as_ref().map(|out| out.as_ref()),
804                )
805            }
806            RpcInvalidTransactionError::Other(err) => err.to_rpc_error(),
807            err => rpc_err(err.error_code(), err.to_string(), None),
808        }
809    }
810}
811
812impl From<InvalidTransaction> for RpcInvalidTransactionError {
813    fn from(err: InvalidTransaction) -> Self {
814        match err {
815            InvalidTransaction::InvalidChainId | InvalidTransaction::MissingChainId => {
816                Self::InvalidChainId
817            }
818            InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap,
819            InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow,
820            InvalidTransaction::CallerGasLimitMoreThanBlock |
821            InvalidTransaction::TxGasLimitGreaterThanCap { .. } => {
822                // tx.gas > block.gas_limit
823                Self::GasTooHigh
824            }
825            InvalidTransaction::CallGasCostMoreThanGasLimit { .. } => {
826                // tx.gas < cost
827                Self::GasTooLow
828            }
829            InvalidTransaction::GasFloorMoreThanGasLimit { .. } => {
830                // Post prague EIP-7623 tx floor calldata gas cost > tx.gas_limit
831                // where floor gas is the minimum amount of gas that will be spent
832                // In other words, the tx's gas limit is lower that the minimum gas requirements of
833                // the tx's calldata
834                Self::GasTooLow
835            }
836            InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA,
837            InvalidTransaction::LackOfFundForMaxFee { fee, balance } => {
838                Self::InsufficientFunds { cost: *fee, balance: *balance }
839            }
840            InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow,
841            InvalidTransaction::NonceOverflowInTransaction => Self::NonceMaxValue,
842            InvalidTransaction::CreateInitCodeSizeLimit => Self::MaxInitCodeSizeExceeded,
843            InvalidTransaction::NonceTooHigh { .. } => Self::NonceTooHigh,
844            InvalidTransaction::NonceTooLow { tx, state } => Self::NonceTooLow { tx, state },
845            InvalidTransaction::AccessListNotSupported => Self::AccessListNotSupported,
846            InvalidTransaction::MaxFeePerBlobGasNotSupported => Self::MaxFeePerBlobGasNotSupported,
847            InvalidTransaction::BlobVersionedHashesNotSupported => {
848                Self::BlobVersionedHashesNotSupported
849            }
850            InvalidTransaction::BlobGasPriceGreaterThanMax { .. } => Self::BlobFeeCapTooLow,
851            InvalidTransaction::EmptyBlobs => Self::BlobTransactionMissingBlobHashes,
852            InvalidTransaction::BlobVersionNotSupported => Self::BlobHashVersionMismatch,
853            InvalidTransaction::TooManyBlobs { have, .. } => Self::TooManyBlobs { have },
854            InvalidTransaction::BlobCreateTransaction => Self::BlobTransactionIsCreate,
855            InvalidTransaction::AuthorizationListNotSupported => {
856                Self::AuthorizationListNotSupported
857            }
858            InvalidTransaction::AuthorizationListInvalidFields |
859            InvalidTransaction::EmptyAuthorizationList => Self::AuthorizationListInvalidFields,
860            InvalidTransaction::Eip2930NotSupported |
861            InvalidTransaction::Eip1559NotSupported |
862            InvalidTransaction::Eip4844NotSupported |
863            InvalidTransaction::Eip7702NotSupported |
864            InvalidTransaction::Eip7873NotSupported => Self::TxTypeNotSupported,
865            InvalidTransaction::Eip7873MissingTarget => {
866                Self::other(internal_rpc_err(err.to_string()))
867            }
868            InvalidTransaction::Str(_) => Self::other(internal_rpc_err(err.to_string())),
869        }
870    }
871}
872
873impl From<InvalidTransactionError> for RpcInvalidTransactionError {
874    fn from(err: InvalidTransactionError) -> Self {
875        use InvalidTransactionError;
876        // This conversion is used to convert any transaction errors that could occur inside the
877        // txpool (e.g. `eth_sendRawTransaction`) to their corresponding RPC
878        match err {
879            InvalidTransactionError::InsufficientFunds(res) => {
880                Self::InsufficientFunds { cost: res.expected, balance: res.got }
881            }
882            InvalidTransactionError::NonceNotConsistent { tx, state } => {
883                Self::NonceTooLow { tx, state }
884            }
885            InvalidTransactionError::OldLegacyChainId => {
886                // Note: this should be unreachable since Spurious Dragon now enabled
887                Self::OldLegacyChainId
888            }
889            InvalidTransactionError::ChainIdMismatch => Self::InvalidChainId,
890            InvalidTransactionError::Eip2930Disabled |
891            InvalidTransactionError::Eip1559Disabled |
892            InvalidTransactionError::Eip4844Disabled |
893            InvalidTransactionError::Eip7702Disabled |
894            InvalidTransactionError::TxTypeNotSupported => Self::TxTypeNotSupported,
895            InvalidTransactionError::GasUintOverflow => Self::GasUintOverflow,
896            InvalidTransactionError::GasTooLow => Self::GasTooLow,
897            InvalidTransactionError::GasTooHigh => Self::GasTooHigh,
898            InvalidTransactionError::TipAboveFeeCap => Self::TipAboveFeeCap,
899            InvalidTransactionError::FeeCapTooLow => Self::FeeCapTooLow,
900            InvalidTransactionError::SignerAccountHasBytecode => Self::SenderNoEOA,
901            InvalidTransactionError::GasLimitTooHigh => Self::GasLimitTooHigh,
902        }
903    }
904}
905
906/// Represents a reverted transaction and its output data.
907///
908/// Displays "execution reverted(: reason)?" if the reason is a string.
909#[derive(Debug, Clone, thiserror::Error)]
910pub struct RevertError {
911    /// The transaction output data
912    ///
913    /// Note: this is `None` if output was empty
914    output: Option<Bytes>,
915}
916
917// === impl RevertError ==
918
919impl RevertError {
920    /// Wraps the output bytes
921    ///
922    /// Note: this is intended to wrap a revm output
923    pub fn new(output: Bytes) -> Self {
924        if output.is_empty() {
925            Self { output: None }
926        } else {
927            Self { output: Some(output) }
928        }
929    }
930
931    /// Returns error code to return for this error.
932    pub const fn error_code(&self) -> i32 {
933        EthRpcErrorCode::ExecutionError.code()
934    }
935}
936
937impl std::fmt::Display for RevertError {
938    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
939        f.write_str("execution reverted")?;
940        if let Some(reason) = self.output.as_ref().and_then(|out| RevertReason::decode(out)) {
941            let error = reason.to_string();
942            let mut error = error.as_str();
943            if matches!(reason, RevertReason::ContractError(ContractError::Revert(_))) {
944                // we strip redundant `revert: ` prefix from the revert reason
945                error = error.trim_start_matches("revert: ");
946            }
947            write!(f, ": {error}")?;
948        }
949        Ok(())
950    }
951}
952
953/// A helper error type that's mainly used to mirror `geth` Txpool's error messages
954#[derive(Debug, thiserror::Error)]
955pub enum RpcPoolError {
956    /// When the transaction is already known
957    #[error("already known")]
958    AlreadyKnown,
959    /// When the sender is invalid
960    #[error("invalid sender")]
961    InvalidSender,
962    /// When the transaction is underpriced
963    #[error("transaction underpriced")]
964    Underpriced,
965    /// When the transaction pool is full
966    #[error("txpool is full")]
967    TxPoolOverflow,
968    /// When the replacement transaction is underpriced
969    #[error("replacement transaction underpriced")]
970    ReplaceUnderpriced,
971    /// When the transaction exceeds the block gas limit
972    #[error("exceeds block gas limit")]
973    ExceedsGasLimit,
974    /// When the transaction gas limit exceeds the maximum transaction gas limit
975    #[error("exceeds max transaction gas limit")]
976    MaxTxGasLimitExceeded,
977    /// Thrown when a new transaction is added to the pool, but then immediately discarded to
978    /// respect the tx fee exceeds the configured cap
979    #[error("tx fee ({max_tx_fee_wei} wei) exceeds the configured cap ({tx_fee_cap_wei} wei)")]
980    ExceedsFeeCap {
981        /// max fee in wei of new tx submitted to the pool (e.g. 0.11534 ETH)
982        max_tx_fee_wei: u128,
983        /// configured tx fee cap in wei (e.g. 1.0 ETH)
984        tx_fee_cap_wei: u128,
985    },
986    /// When a negative value is encountered
987    #[error("negative value")]
988    NegativeValue,
989    /// When oversized data is encountered
990    #[error("oversized data: transaction size {size}, limit {limit}")]
991    OversizedData {
992        /// Size of the transaction/input data that exceeded the limit.
993        size: usize,
994        /// Configured limit that was exceeded.
995        limit: usize,
996    },
997    /// When the max initcode size is exceeded
998    #[error("max initcode size exceeded")]
999    ExceedsMaxInitCodeSize,
1000    /// Errors related to invalid transactions
1001    #[error(transparent)]
1002    Invalid(#[from] RpcInvalidTransactionError),
1003    /// Custom pool error
1004    #[error(transparent)]
1005    PoolTransactionError(Box<dyn PoolTransactionError>),
1006    /// EIP-4844 related error
1007    #[error(transparent)]
1008    Eip4844(#[from] Eip4844PoolTransactionError),
1009    /// EIP-7702 related error
1010    #[error(transparent)]
1011    Eip7702(#[from] Eip7702PoolTransactionError),
1012    /// Thrown if a conflicting transaction type is already in the pool
1013    ///
1014    /// In other words, thrown if a transaction with the same sender that violates the exclusivity
1015    /// constraint (blob vs normal tx)
1016    #[error("address already reserved")]
1017    AddressAlreadyReserved,
1018    /// Other unspecified error
1019    #[error(transparent)]
1020    Other(Box<dyn core::error::Error + Send + Sync>),
1021}
1022
1023impl From<RpcPoolError> for jsonrpsee_types::error::ErrorObject<'static> {
1024    fn from(error: RpcPoolError) -> Self {
1025        match error {
1026            RpcPoolError::Invalid(err) => err.into(),
1027            RpcPoolError::TxPoolOverflow => {
1028                rpc_error_with_code(EthRpcErrorCode::TransactionRejected.code(), error.to_string())
1029            }
1030            RpcPoolError::AlreadyKnown |
1031            RpcPoolError::InvalidSender |
1032            RpcPoolError::Underpriced |
1033            RpcPoolError::ReplaceUnderpriced |
1034            RpcPoolError::ExceedsGasLimit |
1035            RpcPoolError::MaxTxGasLimitExceeded |
1036            RpcPoolError::ExceedsFeeCap { .. } |
1037            RpcPoolError::NegativeValue |
1038            RpcPoolError::OversizedData { .. } |
1039            RpcPoolError::ExceedsMaxInitCodeSize |
1040            RpcPoolError::PoolTransactionError(_) |
1041            RpcPoolError::Eip4844(_) |
1042            RpcPoolError::Eip7702(_) |
1043            RpcPoolError::AddressAlreadyReserved => {
1044                rpc_error_with_code(EthRpcErrorCode::InvalidInput.code(), error.to_string())
1045            }
1046            RpcPoolError::Other(other) => internal_rpc_err(other.to_string()),
1047        }
1048    }
1049}
1050
1051impl From<PoolError> for RpcPoolError {
1052    fn from(err: PoolError) -> Self {
1053        match err.kind {
1054            PoolErrorKind::ReplacementUnderpriced => Self::ReplaceUnderpriced,
1055            PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(_) => Self::Underpriced,
1056            PoolErrorKind::SpammerExceededCapacity(_) | PoolErrorKind::DiscardedOnInsert => {
1057                Self::TxPoolOverflow
1058            }
1059            PoolErrorKind::InvalidTransaction(err) => err.into(),
1060            PoolErrorKind::Other(err) => Self::Other(err),
1061            PoolErrorKind::AlreadyImported => Self::AlreadyKnown,
1062            PoolErrorKind::ExistingConflictingTransactionType(_, _) => Self::AddressAlreadyReserved,
1063        }
1064    }
1065}
1066
1067impl From<InvalidPoolTransactionError> for RpcPoolError {
1068    fn from(err: InvalidPoolTransactionError) -> Self {
1069        match err {
1070            InvalidPoolTransactionError::Consensus(err) => Self::Invalid(err.into()),
1071            InvalidPoolTransactionError::ExceedsGasLimit(_, _) => Self::ExceedsGasLimit,
1072            InvalidPoolTransactionError::MaxTxGasLimitExceeded(_, _) => Self::MaxTxGasLimitExceeded,
1073            InvalidPoolTransactionError::ExceedsFeeCap { max_tx_fee_wei, tx_fee_cap_wei } => {
1074                Self::ExceedsFeeCap { max_tx_fee_wei, tx_fee_cap_wei }
1075            }
1076            InvalidPoolTransactionError::ExceedsMaxInitCodeSize(_, _) => {
1077                Self::ExceedsMaxInitCodeSize
1078            }
1079            InvalidPoolTransactionError::IntrinsicGasTooLow => {
1080                Self::Invalid(RpcInvalidTransactionError::GasTooLow)
1081            }
1082            InvalidPoolTransactionError::OversizedData { size, limit } => {
1083                Self::OversizedData { size, limit }
1084            }
1085            InvalidPoolTransactionError::Underpriced => Self::Underpriced,
1086            InvalidPoolTransactionError::Eip2681 => {
1087                Self::Invalid(RpcInvalidTransactionError::NonceMaxValue)
1088            }
1089            InvalidPoolTransactionError::Other(err) => Self::PoolTransactionError(err),
1090            InvalidPoolTransactionError::Eip4844(err) => Self::Eip4844(err),
1091            InvalidPoolTransactionError::Eip7702(err) => Self::Eip7702(err),
1092            InvalidPoolTransactionError::Overdraft { cost, balance } => {
1093                Self::Invalid(RpcInvalidTransactionError::InsufficientFunds { cost, balance })
1094            }
1095            InvalidPoolTransactionError::PriorityFeeBelowMinimum { minimum_priority_fee } => {
1096                Self::Invalid(RpcInvalidTransactionError::PriorityFeeBelowMinimum {
1097                    minimum_priority_fee,
1098                })
1099            }
1100        }
1101    }
1102}
1103
1104impl From<PoolError> for EthApiError {
1105    fn from(err: PoolError) -> Self {
1106        Self::PoolError(RpcPoolError::from(err))
1107    }
1108}
1109
1110/// Errors returned from a sign request.
1111#[derive(Debug, thiserror::Error)]
1112pub enum SignError {
1113    /// Error occurred while trying to sign data.
1114    #[error("could not sign")]
1115    CouldNotSign,
1116    /// Signer for requested account not found.
1117    #[error("unknown account")]
1118    NoAccount,
1119    /// `TypedData` has invalid format.
1120    #[error("given typed data is not valid")]
1121    InvalidTypedData,
1122    /// Invalid transaction request in `sign_transaction`.
1123    #[error("invalid transaction request")]
1124    InvalidTransactionRequest,
1125    /// No chain ID was given.
1126    #[error("no chainid")]
1127    NoChainId,
1128}
1129
1130#[cfg(test)]
1131mod tests {
1132    use super::*;
1133    use alloy_sol_types::{Revert, SolError};
1134    use revm::primitives::b256;
1135
1136    #[test]
1137    fn timed_out_error() {
1138        let err = EthApiError::ExecutionTimedOut(Duration::from_secs(10));
1139        assert_eq!(err.to_string(), "execution aborted (timeout = 10s)");
1140    }
1141
1142    #[test]
1143    fn header_not_found_message() {
1144        let err: jsonrpsee_types::error::ErrorObject<'static> =
1145            EthApiError::HeaderNotFound(BlockId::hash(b256!(
1146                "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1147            )))
1148            .into();
1149        assert_eq!(
1150            err.message(),
1151            "block not found: hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1152        );
1153        let err: jsonrpsee_types::error::ErrorObject<'static> =
1154            EthApiError::HeaderNotFound(BlockId::hash_canonical(b256!(
1155                "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1156            )))
1157            .into();
1158        assert_eq!(
1159            err.message(),
1160            "block not found: canonical hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1161        );
1162        let err: jsonrpsee_types::error::ErrorObject<'static> =
1163            EthApiError::HeaderNotFound(BlockId::number(100000)).into();
1164        assert_eq!(err.message(), "block not found: 0x186a0");
1165        let err: jsonrpsee_types::error::ErrorObject<'static> =
1166            EthApiError::HeaderNotFound(BlockId::latest()).into();
1167        assert_eq!(err.message(), "block not found: latest");
1168        let err: jsonrpsee_types::error::ErrorObject<'static> =
1169            EthApiError::HeaderNotFound(BlockId::safe()).into();
1170        assert_eq!(err.message(), "block not found: safe");
1171        let err: jsonrpsee_types::error::ErrorObject<'static> =
1172            EthApiError::HeaderNotFound(BlockId::finalized()).into();
1173        assert_eq!(err.message(), "block not found: finalized");
1174    }
1175
1176    #[test]
1177    fn receipts_not_found_message() {
1178        let err: jsonrpsee_types::error::ErrorObject<'static> =
1179            EthApiError::ReceiptsNotFound(BlockId::hash(b256!(
1180                "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1181            )))
1182            .into();
1183        assert_eq!(
1184            err.message(),
1185            "block not found: hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1186        );
1187        let err: jsonrpsee_types::error::ErrorObject<'static> =
1188            EthApiError::ReceiptsNotFound(BlockId::hash_canonical(b256!(
1189                "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1190            )))
1191            .into();
1192        assert_eq!(
1193            err.message(),
1194            "block not found: canonical hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1195        );
1196        let err: jsonrpsee_types::error::ErrorObject<'static> =
1197            EthApiError::ReceiptsNotFound(BlockId::number(100000)).into();
1198        assert_eq!(err.code(), EthRpcErrorCode::ResourceNotFound.code());
1199        assert_eq!(err.message(), "block not found: 0x186a0");
1200        let err: jsonrpsee_types::error::ErrorObject<'static> =
1201            EthApiError::ReceiptsNotFound(BlockId::latest()).into();
1202        assert_eq!(err.message(), "block not found: latest");
1203        let err: jsonrpsee_types::error::ErrorObject<'static> =
1204            EthApiError::ReceiptsNotFound(BlockId::safe()).into();
1205        assert_eq!(err.message(), "block not found: safe");
1206        let err: jsonrpsee_types::error::ErrorObject<'static> =
1207            EthApiError::ReceiptsNotFound(BlockId::finalized()).into();
1208        assert_eq!(err.message(), "block not found: finalized");
1209        let err: jsonrpsee_types::error::ErrorObject<'static> =
1210            EthApiError::ReceiptsNotFound(BlockId::pending()).into();
1211        assert_eq!(err.message(), "block not found: pending");
1212        let err: jsonrpsee_types::error::ErrorObject<'static> =
1213            EthApiError::ReceiptsNotFound(BlockId::earliest()).into();
1214        assert_eq!(err.message(), "block not found: earliest");
1215    }
1216
1217    #[test]
1218    fn revert_err_display() {
1219        let revert = Revert::from("test_revert_reason");
1220        let err = RevertError::new(revert.abi_encode().into());
1221        let msg = err.to_string();
1222        assert_eq!(msg, "execution reverted: test_revert_reason");
1223    }
1224}