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, RawPoolTransactionError,
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<RawPoolTransactionError> for EthApiError {
574    fn from(err: RawPoolTransactionError) -> Self {
575        match err {
576            RawPoolTransactionError::EmptyRawTransactionData => Self::EmptyRawTransactionData,
577            RawPoolTransactionError::FailedToDecodeSignedTransaction => {
578                Self::FailedToDecodeSignedTransaction
579            }
580            RawPoolTransactionError::InvalidTransactionSignature => {
581                Self::InvalidTransactionSignature
582            }
583            RawPoolTransactionError::Other(err) => Self::PoolError(RpcPoolError::Other(err)),
584        }
585    }
586}
587
588impl From<Infallible> for EthApiError {
589    fn from(_: Infallible) -> Self {
590        unreachable!()
591    }
592}
593
594/// An error due to invalid transaction.
595///
596/// The only reason this exists is to maintain compatibility with other clients de-facto standard
597/// error messages.
598///
599/// These error variants can be thrown when the transaction is checked prior to execution.
600///
601/// These variants also cover all errors that can be thrown by revm.
602///
603/// ## Nomenclature
604///
605/// This type is explicitly modeled after geth's error variants and uses
606///   `fee cap` for `max_fee_per_gas`
607///   `tip` for `max_priority_fee_per_gas`
608#[derive(thiserror::Error, Debug)]
609pub enum RpcInvalidTransactionError {
610    /// returned if the nonce of a transaction is lower than the one present in the local chain.
611    #[error("nonce too low: next nonce {state}, tx nonce {tx}")]
612    NonceTooLow {
613        /// The nonce of the transaction.
614        tx: u64,
615        /// The current state of the nonce in the local chain.
616        state: u64,
617    },
618    /// returned if the nonce of a transaction is higher than the next one expected based on the
619    /// local chain.
620    #[error("nonce too high")]
621    NonceTooHigh,
622    /// Returned if the nonce of a transaction is too high
623    /// Incrementing the nonce would lead to invalid state (overflow)
624    #[error("nonce has max value")]
625    NonceMaxValue,
626    /// thrown if the transaction sender doesn't have enough funds for a transfer
627    #[error("insufficient funds for transfer")]
628    InsufficientFundsForTransfer,
629    /// thrown if creation transaction provides the init code bigger than init code size limit.
630    #[error("max initcode size exceeded")]
631    MaxInitCodeSizeExceeded,
632    /// Represents the inability to cover max fee + value (account balance too low).
633    #[error("insufficient funds for gas * price + value: have {balance} want {cost}")]
634    InsufficientFunds {
635        /// Transaction cost.
636        cost: U256,
637        /// Current balance of transaction sender.
638        balance: U256,
639    },
640    /// This is similar to [`Self::InsufficientFunds`] but with a different error message and
641    /// exists for compatibility reasons.
642    ///
643    /// This error is used in `eth_estimateCall` when the highest available gas limit, capped with
644    /// the allowance of the caller is too low: [`Self::GasTooLow`].
645    #[error("gas required exceeds allowance ({gas_limit})")]
646    GasRequiredExceedsAllowance {
647        /// The gas limit the transaction was executed with.
648        gas_limit: u64,
649    },
650    /// Thrown when calculating gas usage
651    #[error("gas uint64 overflow")]
652    GasUintOverflow,
653    /// Thrown if the transaction is specified to use less gas than required to start the
654    /// invocation.
655    #[error("intrinsic gas too low")]
656    GasTooLow,
657    /// Thrown if the transaction gas exceeds the limit
658    #[error("intrinsic gas too high")]
659    GasTooHigh,
660    /// Thrown if the transaction gas limit exceeds the maximum
661    #[error("gas limit too high")]
662    GasLimitTooHigh,
663    /// Thrown if a transaction is not supported in the current network configuration.
664    #[error("transaction type not supported")]
665    TxTypeNotSupported,
666    /// Thrown to ensure no one is able to specify a transaction with a tip higher than the total
667    /// fee cap.
668    #[error("max priority fee per gas higher than max fee per gas")]
669    TipAboveFeeCap,
670    /// A sanity error to avoid huge numbers specified in the tip field.
671    #[error("max priority fee per gas higher than 2^256-1")]
672    TipVeryHigh,
673    /// A sanity error to avoid huge numbers specified in the fee cap field.
674    #[error("max fee per gas higher than 2^256-1")]
675    FeeCapVeryHigh,
676    /// Thrown post London if the transaction's fee is less than the base fee of the block
677    #[error("max fee per gas less than block base fee")]
678    FeeCapTooLow,
679    /// Thrown if the sender of a transaction is a contract.
680    #[error("sender is not an EOA")]
681    SenderNoEOA,
682    /// Gas limit was exceeded during execution.
683    /// Contains the gas limit.
684    #[error("out of gas: gas required exceeds: {0}")]
685    BasicOutOfGas(u64),
686    /// Gas limit was exceeded during memory expansion.
687    /// Contains the gas limit.
688    #[error("out of gas: gas exhausted during memory expansion: {0}")]
689    MemoryOutOfGas(u64),
690    /// Memory limit was exceeded during memory expansion.
691    #[error("out of memory: memory limit exceeded during memory expansion")]
692    MemoryLimitOutOfGas,
693    /// Gas limit was exceeded during precompile execution.
694    /// Contains the gas limit.
695    #[error("out of gas: gas exhausted during precompiled contract execution: {0}")]
696    PrecompileOutOfGas(u64),
697    /// An operand to an opcode was invalid or out of range.
698    /// Contains the gas limit.
699    #[error("out of gas: invalid operand to an opcode: {0}")]
700    InvalidOperandOutOfGas(u64),
701    /// Thrown if executing a transaction failed during estimate/call
702    #[error(transparent)]
703    Revert(RevertError),
704    /// Unspecific EVM halt error.
705    #[error("EVM error: {0:?}")]
706    EvmHalt(HaltReason),
707    /// Invalid chain id set for the transaction.
708    #[error("invalid chain ID")]
709    InvalidChainId,
710    /// The transaction is before Spurious Dragon and has a chain ID
711    #[error("transactions before Spurious Dragon should not have a chain ID")]
712    OldLegacyChainId,
713    /// The transaction is before Berlin and has access list
714    #[error("transactions before Berlin should not have access list")]
715    AccessListNotSupported,
716    /// `max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.
717    #[error("max_fee_per_blob_gas is not supported for blocks before the Cancun hardfork")]
718    MaxFeePerBlobGasNotSupported,
719    /// `blob_hashes`/`blob_versioned_hashes` is not supported for blocks before the Cancun
720    /// hardfork.
721    #[error("blob_versioned_hashes is not supported for blocks before the Cancun hardfork")]
722    BlobVersionedHashesNotSupported,
723    /// Block `blob_base_fee` is greater than tx-specified `max_fee_per_blob_gas` after Cancun.
724    #[error("max fee per blob gas less than block blob gas fee")]
725    BlobFeeCapTooLow,
726    /// Blob transaction has a versioned hash with an invalid blob
727    #[error("blob hash version mismatch")]
728    BlobHashVersionMismatch,
729    /// Blob transaction has no versioned hashes
730    #[error("blob transaction missing blob hashes")]
731    BlobTransactionMissingBlobHashes,
732    /// Blob transaction has too many blobs
733    #[error("blob transaction exceeds max blobs per block; got {have}")]
734    TooManyBlobs {
735        /// The number of blobs in the transaction.
736        have: usize,
737    },
738    /// Blob transaction is a create transaction
739    #[error("blob transaction is a create transaction")]
740    BlobTransactionIsCreate,
741    /// EIP-7702 is not enabled.
742    #[error("EIP-7702 authorization list not supported")]
743    AuthorizationListNotSupported,
744    /// EIP-7702 transaction has invalid fields set.
745    #[error("EIP-7702 authorization list has invalid fields")]
746    AuthorizationListInvalidFields,
747    /// Transaction priority fee is below the minimum required priority fee.
748    #[error("transaction priority fee below minimum required priority fee {minimum_priority_fee}")]
749    PriorityFeeBelowMinimum {
750        /// Minimum required priority fee.
751        minimum_priority_fee: u128,
752    },
753    /// Any other error
754    #[error("{0}")]
755    Other(Box<dyn ToRpcError>),
756}
757
758impl RpcInvalidTransactionError {
759    /// Creates a new [`RpcInvalidTransactionError::Other`] variant.
760    pub fn other<E: ToRpcError>(err: E) -> Self {
761        Self::Other(Box::new(err))
762    }
763
764    /// Returns the rpc error code for this error.
765    pub const fn error_code(&self) -> i32 {
766        match self {
767            Self::InvalidChainId |
768            Self::GasTooLow |
769            Self::GasTooHigh |
770            Self::GasRequiredExceedsAllowance { .. } |
771            Self::NonceTooLow { .. } |
772            Self::NonceTooHigh { .. } |
773            Self::FeeCapTooLow |
774            Self::FeeCapVeryHigh => EthRpcErrorCode::InvalidInput.code(),
775            Self::Revert(_) => EthRpcErrorCode::ExecutionError.code(),
776            _ => EthRpcErrorCode::TransactionRejected.code(),
777        }
778    }
779
780    /// Converts the halt error
781    ///
782    /// Takes the configured gas limit of the transaction which is attached to the error
783    pub fn halt(reason: HaltReason, gas_limit: u64) -> Self {
784        match reason {
785            HaltReason::OutOfGas(err) => Self::out_of_gas(err, gas_limit),
786            HaltReason::NonceOverflow => Self::NonceMaxValue,
787            err => Self::EvmHalt(err),
788        }
789    }
790
791    /// Converts the out of gas error
792    pub const fn out_of_gas(reason: OutOfGasError, gas_limit: u64) -> Self {
793        match reason {
794            OutOfGasError::Basic | OutOfGasError::ReentrancySentry => {
795                Self::BasicOutOfGas(gas_limit)
796            }
797            OutOfGasError::Memory => Self::MemoryOutOfGas(gas_limit),
798            OutOfGasError::MemoryLimit => Self::MemoryLimitOutOfGas,
799            OutOfGasError::Precompile => Self::PrecompileOutOfGas(gas_limit),
800            OutOfGasError::InvalidOperand => Self::InvalidOperandOutOfGas(gas_limit),
801        }
802    }
803
804    /// Converts this error into the rpc error object.
805    pub fn into_rpc_err(self) -> jsonrpsee_types::error::ErrorObject<'static> {
806        self.into()
807    }
808}
809
810impl From<RpcInvalidTransactionError> for jsonrpsee_types::error::ErrorObject<'static> {
811    fn from(err: RpcInvalidTransactionError) -> Self {
812        match err {
813            RpcInvalidTransactionError::Revert(revert) => {
814                // include out data if some
815                rpc_err(
816                    revert.error_code(),
817                    revert.to_string(),
818                    revert.output.as_ref().map(|out| out.as_ref()),
819                )
820            }
821            RpcInvalidTransactionError::Other(err) => err.to_rpc_error(),
822            err => rpc_err(err.error_code(), err.to_string(), None),
823        }
824    }
825}
826
827impl From<InvalidTransaction> for RpcInvalidTransactionError {
828    fn from(err: InvalidTransaction) -> Self {
829        match err {
830            InvalidTransaction::InvalidChainId | InvalidTransaction::MissingChainId => {
831                Self::InvalidChainId
832            }
833            InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap,
834            InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow,
835            InvalidTransaction::CallerGasLimitMoreThanBlock |
836            InvalidTransaction::TxGasLimitGreaterThanCap { .. } => {
837                // tx.gas > block.gas_limit
838                Self::GasTooHigh
839            }
840            InvalidTransaction::CallGasCostMoreThanGasLimit { .. } => {
841                // tx.gas < cost
842                Self::GasTooLow
843            }
844            InvalidTransaction::GasFloorMoreThanGasLimit { .. } => {
845                // Post prague EIP-7623 tx floor calldata gas cost > tx.gas_limit
846                // where floor gas is the minimum amount of gas that will be spent
847                // In other words, the tx's gas limit is lower that the minimum gas requirements of
848                // the tx's calldata
849                Self::GasTooLow
850            }
851            InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA,
852            InvalidTransaction::LackOfFundForMaxFee { fee, balance } => {
853                Self::InsufficientFunds { cost: *fee, balance: *balance }
854            }
855            InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow,
856            InvalidTransaction::NonceOverflowInTransaction => Self::NonceMaxValue,
857            InvalidTransaction::CreateInitCodeSizeLimit => Self::MaxInitCodeSizeExceeded,
858            InvalidTransaction::NonceTooHigh { .. } => Self::NonceTooHigh,
859            InvalidTransaction::NonceTooLow { tx, state } => Self::NonceTooLow { tx, state },
860            InvalidTransaction::AccessListNotSupported => Self::AccessListNotSupported,
861            InvalidTransaction::MaxFeePerBlobGasNotSupported => Self::MaxFeePerBlobGasNotSupported,
862            InvalidTransaction::BlobVersionedHashesNotSupported => {
863                Self::BlobVersionedHashesNotSupported
864            }
865            InvalidTransaction::BlobGasPriceGreaterThanMax { .. } => Self::BlobFeeCapTooLow,
866            InvalidTransaction::EmptyBlobs => Self::BlobTransactionMissingBlobHashes,
867            InvalidTransaction::BlobVersionNotSupported => Self::BlobHashVersionMismatch,
868            InvalidTransaction::TooManyBlobs { have, .. } => Self::TooManyBlobs { have },
869            InvalidTransaction::BlobCreateTransaction => Self::BlobTransactionIsCreate,
870            InvalidTransaction::AuthorizationListNotSupported => {
871                Self::AuthorizationListNotSupported
872            }
873            InvalidTransaction::AuthorizationListInvalidFields |
874            InvalidTransaction::EmptyAuthorizationList => Self::AuthorizationListInvalidFields,
875            InvalidTransaction::Eip2930NotSupported |
876            InvalidTransaction::Eip1559NotSupported |
877            InvalidTransaction::Eip4844NotSupported |
878            InvalidTransaction::Eip7702NotSupported |
879            InvalidTransaction::Eip7873NotSupported => Self::TxTypeNotSupported,
880            InvalidTransaction::Eip7873MissingTarget => {
881                Self::other(internal_rpc_err(err.to_string()))
882            }
883            InvalidTransaction::Str(_) => Self::other(internal_rpc_err(err.to_string())),
884        }
885    }
886}
887
888impl From<InvalidTransactionError> for RpcInvalidTransactionError {
889    fn from(err: InvalidTransactionError) -> Self {
890        use InvalidTransactionError;
891        // This conversion is used to convert any transaction errors that could occur inside the
892        // txpool (e.g. `eth_sendRawTransaction`) to their corresponding RPC
893        match err {
894            InvalidTransactionError::InsufficientFunds(res) => {
895                Self::InsufficientFunds { cost: res.expected, balance: res.got }
896            }
897            InvalidTransactionError::NonceNotConsistent { tx, state } => {
898                Self::NonceTooLow { tx, state }
899            }
900            InvalidTransactionError::OldLegacyChainId => {
901                // Note: this should be unreachable since Spurious Dragon now enabled
902                Self::OldLegacyChainId
903            }
904            InvalidTransactionError::ChainIdMismatch => Self::InvalidChainId,
905            InvalidTransactionError::Eip2930Disabled |
906            InvalidTransactionError::Eip1559Disabled |
907            InvalidTransactionError::Eip4844Disabled |
908            InvalidTransactionError::Eip7702Disabled |
909            InvalidTransactionError::TxTypeNotSupported => Self::TxTypeNotSupported,
910            InvalidTransactionError::GasUintOverflow => Self::GasUintOverflow,
911            InvalidTransactionError::GasTooLow => Self::GasTooLow,
912            InvalidTransactionError::GasTooHigh => Self::GasTooHigh,
913            InvalidTransactionError::TipAboveFeeCap => Self::TipAboveFeeCap,
914            InvalidTransactionError::FeeCapTooLow => Self::FeeCapTooLow,
915            InvalidTransactionError::SignerAccountHasBytecode => Self::SenderNoEOA,
916            InvalidTransactionError::GasLimitTooHigh => Self::GasLimitTooHigh,
917        }
918    }
919}
920
921/// Represents a reverted transaction and its output data.
922///
923/// Displays "execution reverted(: reason)?" if the reason is a string.
924#[derive(Debug, Clone, thiserror::Error)]
925pub struct RevertError {
926    /// The transaction output data
927    ///
928    /// Note: this is `None` if output was empty
929    output: Option<Bytes>,
930}
931
932// === impl RevertError ==
933
934impl RevertError {
935    /// Wraps the output bytes
936    ///
937    /// Note: this is intended to wrap a revm output
938    pub fn new(output: Bytes) -> Self {
939        if output.is_empty() {
940            Self { output: None }
941        } else {
942            Self { output: Some(output) }
943        }
944    }
945
946    /// Returns error code to return for this error.
947    pub const fn error_code(&self) -> i32 {
948        EthRpcErrorCode::ExecutionError.code()
949    }
950}
951
952impl std::fmt::Display for RevertError {
953    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
954        f.write_str("execution reverted")?;
955        if let Some(reason) = self.output.as_ref().and_then(|out| RevertReason::decode(out)) {
956            let error = reason.to_string();
957            let mut error = error.as_str();
958            if matches!(reason, RevertReason::ContractError(ContractError::Revert(_))) {
959                // we strip redundant `revert: ` prefix from the revert reason
960                error = error.trim_start_matches("revert: ");
961            }
962            write!(f, ": {error}")?;
963        }
964        Ok(())
965    }
966}
967
968/// A helper error type that's mainly used to mirror `geth` Txpool's error messages
969#[derive(Debug, thiserror::Error)]
970pub enum RpcPoolError {
971    /// When the transaction is already known
972    #[error("already known")]
973    AlreadyKnown,
974    /// When the sender is invalid
975    #[error("invalid sender")]
976    InvalidSender,
977    /// When the transaction is underpriced
978    #[error("transaction underpriced")]
979    Underpriced,
980    /// When the transaction pool is full
981    #[error("txpool is full")]
982    TxPoolOverflow,
983    /// When the replacement transaction is underpriced
984    #[error("replacement transaction underpriced")]
985    ReplaceUnderpriced,
986    /// When the transaction exceeds the block gas limit
987    #[error("exceeds block gas limit")]
988    ExceedsGasLimit,
989    /// When the transaction gas limit exceeds the maximum transaction gas limit
990    #[error("exceeds max transaction gas limit")]
991    MaxTxGasLimitExceeded,
992    /// Thrown when a new transaction is added to the pool, but then immediately discarded to
993    /// respect the tx fee exceeds the configured cap
994    #[error("tx fee ({max_tx_fee_wei} wei) exceeds the configured cap ({tx_fee_cap_wei} wei)")]
995    ExceedsFeeCap {
996        /// max fee in wei of new tx submitted to the pool (e.g. 0.11534 ETH)
997        max_tx_fee_wei: u128,
998        /// configured tx fee cap in wei (e.g. 1.0 ETH)
999        tx_fee_cap_wei: u128,
1000    },
1001    /// When a negative value is encountered
1002    #[error("negative value")]
1003    NegativeValue,
1004    /// When oversized data is encountered
1005    #[error("oversized data: transaction size {size}, limit {limit}")]
1006    OversizedData {
1007        /// Size of the transaction/input data that exceeded the limit.
1008        size: usize,
1009        /// Configured limit that was exceeded.
1010        limit: usize,
1011    },
1012    /// When the max initcode size is exceeded
1013    #[error("max initcode size exceeded")]
1014    ExceedsMaxInitCodeSize,
1015    /// Errors related to invalid transactions
1016    #[error(transparent)]
1017    Invalid(#[from] RpcInvalidTransactionError),
1018    /// Custom pool error
1019    #[error(transparent)]
1020    PoolTransactionError(Box<dyn PoolTransactionError>),
1021    /// EIP-4844 related error
1022    #[error(transparent)]
1023    Eip4844(#[from] Eip4844PoolTransactionError),
1024    /// EIP-7702 related error
1025    #[error(transparent)]
1026    Eip7702(#[from] Eip7702PoolTransactionError),
1027    /// Thrown if a conflicting transaction type is already in the pool
1028    ///
1029    /// In other words, thrown if a transaction with the same sender that violates the exclusivity
1030    /// constraint (blob vs normal tx)
1031    #[error("address already reserved")]
1032    AddressAlreadyReserved,
1033    /// Other unspecified error
1034    #[error(transparent)]
1035    Other(Box<dyn core::error::Error + Send + Sync>),
1036}
1037
1038impl From<RpcPoolError> for jsonrpsee_types::error::ErrorObject<'static> {
1039    fn from(error: RpcPoolError) -> Self {
1040        match error {
1041            RpcPoolError::Invalid(err) => err.into(),
1042            RpcPoolError::TxPoolOverflow => {
1043                rpc_error_with_code(EthRpcErrorCode::TransactionRejected.code(), error.to_string())
1044            }
1045            RpcPoolError::AlreadyKnown |
1046            RpcPoolError::InvalidSender |
1047            RpcPoolError::Underpriced |
1048            RpcPoolError::ReplaceUnderpriced |
1049            RpcPoolError::ExceedsGasLimit |
1050            RpcPoolError::MaxTxGasLimitExceeded |
1051            RpcPoolError::ExceedsFeeCap { .. } |
1052            RpcPoolError::NegativeValue |
1053            RpcPoolError::OversizedData { .. } |
1054            RpcPoolError::ExceedsMaxInitCodeSize |
1055            RpcPoolError::PoolTransactionError(_) |
1056            RpcPoolError::Eip4844(_) |
1057            RpcPoolError::Eip7702(_) |
1058            RpcPoolError::AddressAlreadyReserved => {
1059                rpc_error_with_code(EthRpcErrorCode::InvalidInput.code(), error.to_string())
1060            }
1061            RpcPoolError::Other(other) => internal_rpc_err(other.to_string()),
1062        }
1063    }
1064}
1065
1066impl From<PoolError> for RpcPoolError {
1067    fn from(err: PoolError) -> Self {
1068        match err.kind {
1069            PoolErrorKind::ReplacementUnderpriced => Self::ReplaceUnderpriced,
1070            PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(_) => Self::Underpriced,
1071            PoolErrorKind::SpammerExceededCapacity(_) | PoolErrorKind::DiscardedOnInsert => {
1072                Self::TxPoolOverflow
1073            }
1074            PoolErrorKind::InvalidTransaction(err) => err.into(),
1075            PoolErrorKind::Other(err) => Self::Other(err),
1076            PoolErrorKind::AlreadyImported => Self::AlreadyKnown,
1077            PoolErrorKind::ExistingConflictingTransactionType(_, _) => Self::AddressAlreadyReserved,
1078        }
1079    }
1080}
1081
1082impl From<InvalidPoolTransactionError> for RpcPoolError {
1083    fn from(err: InvalidPoolTransactionError) -> Self {
1084        match err {
1085            InvalidPoolTransactionError::Consensus(err) => Self::Invalid(err.into()),
1086            InvalidPoolTransactionError::ExceedsGasLimit(_, _) => Self::ExceedsGasLimit,
1087            InvalidPoolTransactionError::MaxTxGasLimitExceeded(_, _) => Self::MaxTxGasLimitExceeded,
1088            InvalidPoolTransactionError::ExceedsFeeCap { max_tx_fee_wei, tx_fee_cap_wei } => {
1089                Self::ExceedsFeeCap { max_tx_fee_wei, tx_fee_cap_wei }
1090            }
1091            InvalidPoolTransactionError::ExceedsMaxInitCodeSize(_, _) => {
1092                Self::ExceedsMaxInitCodeSize
1093            }
1094            InvalidPoolTransactionError::IntrinsicGasTooLow => {
1095                Self::Invalid(RpcInvalidTransactionError::GasTooLow)
1096            }
1097            InvalidPoolTransactionError::OversizedData { size, limit } => {
1098                Self::OversizedData { size, limit }
1099            }
1100            InvalidPoolTransactionError::Underpriced => Self::Underpriced,
1101            InvalidPoolTransactionError::Eip2681 => {
1102                Self::Invalid(RpcInvalidTransactionError::NonceMaxValue)
1103            }
1104            InvalidPoolTransactionError::Other(err) => Self::PoolTransactionError(err),
1105            InvalidPoolTransactionError::Eip4844(err) => Self::Eip4844(err),
1106            InvalidPoolTransactionError::Eip7702(err) => Self::Eip7702(err),
1107            InvalidPoolTransactionError::Overdraft { cost, balance } => {
1108                Self::Invalid(RpcInvalidTransactionError::InsufficientFunds { cost, balance })
1109            }
1110            InvalidPoolTransactionError::PriorityFeeBelowMinimum { minimum_priority_fee } => {
1111                Self::Invalid(RpcInvalidTransactionError::PriorityFeeBelowMinimum {
1112                    minimum_priority_fee,
1113                })
1114            }
1115        }
1116    }
1117}
1118
1119impl From<PoolError> for EthApiError {
1120    fn from(err: PoolError) -> Self {
1121        Self::PoolError(RpcPoolError::from(err))
1122    }
1123}
1124
1125/// Errors returned from a sign request.
1126#[derive(Debug, thiserror::Error)]
1127pub enum SignError {
1128    /// Error occurred while trying to sign data.
1129    #[error("could not sign")]
1130    CouldNotSign,
1131    /// Signer for requested account not found.
1132    #[error("unknown account")]
1133    NoAccount,
1134    /// `TypedData` has invalid format.
1135    #[error("given typed data is not valid")]
1136    InvalidTypedData,
1137    /// Invalid transaction request in `sign_transaction`.
1138    #[error("invalid transaction request")]
1139    InvalidTransactionRequest,
1140    /// No chain ID was given.
1141    #[error("no chainid")]
1142    NoChainId,
1143}
1144
1145#[cfg(test)]
1146mod tests {
1147    use super::*;
1148    use alloy_primitives::b256;
1149    use alloy_sol_types::{Revert, SolError};
1150
1151    #[test]
1152    fn timed_out_error() {
1153        let err = EthApiError::ExecutionTimedOut(Duration::from_secs(10));
1154        assert_eq!(err.to_string(), "execution aborted (timeout = 10s)");
1155    }
1156
1157    #[test]
1158    fn header_not_found_message() {
1159        let err: jsonrpsee_types::error::ErrorObject<'static> =
1160            EthApiError::HeaderNotFound(BlockId::hash(b256!(
1161                "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1162            )))
1163            .into();
1164        assert_eq!(
1165            err.message(),
1166            "block not found: hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1167        );
1168        let err: jsonrpsee_types::error::ErrorObject<'static> =
1169            EthApiError::HeaderNotFound(BlockId::hash_canonical(b256!(
1170                "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1171            )))
1172            .into();
1173        assert_eq!(
1174            err.message(),
1175            "block not found: canonical hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1176        );
1177        let err: jsonrpsee_types::error::ErrorObject<'static> =
1178            EthApiError::HeaderNotFound(BlockId::number(100000)).into();
1179        assert_eq!(err.message(), "block not found: 0x186a0");
1180        let err: jsonrpsee_types::error::ErrorObject<'static> =
1181            EthApiError::HeaderNotFound(BlockId::latest()).into();
1182        assert_eq!(err.message(), "block not found: latest");
1183        let err: jsonrpsee_types::error::ErrorObject<'static> =
1184            EthApiError::HeaderNotFound(BlockId::safe()).into();
1185        assert_eq!(err.message(), "block not found: safe");
1186        let err: jsonrpsee_types::error::ErrorObject<'static> =
1187            EthApiError::HeaderNotFound(BlockId::finalized()).into();
1188        assert_eq!(err.message(), "block not found: finalized");
1189    }
1190
1191    #[test]
1192    fn receipts_not_found_message() {
1193        let err: jsonrpsee_types::error::ErrorObject<'static> =
1194            EthApiError::ReceiptsNotFound(BlockId::hash(b256!(
1195                "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1196            )))
1197            .into();
1198        assert_eq!(
1199            err.message(),
1200            "block not found: hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1201        );
1202        let err: jsonrpsee_types::error::ErrorObject<'static> =
1203            EthApiError::ReceiptsNotFound(BlockId::hash_canonical(b256!(
1204                "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1205            )))
1206            .into();
1207        assert_eq!(
1208            err.message(),
1209            "block not found: canonical hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1210        );
1211        let err: jsonrpsee_types::error::ErrorObject<'static> =
1212            EthApiError::ReceiptsNotFound(BlockId::number(100000)).into();
1213        assert_eq!(err.code(), EthRpcErrorCode::ResourceNotFound.code());
1214        assert_eq!(err.message(), "block not found: 0x186a0");
1215        let err: jsonrpsee_types::error::ErrorObject<'static> =
1216            EthApiError::ReceiptsNotFound(BlockId::latest()).into();
1217        assert_eq!(err.message(), "block not found: latest");
1218        let err: jsonrpsee_types::error::ErrorObject<'static> =
1219            EthApiError::ReceiptsNotFound(BlockId::safe()).into();
1220        assert_eq!(err.message(), "block not found: safe");
1221        let err: jsonrpsee_types::error::ErrorObject<'static> =
1222            EthApiError::ReceiptsNotFound(BlockId::finalized()).into();
1223        assert_eq!(err.message(), "block not found: finalized");
1224        let err: jsonrpsee_types::error::ErrorObject<'static> =
1225            EthApiError::ReceiptsNotFound(BlockId::pending()).into();
1226        assert_eq!(err.message(), "block not found: pending");
1227        let err: jsonrpsee_types::error::ErrorObject<'static> =
1228            EthApiError::ReceiptsNotFound(BlockId::earliest()).into();
1229        assert_eq!(err.message(), "block not found: earliest");
1230    }
1231
1232    #[test]
1233    fn revert_err_display() {
1234        let revert = Revert::from("test_revert_reason");
1235        let err = RevertError::new(revert.abi_encode().into());
1236        let msg = err.to_string();
1237        assert_eq!(msg, "execution reverted: test_revert_reason");
1238    }
1239}