Skip to main content

anvil/eth/error/
mod.rs

1//! Aggregated error type for this module
2
3use alloy_consensus::crypto::RecoveryError;
4use alloy_evm::overrides::StateOverrideError;
5use alloy_primitives::{B256, Bytes, SignatureError, TxHash, U256};
6use alloy_rpc_types::BlockNumberOrTag;
7use alloy_signer::Error as SignerError;
8use alloy_transport::TransportError;
9use anvil_core::eth::wallet::WalletError;
10use anvil_rpc::{
11    error::{ErrorCode, RpcError},
12    response::ResponseResult,
13};
14use foundry_evm::{backend::DatabaseError, decode::RevertDecoder};
15use revm::{
16    context_interface::result::{EVMError, InvalidHeader, InvalidTransaction},
17    interpreter::InstructionResult,
18};
19use serde::Serialize;
20use tempo_revm::TempoInvalidTransaction;
21use tokio::time::Duration;
22
23#[cfg(feature = "base")]
24mod base;
25#[cfg(feature = "optimism")]
26mod optimism;
27
28pub(crate) type Result<T> = std::result::Result<T, BlockchainError>;
29
30#[derive(Debug, thiserror::Error)]
31pub enum BlockchainError {
32    #[error(transparent)]
33    Pool(#[from] PoolError),
34    #[error("No signer available")]
35    NoSignerAvailable,
36    #[error("Chain Id not available")]
37    ChainIdNotAvailable,
38    #[error("Invalid input: `max_priority_fee_per_gas` greater than `max_fee_per_gas`")]
39    InvalidFeeInput,
40    #[error("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")]
41    ConflictingFeeFields,
42    #[error("Transaction data is empty")]
43    EmptyRawTransactionData,
44    #[error("Failed to decode signed transaction")]
45    FailedToDecodeSignedTransaction,
46    #[error("Failed to decode transaction")]
47    FailedToDecodeTransaction,
48    #[error("Failed to decode receipt")]
49    FailedToDecodeReceipt,
50    #[error("Cannot EIP-2718 encode transaction type 0x{0:x}")]
51    UnsupportedTransactionEncoding(u8),
52    #[error("Failed to decode state")]
53    FailedToDecodeStateDump,
54    #[error("Prevrandao not in the EVM's environment after merge")]
55    PrevrandaoNotSet,
56    #[error(transparent)]
57    SignatureError(#[from] SignatureError),
58    #[error(transparent)]
59    RecoveryError(#[from] RecoveryError),
60    #[error(transparent)]
61    SignerError(#[from] SignerError),
62    #[error("Rpc Endpoint not implemented")]
63    RpcUnimplemented,
64    #[error("Rpc error {0:?}")]
65    RpcError(RpcError),
66    #[error(transparent)]
67    InvalidTransaction(#[from] InvalidTransactionError),
68    #[error(transparent)]
69    FeeHistory(#[from] FeeHistoryError),
70    #[error(transparent)]
71    AlloyForkProvider(#[from] TransportError),
72    #[error("EVM error {0:?}")]
73    EvmError(InstructionResult),
74    #[error("Evm override error: {0}")]
75    EvmOverrideError(String),
76    #[error("Invalid url {0:?}")]
77    InvalidUrl(String),
78    #[error("unsupported fork network for chain {chain_id}: {reason}")]
79    UnsupportedForkNetwork { chain_id: u64, reason: &'static str },
80    #[error("Internal error: {0:?}")]
81    Internal(String),
82    #[error("BlockOutOfRangeError: block height is {0} but requested was {1}")]
83    BlockOutOfRange(u64, u64),
84    #[error("Resource not found")]
85    BlockNotFound,
86    #[error("unknown block")]
87    UnknownBlock,
88    /// Thrown when a requested transaction is not found
89    #[error("transaction not found")]
90    TransactionNotFound,
91    #[error("Required data unavailable")]
92    DataUnavailable,
93    /// Thrown when replaying a mined block requires a parent state that is no longer stored.
94    #[error("historical state needed to replay block {0} is not available")]
95    HistoricalStateUnavailable(u64),
96    #[error("Trie error: {0}")]
97    TrieError(String),
98    #[error("{0}")]
99    UintConversion(&'static str),
100    #[error("State override error: {0}")]
101    StateOverrideError(String),
102    #[error("Timestamp error: {0}")]
103    TimestampError(String),
104    #[error(transparent)]
105    DatabaseError(#[from] DatabaseError),
106    #[error(
107        "EIP-1559 style fee params (maxFeePerGas or maxPriorityFeePerGas) received but they are not supported by the current hardfork.\n\nYou can use them by running anvil with '--hardfork london' or later."
108    )]
109    EIP1559TransactionUnsupportedAtHardfork,
110    #[error(
111        "Access list received but is not supported by the current hardfork.\n\nYou can use it by running anvil with '--hardfork berlin' or later."
112    )]
113    EIP2930TransactionUnsupportedAtHardfork,
114    #[error(
115        "EIP-4844 fields received but is not supported by the current hardfork.\n\nYou can use it by running anvil with '--hardfork cancun' or later."
116    )]
117    EIP4844TransactionUnsupportedAtHardfork,
118    #[error(
119        "EIP-7702 fields received but is not supported by the current hardfork.\n\nYou can use it by running anvil with '--hardfork prague' or later."
120    )]
121    EIP7702TransactionUnsupportedAtHardfork,
122    // Base is an OP-stack chain and uses the same deposit envelope, so only the hint that names
123    // the flags able to enable it varies with the compiled-in families.
124    #[cfg_attr(
125        all(feature = "base", feature = "optimism"),
126        error(
127            "op-stack deposit tx received but is not supported.\n\nYou can use it by running anvil with '--optimism' or '--network base'."
128        )
129    )]
130    #[cfg_attr(
131        all(feature = "base", not(feature = "optimism")),
132        error(
133            "op-stack deposit tx received but is not supported.\n\nYou can use it by running anvil with '--network base'."
134        )
135    )]
136    #[cfg_attr(
137        not(feature = "base"),
138        error(
139            "op-stack deposit tx received but is not supported.\n\nYou can use it by running anvil with '--optimism'."
140        )
141    )]
142    DepositTransactionUnsupported,
143    #[error(
144        "tempo transaction received but is not supported.\n\nYou can use it by running anvil with '--tempo'."
145    )]
146    TempoTransactionUnsupported,
147    #[cfg(feature = "base")]
148    #[error("Base transactions require native Base execution; run Anvil with --network base")]
149    BaseTransactionUnsupported,
150    #[error("Unknown transaction type not supported")]
151    UnknownTransactionType,
152    #[error("Excess blob gas not set.")]
153    ExcessBlobGasNotSet,
154    #[error("{0}")]
155    Message(String),
156    #[error("Transaction {hash} was added to the mempool but wasn't confirmed within {duration:?}")]
157    TransactionConfirmationTimeout {
158        /// Hash of the transaction that timed out
159        hash: B256,
160        /// Duration that was waited before timing out
161        duration: Duration,
162    },
163    #[error("Invalid transaction request: {0}")]
164    InvalidTransactionRequest(String),
165    #[cfg(feature = "base")]
166    #[error("EIP-8130 transaction rejected: {0}")]
167    Eip8130TransactionRejected(String),
168    #[error("filter not found")]
169    FilterNotFound,
170}
171
172impl From<eyre::Report> for BlockchainError {
173    fn from(err: eyre::Report) -> Self {
174        Self::Message(err.to_string())
175    }
176}
177
178impl From<RpcError> for BlockchainError {
179    fn from(err: RpcError) -> Self {
180        Self::RpcError(err)
181    }
182}
183
184impl<T> From<EVMError<T>> for BlockchainError
185where
186    T: Into<Self>,
187{
188    fn from(err: EVMError<T>) -> Self {
189        match err {
190            EVMError::Transaction(err) => InvalidTransactionError::from(err).into(),
191            EVMError::Header(err) => match err {
192                InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet,
193                InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet,
194            },
195            EVMError::Database(err) => err.into(),
196            EVMError::Custom(err) => Self::Message(err),
197            EVMError::CustomAny(err) => Self::Message(err.to_string()),
198        }
199    }
200}
201
202impl<T> From<EVMError<T, TempoInvalidTransaction>> for BlockchainError
203where
204    T: Into<Self>,
205{
206    fn from(err: EVMError<T, TempoInvalidTransaction>) -> Self {
207        match err {
208            EVMError::Transaction(err) => match err {
209                TempoInvalidTransaction::EthInvalidTransaction(err) => {
210                    InvalidTransactionError::from(err).into()
211                }
212                err => Self::Message(format!("tempo transaction error: {err}")),
213            },
214            EVMError::Header(err) => match err {
215                InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet,
216                InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet,
217            },
218            EVMError::Database(err) => err.into(),
219            EVMError::Custom(err) => Self::Message(err),
220            EVMError::CustomAny(err) => Self::Message(err.to_string()),
221        }
222    }
223}
224
225impl From<WalletError> for BlockchainError {
226    fn from(value: WalletError) -> Self {
227        Self::Message(value.to_string())
228    }
229}
230
231impl<E> From<StateOverrideError<E>> for BlockchainError
232where
233    E: Into<Self>,
234{
235    fn from(value: StateOverrideError<E>) -> Self {
236        match value {
237            StateOverrideError::InvalidBytecode(err) => Self::StateOverrideError(err.to_string()),
238            StateOverrideError::BothStateAndStateDiff(addr) => Self::StateOverrideError(format!(
239                "state and state_diff can't be used together for account {addr}",
240            )),
241            StateOverrideError::Database(err) => err.into(),
242        }
243    }
244}
245
246/// Errors that can occur in the transaction pool
247#[derive(Debug, thiserror::Error)]
248pub enum PoolError {
249    #[error("Transaction with cyclic dependent transactions")]
250    CyclicTransaction,
251    /// Thrown if a replacement transaction's gas price is below the already imported transaction
252    #[error("Tx: [{0:?}] insufficient gas price to replace existing transaction")]
253    ReplacementUnderpriced(TxHash),
254    #[error("Tx: [{0:?}] already Imported")]
255    AlreadyImported(TxHash),
256}
257
258/// Errors that can occur with `eth_feeHistory`
259#[derive(Debug, thiserror::Error)]
260pub enum FeeHistoryError {
261    #[error("requested block range is out of bounds")]
262    InvalidBlockRange,
263    #[error("reward percentiles must be strictly increasing and between 0 and 100")]
264    InvalidRewardPercentiles,
265    #[error("could not find block number requested: {0}")]
266    BlockNotFound(BlockNumberOrTag),
267}
268
269#[derive(Debug)]
270pub struct ErrDetail {
271    pub detail: String,
272}
273
274/// An error due to invalid transaction
275#[derive(Debug, thiserror::Error)]
276pub enum InvalidTransactionError {
277    /// returned if the nonce of a transaction is lower than the one present in the local chain.
278    #[error("nonce too low")]
279    NonceTooLow,
280    /// returned if the nonce of a transaction is higher than the next one expected based on the
281    /// local chain.
282    #[error("Nonce too high")]
283    NonceTooHigh,
284    /// Returned if the nonce of a transaction is too high
285    /// Incrementing the nonce would lead to invalid state (overflow)
286    #[error("nonce has max value")]
287    NonceMaxValue,
288    /// thrown if the transaction sender doesn't have enough funds for a transfer
289    #[error("insufficient funds for transfer")]
290    InsufficientFundsForTransfer,
291    /// thrown if creation transaction provides the init code bigger than init code size limit.
292    #[error("max initcode size exceeded")]
293    MaxInitCodeSizeExceeded,
294    /// Represents the inability to cover max cost + value (account balance too low).
295    #[error("Insufficient funds for gas * price + value")]
296    InsufficientFunds,
297    /// Thrown when calculating gas usage
298    #[error("gas uint64 overflow")]
299    GasUintOverflow,
300    /// returned if the transaction is specified to use less gas than required to start the
301    /// invocation.
302    #[error("intrinsic gas too low")]
303    GasTooLow,
304    /// returned if the transaction gas exceeds the limit
305    #[error("intrinsic gas too high -- {}",.0.detail)]
306    GasTooHigh(ErrDetail),
307    /// Thrown to ensure no one is able to specify a transaction with a tip higher than the total
308    /// fee cap.
309    #[error("max priority fee per gas higher than max fee per gas")]
310    TipAboveFeeCap,
311    /// Thrown post London if the transaction's fee is less than the base fee of the block
312    #[error("max fee per gas less than block base fee")]
313    FeeCapTooLow,
314    /// Thrown during estimate if caller has insufficient funds to cover the tx.
315    #[error("Out of gas: gas required exceeds allowance: {0:?}")]
316    BasicOutOfGas(u128),
317    /// Thrown if executing a transaction failed during estimate/call
318    #[error("execution reverted: {0:?}")]
319    Revert(Option<Bytes>),
320    /// Thrown if the sender of a transaction is a contract.
321    #[error("sender not an eoa")]
322    SenderNoEOA,
323    /// Thrown when a tx was signed with a different chain_id
324    #[error("invalid chain id for signer")]
325    InvalidChainId,
326    /// Thrown when a legacy tx was signed for a different chain
327    #[error("Incompatible EIP-155 transaction, signed for another chain")]
328    IncompatibleEIP155,
329    /// Thrown when an access list is used before the berlin hard fork.
330    #[error("Access lists are not supported before the Berlin hardfork")]
331    AccessListNotSupported,
332    /// Thrown when the block's `blob_gas_price` is greater than tx-specified
333    /// `max_fee_per_blob_gas` after Cancun.
334    #[error("Block `blob_gas_price` is greater than tx-specified `max_fee_per_blob_gas`")]
335    BlobFeeCapTooLow(u128, u128),
336    /// Thrown when we receive a tx with `blob_versioned_hashes` and we're not on the Cancun hard
337    /// fork.
338    #[error("Block `blob_versioned_hashes` is not supported before the Cancun hardfork")]
339    BlobVersionedHashesNotSupported,
340    /// Thrown when `max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.
341    #[error("`max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.")]
342    MaxFeePerBlobGasNotSupported,
343    /// Thrown when there are no `blob_hashes` in the transaction, and it is an EIP-4844 tx.
344    #[error("`blob_hashes` are required for EIP-4844 transactions")]
345    NoBlobHashes,
346    #[error("too many blobs in one transaction, have: {0}, max: {1}")]
347    TooManyBlobs(usize, usize),
348    /// Thrown when there's a blob validation error
349    #[error(transparent)]
350    BlobTransactionValidationError(#[from] alloy_consensus::BlobTransactionValidationError),
351    /// Thrown when Blob transaction is a create transaction. `to` must be present.
352    #[error("Blob transaction can't be a create transaction. `to` must be present.")]
353    BlobCreateTransaction,
354    /// Thrown when Blob transaction contains a versioned hash with an incorrect version.
355    #[error("Blob transaction contains a versioned hash with an incorrect version")]
356    BlobVersionNotSupported,
357    /// Thrown when a blob transaction is submitted on a Monad network.
358    #[error("EIP-4844 blob transactions are not supported on Monad")]
359    MonadBlobTransactionUnsupported,
360    /// Thrown when there are no `blob_hashes` in the transaction.
361    #[error("There should be at least one blob in a Blob transaction.")]
362    EmptyBlobs,
363    /// Thrown when an access list is used before the berlin hard fork.
364    #[error("EIP-7702 authorization lists are not supported before the Prague hardfork")]
365    AuthorizationListNotSupported,
366    #[error("Transaction gas limit is greater than the block gas limit, gas_limit: {0}, cap: {1}")]
367    TxGasLimitGreaterThanCap(u64, u64),
368    /// Forwards error from the revm
369    #[error(transparent)]
370    Revm(revm::context_interface::result::InvalidTransaction),
371    /// Deposit transaction error post regolith
372    #[error("op-deposit failure post regolith")]
373    DepositTxErrorPostRegolith,
374    /// Missing enveloped transaction
375    #[error("missing enveloped transaction")]
376    MissingEnvelopedTx,
377    /// EIP-8130 transaction failed block-inclusion validation.
378    #[cfg(feature = "base")]
379    #[error("EIP-8130 transaction rejected: {0}")]
380    Eip8130(String),
381    /// Native ETH value transfers are not allowed in Tempo mode
382    #[error("native value transfer not allowed in Tempo mode")]
383    TempoNativeValueTransfer,
384    /// Tempo transaction valid_before is expired or too close to current time
385    #[error("Tempo tx valid_before ({valid_before}) must be > current time + 3s ({min_allowed})")]
386    TempoValidBeforeExpired { valid_before: u64, min_allowed: u64 },
387    /// Tempo expiring nonce transaction valid_before is too far in the future.
388    #[error(
389        "Tempo expiring nonce tx valid_before ({valid_before}) must be <= current time + {max_expiry_secs}s ({max_allowed})"
390    )]
391    TempoValidBeforeTooFar { valid_before: u64, max_expiry_secs: u64, max_allowed: u64 },
392    /// Tempo transaction valid_after is too far in the future
393    #[error(
394        "Tempo tx valid_after ({valid_after}) must be <= current time + {max_valid_after_secs}s ({max_allowed})"
395    )]
396    TempoValidAfterTooFar { valid_after: u64, max_valid_after_secs: u64, max_allowed: u64 },
397    /// Tempo transaction has too many authorizations
398    #[error("Tempo tx has too many authorizations ({count}), max allowed is {max}")]
399    TempoTooManyAuthorizations { count: usize, max: usize },
400    /// Tempo transaction fee payer has insufficient fee token balance
401    #[error("insufficient fee token balance: have {balance}, need {required}")]
402    TempoInsufficientFeeTokenBalance { balance: U256, required: U256 },
403}
404
405impl From<InvalidTransaction> for InvalidTransactionError {
406    fn from(err: InvalidTransaction) -> Self {
407        match err {
408            InvalidTransaction::InvalidChainId => Self::InvalidChainId,
409            InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap,
410            InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow,
411            InvalidTransaction::CallerGasLimitMoreThanBlock => {
412                Self::GasTooHigh(ErrDetail { detail: String::from("CallerGasLimitMoreThanBlock") })
413            }
414            InvalidTransaction::CallGasCostMoreThanGasLimit { .. } => {
415                Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") })
416            }
417            InvalidTransaction::GasFloorMoreThanGasLimit { .. } => {
418                Self::GasTooHigh(ErrDetail { detail: String::from("GasFloorMoreThanGasLimit") })
419            }
420            InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA,
421            InvalidTransaction::LackOfFundForMaxFee { .. } => Self::InsufficientFunds,
422            InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow,
423            InvalidTransaction::NonceOverflowInTransaction => Self::NonceMaxValue,
424            InvalidTransaction::CreateInitCodeSizeLimit => Self::MaxInitCodeSizeExceeded,
425            InvalidTransaction::NonceTooHigh { .. } => Self::NonceTooHigh,
426            InvalidTransaction::NonceTooLow { .. } => Self::NonceTooLow,
427            InvalidTransaction::AccessListNotSupported => Self::AccessListNotSupported,
428            InvalidTransaction::BlobGasPriceGreaterThanMax {
429                block_blob_gas_price,
430                tx_max_fee_per_blob_gas,
431            } => Self::BlobFeeCapTooLow(block_blob_gas_price, tx_max_fee_per_blob_gas),
432            InvalidTransaction::BlobVersionedHashesNotSupported => {
433                Self::BlobVersionedHashesNotSupported
434            }
435            InvalidTransaction::MaxFeePerBlobGasNotSupported => Self::MaxFeePerBlobGasNotSupported,
436            InvalidTransaction::BlobCreateTransaction => Self::BlobCreateTransaction,
437            InvalidTransaction::BlobVersionNotSupported => Self::BlobVersionNotSupported,
438            InvalidTransaction::EmptyBlobs => Self::EmptyBlobs,
439            InvalidTransaction::TooManyBlobs { have, max } => Self::TooManyBlobs(have, max),
440            InvalidTransaction::AuthorizationListNotSupported => {
441                Self::AuthorizationListNotSupported
442            }
443            InvalidTransaction::TxGasLimitGreaterThanCap { gas_limit, cap } => {
444                Self::TxGasLimitGreaterThanCap(gas_limit, cap)
445            }
446
447            InvalidTransaction::AuthorizationListInvalidFields
448            | InvalidTransaction::Eip1559NotSupported
449            | InvalidTransaction::Eip2930NotSupported
450            | InvalidTransaction::Eip4844NotSupported
451            | InvalidTransaction::Eip7702NotSupported
452            | InvalidTransaction::EmptyAuthorizationList
453            | InvalidTransaction::Eip7873NotSupported
454            | InvalidTransaction::Eip7873MissingTarget
455            | InvalidTransaction::MissingChainId
456            | InvalidTransaction::Str(_) => Self::Revm(err),
457        }
458    }
459}
460
461/// Helper trait to easily convert results to rpc results
462pub(crate) trait ToRpcResponseResult {
463    fn to_rpc_result(self) -> ResponseResult;
464}
465
466/// Converts a serializable value into a `ResponseResult`
467pub fn to_rpc_result<T: Serialize>(val: T) -> ResponseResult {
468    match serde_json::to_value(val) {
469        Ok(success) => ResponseResult::Success(success),
470        Err(err) => {
471            error!(%err, "Failed serialize rpc response");
472            ResponseResult::error(RpcError::internal_error())
473        }
474    }
475}
476
477impl<T: Serialize> ToRpcResponseResult for Result<T> {
478    fn to_rpc_result(self) -> ResponseResult {
479        match self {
480            Ok(val) => to_rpc_result(val),
481            Err(err) => match err {
482                BlockchainError::Pool(err) => {
483                    error!(%err, "txpool error");
484                    match err {
485                        PoolError::CyclicTransaction => {
486                            RpcError::transaction_rejected("Cyclic transaction detected")
487                        }
488                        PoolError::ReplacementUnderpriced(_) => {
489                            RpcError::transaction_rejected("replacement transaction underpriced")
490                        }
491                        PoolError::AlreadyImported(_) => {
492                            RpcError::transaction_rejected("transaction already imported")
493                        }
494                    }
495                }
496                BlockchainError::NoSignerAvailable => {
497                    RpcError::invalid_params("No Signer available")
498                }
499                BlockchainError::ChainIdNotAvailable => {
500                    RpcError::invalid_params("Chain Id not available")
501                }
502                BlockchainError::TransactionConfirmationTimeout { hash, .. } => RpcError {
503                    code: ErrorCode::ServerError(4),
504                    message: "Transaction confirmation timeout".into(),
505                    data: Some(serde_json::Value::String(hash.to_string())),
506                },
507                BlockchainError::InvalidTransaction(err) => match err {
508                    InvalidTransactionError::Revert(data) => {
509                        // this mimics geth revert error
510                        let mut msg = "execution reverted".to_string();
511                        if let Some(reason) = data
512                            .as_ref()
513                            .and_then(|data| RevertDecoder::new().maybe_decode(data, None))
514                        {
515                            msg = format!("{msg}: {reason}");
516                        }
517                        RpcError {
518                            // geth returns this error code on reverts, See <https://eips.ethereum.org/EIPS/eip-1474#specification>
519                            code: ErrorCode::ExecutionError,
520                            message: msg.into(),
521                            data: serde_json::to_value(data).ok(),
522                        }
523                    }
524                    InvalidTransactionError::GasTooLow => {
525                        // <https://eips.ethereum.org/EIPS/eip-1898>
526                        RpcError {
527                            code: ErrorCode::ServerError(-32000),
528                            message: err.to_string().into(),
529                            data: None,
530                        }
531                    }
532                    InvalidTransactionError::GasTooHigh(_) => {
533                        // <https://eips.ethereum.org/EIPS/eip-1898>
534                        RpcError {
535                            code: ErrorCode::ServerError(-32000),
536                            message: err.to_string().into(),
537                            data: None,
538                        }
539                    }
540                    _ => RpcError::transaction_rejected(err.to_string()),
541                },
542                BlockchainError::FeeHistory(err) => RpcError::invalid_params(err.to_string()),
543                BlockchainError::EmptyRawTransactionData => {
544                    RpcError::invalid_params("Empty transaction data")
545                }
546                BlockchainError::FailedToDecodeSignedTransaction => {
547                    RpcError::invalid_params("Failed to decode transaction")
548                }
549                BlockchainError::FailedToDecodeTransaction => {
550                    RpcError::invalid_params("Failed to decode transaction")
551                }
552                BlockchainError::FailedToDecodeReceipt => {
553                    RpcError::invalid_params("Failed to decode receipt")
554                }
555                BlockchainError::UnsupportedTransactionEncoding(_) => {
556                    RpcError::internal_error_with(err.to_string())
557                }
558                BlockchainError::FailedToDecodeStateDump => {
559                    RpcError::invalid_params("Failed to decode state dump")
560                }
561                BlockchainError::SignerError(err) => RpcError::invalid_params(err.to_string()),
562                BlockchainError::SignatureError(err) => RpcError::invalid_params(err.to_string()),
563                BlockchainError::RpcUnimplemented => {
564                    RpcError::internal_error_with("Not implemented")
565                }
566                BlockchainError::PrevrandaoNotSet => RpcError::internal_error_with(err.to_string()),
567                BlockchainError::RpcError(err) => err,
568                BlockchainError::InvalidFeeInput => RpcError::invalid_params(
569                    "Invalid input: `max_priority_fee_per_gas` greater than `max_fee_per_gas`",
570                ),
571                err @ BlockchainError::ConflictingFeeFields => {
572                    RpcError::invalid_params(err.to_string())
573                }
574                BlockchainError::AlloyForkProvider(err) => {
575                    error!(target: "backend", %err, "fork provider error");
576                    match err {
577                        TransportError::ErrorResp(err) => RpcError {
578                            code: ErrorCode::from(err.code),
579                            message: err.message,
580                            data: err.data.and_then(|data| serde_json::to_value(data).ok()),
581                        },
582                        err => RpcError::internal_error_with(format!("Fork Error: {err:?}")),
583                    }
584                }
585                err @ BlockchainError::EvmError(_) => RpcError {
586                    // VM halts are execution failures, not JSON-RPC server faults. REVERT has a
587                    // dedicated code/data path above; other halts, such as invalid opcode, do not.
588                    code: ErrorCode::TransactionRejected,
589                    message: err.to_string().into(),
590                    data: None,
591                },
592                #[cfg(feature = "base")]
593                err @ BlockchainError::Eip8130TransactionRejected(_) => RpcError {
594                    code: ErrorCode::TransactionRejected,
595                    message: err.to_string().into(),
596                    data: None,
597                },
598                err @ BlockchainError::EvmOverrideError(_) => {
599                    RpcError::invalid_params(err.to_string())
600                }
601                err @ BlockchainError::InvalidUrl(_) => RpcError::invalid_params(err.to_string()),
602                err @ BlockchainError::UnsupportedForkNetwork { .. } => {
603                    RpcError::invalid_params(err.to_string())
604                }
605                BlockchainError::Internal(err) => RpcError::internal_error_with(err),
606                err @ BlockchainError::BlockOutOfRange(_, _) => {
607                    RpcError::invalid_params(err.to_string())
608                }
609                err @ BlockchainError::BlockNotFound => RpcError {
610                    // <https://eips.ethereum.org/EIPS/eip-1898>
611                    code: ErrorCode::ServerError(-32001),
612                    message: err.to_string().into(),
613                    data: None,
614                },
615                err @ BlockchainError::TransactionNotFound => RpcError {
616                    code: ErrorCode::ServerError(-32001),
617                    message: err.to_string().into(),
618                    data: None,
619                },
620                err @ BlockchainError::DataUnavailable => {
621                    RpcError::internal_error_with(err.to_string())
622                }
623                err @ BlockchainError::HistoricalStateUnavailable(_) => RpcError {
624                    code: ErrorCode::ServerError(-32000),
625                    message: err.to_string().into(),
626                    data: None,
627                },
628                err @ BlockchainError::TrieError(_) => {
629                    RpcError::internal_error_with(err.to_string())
630                }
631                BlockchainError::UintConversion(err) => RpcError::invalid_params(err),
632                err @ BlockchainError::StateOverrideError(_) => {
633                    RpcError::invalid_params(err.to_string())
634                }
635                err @ BlockchainError::TimestampError(_) => {
636                    RpcError::invalid_params(err.to_string())
637                }
638                BlockchainError::DatabaseError(err) => {
639                    RpcError::internal_error_with(err.to_string())
640                }
641                err @ BlockchainError::EIP1559TransactionUnsupportedAtHardfork => {
642                    RpcError::invalid_params(err.to_string())
643                }
644                err @ BlockchainError::EIP2930TransactionUnsupportedAtHardfork => {
645                    RpcError::invalid_params(err.to_string())
646                }
647                err @ BlockchainError::EIP4844TransactionUnsupportedAtHardfork => {
648                    RpcError::invalid_params(err.to_string())
649                }
650                err @ BlockchainError::EIP7702TransactionUnsupportedAtHardfork => {
651                    RpcError::invalid_params(err.to_string())
652                }
653                err @ BlockchainError::DepositTransactionUnsupported => {
654                    RpcError::invalid_params(err.to_string())
655                }
656                #[cfg(feature = "base")]
657                err @ BlockchainError::BaseTransactionUnsupported => {
658                    RpcError::invalid_params(err.to_string())
659                }
660                err @ BlockchainError::TempoTransactionUnsupported => {
661                    RpcError::invalid_params(err.to_string())
662                }
663                err @ BlockchainError::ExcessBlobGasNotSet => {
664                    RpcError::invalid_params(err.to_string())
665                }
666                err @ BlockchainError::Message(_) => RpcError::internal_error_with(err.to_string()),
667                err @ BlockchainError::UnknownTransactionType => {
668                    RpcError::invalid_params(err.to_string())
669                }
670                err @ BlockchainError::InvalidTransactionRequest(_) => {
671                    RpcError::invalid_params(err.to_string())
672                }
673                err @ BlockchainError::RecoveryError(_) => {
674                    RpcError::invalid_params(err.to_string())
675                }
676                BlockchainError::FilterNotFound => RpcError {
677                    code: ErrorCode::ServerError(-32000),
678                    message: "filter not found".into(),
679                    data: None,
680                },
681                err @ BlockchainError::UnknownBlock => RpcError {
682                    code: ErrorCode::ServerError(-32000),
683                    message: err.to_string().into(),
684                    data: None,
685                },
686            }
687            .into(),
688        }
689    }
690}