Skip to main content

anvil/eth/
error.rs

1//! Aggregated error type for this module
2
3use crate::eth::pool::transactions::PoolTransaction;
4use alloy_evm::overrides::StateOverrideError;
5use alloy_primitives::{B256, Bytes, SignatureError};
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 op_revm::OpTransactionError;
16use revm::{
17    context_interface::result::{EVMError, InvalidHeader, InvalidTransaction},
18    interpreter::InstructionResult,
19};
20use serde::Serialize;
21use tokio::time::Duration;
22
23pub(crate) type Result<T> = std::result::Result<T, BlockchainError>;
24
25#[derive(Debug, thiserror::Error)]
26pub enum BlockchainError {
27    #[error(transparent)]
28    Pool(#[from] PoolError),
29    #[error("No signer available")]
30    NoSignerAvailable,
31    #[error("Chain Id not available")]
32    ChainIdNotAvailable,
33    #[error("Invalid input: `max_priority_fee_per_gas` greater than `max_fee_per_gas`")]
34    InvalidFeeInput,
35    #[error("Transaction data is empty")]
36    EmptyRawTransactionData,
37    #[error("Failed to decode signed transaction")]
38    FailedToDecodeSignedTransaction,
39    #[error("Failed to decode transaction")]
40    FailedToDecodeTransaction,
41    #[error("Failed to decode receipt")]
42    FailedToDecodeReceipt,
43    #[error("Failed to decode state")]
44    FailedToDecodeStateDump,
45    #[error("Prevrandao not in th EVM's environment after merge")]
46    PrevrandaoNotSet,
47    #[error(transparent)]
48    SignatureError(#[from] SignatureError),
49    #[error(transparent)]
50    SignerError(#[from] SignerError),
51    #[error("Rpc Endpoint not implemented")]
52    RpcUnimplemented,
53    #[error("Rpc error {0:?}")]
54    RpcError(RpcError),
55    #[error(transparent)]
56    InvalidTransaction(#[from] InvalidTransactionError),
57    #[error(transparent)]
58    FeeHistory(#[from] FeeHistoryError),
59    #[error(transparent)]
60    AlloyForkProvider(#[from] TransportError),
61    #[error("EVM error {0:?}")]
62    EvmError(InstructionResult),
63    #[error("Evm override error: {0}")]
64    EvmOverrideError(String),
65    #[error("Invalid url {0:?}")]
66    InvalidUrl(String),
67    #[error("Internal error: {0:?}")]
68    Internal(String),
69    #[error("BlockOutOfRangeError: block height is {0} but requested was {1}")]
70    BlockOutOfRange(u64, u64),
71    #[error("Resource not found")]
72    BlockNotFound,
73    #[error("Required data unavailable")]
74    DataUnavailable,
75    #[error("Trie error: {0}")]
76    TrieError(String),
77    #[error("{0}")]
78    UintConversion(&'static str),
79    #[error("State override error: {0}")]
80    StateOverrideError(String),
81    #[error("Timestamp error: {0}")]
82    TimestampError(String),
83    #[error(transparent)]
84    DatabaseError(#[from] DatabaseError),
85    #[error(
86        "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."
87    )]
88    EIP1559TransactionUnsupportedAtHardfork,
89    #[error(
90        "Access list received but is not supported by the current hardfork.\n\nYou can use it by running anvil with '--hardfork berlin' or later."
91    )]
92    EIP2930TransactionUnsupportedAtHardfork,
93    #[error(
94        "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."
95    )]
96    EIP4844TransactionUnsupportedAtHardfork,
97    #[error(
98        "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."
99    )]
100    EIP7702TransactionUnsupportedAtHardfork,
101    #[error(
102        "op-stack deposit tx received but is not supported.\n\nYou can use it by running anvil with '--optimism'."
103    )]
104    DepositTransactionUnsupported,
105    #[error("UnknownTransactionType not supported ")]
106    UnknownTransactionType,
107    #[error("Excess blob gas not set.")]
108    ExcessBlobGasNotSet,
109    #[error("{0}")]
110    Message(String),
111    #[error("Transaction {hash} was added to the mempool but wasn't confirmed within {duration:?}")]
112    TransactionConfirmationTimeout {
113        /// Hash of the transaction that timed out
114        hash: B256,
115        /// Duration that was waited before timing out
116        duration: Duration,
117    },
118    #[error("Failed to parse transaction request: missing required fields")]
119    MissingRequiredFields,
120}
121
122impl From<eyre::Report> for BlockchainError {
123    fn from(err: eyre::Report) -> Self {
124        Self::Message(err.to_string())
125    }
126}
127
128impl From<RpcError> for BlockchainError {
129    fn from(err: RpcError) -> Self {
130        Self::RpcError(err)
131    }
132}
133
134impl<T> From<EVMError<T>> for BlockchainError
135where
136    T: Into<Self>,
137{
138    fn from(err: EVMError<T>) -> Self {
139        match err {
140            EVMError::Transaction(err) => InvalidTransactionError::from(err).into(),
141            EVMError::Header(err) => match err {
142                InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet,
143                InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet,
144            },
145            EVMError::Database(err) => err.into(),
146            EVMError::Custom(err) => Self::Message(err),
147        }
148    }
149}
150
151impl<T> From<EVMError<T, OpTransactionError>> for BlockchainError
152where
153    T: Into<Self>,
154{
155    fn from(err: EVMError<T, OpTransactionError>) -> Self {
156        match err {
157            EVMError::Transaction(err) => match err {
158                OpTransactionError::Base(err) => InvalidTransactionError::from(err).into(),
159                OpTransactionError::DepositSystemTxPostRegolith => {
160                    Self::DepositTransactionUnsupported
161                }
162                OpTransactionError::HaltedDepositPostRegolith => {
163                    Self::DepositTransactionUnsupported
164                }
165            },
166            EVMError::Header(err) => match err {
167                InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet,
168                InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet,
169            },
170            EVMError::Database(err) => err.into(),
171            EVMError::Custom(err) => Self::Message(err),
172        }
173    }
174}
175
176impl From<WalletError> for BlockchainError {
177    fn from(value: WalletError) -> Self {
178        match value {
179            WalletError::ValueNotZero => Self::Message("tx value not zero".to_string()),
180            WalletError::FromSet => Self::Message("tx from field is set".to_string()),
181            WalletError::NonceSet => Self::Message("tx nonce is set".to_string()),
182            WalletError::InvalidAuthorization => {
183                Self::Message("invalid authorization address".to_string())
184            }
185            WalletError::IllegalDestination => Self::Message(
186                "the destination of the transaction is not a delegated account".to_string(),
187            ),
188            WalletError::InternalError => Self::Message("internal error".to_string()),
189            WalletError::InvalidTransactionRequest => {
190                Self::Message("invalid tx request".to_string())
191            }
192        }
193    }
194}
195
196impl<E> From<StateOverrideError<E>> for BlockchainError
197where
198    E: Into<Self>,
199{
200    fn from(value: StateOverrideError<E>) -> Self {
201        match value {
202            StateOverrideError::InvalidBytecode(err) => Self::StateOverrideError(err.to_string()),
203            StateOverrideError::BothStateAndStateDiff(addr) => Self::StateOverrideError(format!(
204                "state and state_diff can't be used together for account {addr}",
205            )),
206            StateOverrideError::Database(err) => err.into(),
207        }
208    }
209}
210
211/// Errors that can occur in the transaction pool
212#[derive(Debug, thiserror::Error)]
213pub enum PoolError {
214    #[error("Transaction with cyclic dependent transactions")]
215    CyclicTransaction,
216    /// Thrown if a replacement transaction's gas price is below the already imported transaction
217    #[error("Tx: [{0:?}] insufficient gas price to replace existing transaction")]
218    ReplacementUnderpriced(Box<PoolTransaction>),
219    #[error("Tx: [{0:?}] already Imported")]
220    AlreadyImported(Box<PoolTransaction>),
221}
222
223/// Errors that can occur with `eth_feeHistory`
224#[derive(Debug, thiserror::Error)]
225pub enum FeeHistoryError {
226    #[error("requested block range is out of bounds")]
227    InvalidBlockRange,
228    #[error("could not find newest block number requested: {0}")]
229    BlockNotFound(BlockNumberOrTag),
230}
231
232#[derive(Debug)]
233pub struct ErrDetail {
234    pub detail: String,
235}
236
237/// An error due to invalid transaction
238#[derive(Debug, thiserror::Error)]
239pub enum InvalidTransactionError {
240    /// returned if the nonce of a transaction is lower than the one present in the local chain.
241    #[error("nonce too low")]
242    NonceTooLow,
243    /// returned if the nonce of a transaction is higher than the next one expected based on the
244    /// local chain.
245    #[error("Nonce too high")]
246    NonceTooHigh,
247    /// Returned if the nonce of a transaction is too high
248    /// Incrementing the nonce would lead to invalid state (overflow)
249    #[error("nonce has max value")]
250    NonceMaxValue,
251    /// thrown if the transaction sender doesn't have enough funds for a transfer
252    #[error("insufficient funds for transfer")]
253    InsufficientFundsForTransfer,
254    /// thrown if creation transaction provides the init code bigger than init code size limit.
255    #[error("max initcode size exceeded")]
256    MaxInitCodeSizeExceeded,
257    /// Represents the inability to cover max cost + value (account balance too low).
258    #[error("Insufficient funds for gas * price + value")]
259    InsufficientFunds,
260    /// Thrown when calculating gas usage
261    #[error("gas uint64 overflow")]
262    GasUintOverflow,
263    /// returned if the transaction is specified to use less gas than required to start the
264    /// invocation.
265    #[error("intrinsic gas too low")]
266    GasTooLow,
267    /// returned if the transaction gas exceeds the limit
268    #[error("intrinsic gas too high -- {}",.0.detail)]
269    GasTooHigh(ErrDetail),
270    /// Thrown to ensure no one is able to specify a transaction with a tip higher than the total
271    /// fee cap.
272    #[error("max priority fee per gas higher than max fee per gas")]
273    TipAboveFeeCap,
274    /// Thrown post London if the transaction's fee is less than the base fee of the block
275    #[error("max fee per gas less than block base fee")]
276    FeeCapTooLow,
277    /// Thrown during estimate if caller has insufficient funds to cover the tx.
278    #[error("Out of gas: gas required exceeds allowance: {0:?}")]
279    BasicOutOfGas(u128),
280    /// Thrown if executing a transaction failed during estimate/call
281    #[error("execution reverted: {0:?}")]
282    Revert(Option<Bytes>),
283    /// Thrown if the sender of a transaction is a contract.
284    #[error("sender not an eoa")]
285    SenderNoEOA,
286    /// Thrown when a tx was signed with a different chain_id
287    #[error("invalid chain id for signer")]
288    InvalidChainId,
289    /// Thrown when a legacy tx was signed for a different chain
290    #[error("Incompatible EIP-155 transaction, signed for another chain")]
291    IncompatibleEIP155,
292    /// Thrown when an access list is used before the berlin hard fork.
293    #[error("Access lists are not supported before the Berlin hardfork")]
294    AccessListNotSupported,
295    /// Thrown when the block's `blob_gas_price` is greater than tx-specified
296    /// `max_fee_per_blob_gas` after Cancun.
297    #[error("Block `blob_gas_price` is greater than tx-specified `max_fee_per_blob_gas`")]
298    BlobFeeCapTooLow,
299    /// Thrown when we receive a tx with `blob_versioned_hashes` and we're not on the Cancun hard
300    /// fork.
301    #[error("Block `blob_versioned_hashes` is not supported before the Cancun hardfork")]
302    BlobVersionedHashesNotSupported,
303    /// Thrown when `max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.
304    #[error("`max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.")]
305    MaxFeePerBlobGasNotSupported,
306    /// Thrown when there are no `blob_hashes` in the transaction, and it is an EIP-4844 tx.
307    #[error("`blob_hashes` are required for EIP-4844 transactions")]
308    NoBlobHashes,
309    #[error("too many blobs in one transaction, have: {0}, max: {1}")]
310    TooManyBlobs(usize, usize),
311    /// Thrown when there's a blob validation error
312    #[error(transparent)]
313    BlobTransactionValidationError(#[from] alloy_consensus::BlobTransactionValidationError),
314    /// Thrown when Blob transaction is a create transaction. `to` must be present.
315    #[error("Blob transaction can't be a create transaction. `to` must be present.")]
316    BlobCreateTransaction,
317    /// Thrown when Blob transaction contains a versioned hash with an incorrect version.
318    #[error("Blob transaction contains a versioned hash with an incorrect version")]
319    BlobVersionNotSupported,
320    /// Thrown when there are no `blob_hashes` in the transaction.
321    #[error("There should be at least one blob in a Blob transaction.")]
322    EmptyBlobs,
323    /// Thrown when an access list is used before the berlin hard fork.
324    #[error("EIP-7702 authorization lists are not supported before the Prague hardfork")]
325    AuthorizationListNotSupported,
326    #[error("Transaction gas limit is greater than the block gas limit, gas_limit: {0}, cap: {1}")]
327    TxGasLimitGreaterThanCap(u64, u64),
328    /// Forwards error from the revm
329    #[error(transparent)]
330    Revm(revm::context_interface::result::InvalidTransaction),
331    /// Deposit transaction error post regolith
332    #[error("op-deposit failure post regolith")]
333    DepositTxErrorPostRegolith,
334}
335
336impl From<InvalidTransaction> for InvalidTransactionError {
337    fn from(err: InvalidTransaction) -> Self {
338        match err {
339            InvalidTransaction::InvalidChainId => Self::InvalidChainId,
340            InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap,
341            InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow,
342            InvalidTransaction::CallerGasLimitMoreThanBlock => {
343                Self::GasTooHigh(ErrDetail { detail: String::from("CallerGasLimitMoreThanBlock") })
344            }
345            InvalidTransaction::CallGasCostMoreThanGasLimit { .. } => {
346                Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") })
347            }
348            InvalidTransaction::GasFloorMoreThanGasLimit { .. } => {
349                Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") })
350            }
351            InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA,
352            InvalidTransaction::LackOfFundForMaxFee { .. } => Self::InsufficientFunds,
353            InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow,
354            InvalidTransaction::NonceOverflowInTransaction => Self::NonceMaxValue,
355            InvalidTransaction::CreateInitCodeSizeLimit => Self::MaxInitCodeSizeExceeded,
356            InvalidTransaction::NonceTooHigh { .. } => Self::NonceTooHigh,
357            InvalidTransaction::NonceTooLow { .. } => Self::NonceTooLow,
358            InvalidTransaction::AccessListNotSupported => Self::AccessListNotSupported,
359            InvalidTransaction::BlobGasPriceGreaterThanMax => Self::BlobFeeCapTooLow,
360            InvalidTransaction::BlobVersionedHashesNotSupported => {
361                Self::BlobVersionedHashesNotSupported
362            }
363            InvalidTransaction::MaxFeePerBlobGasNotSupported => Self::MaxFeePerBlobGasNotSupported,
364            InvalidTransaction::BlobCreateTransaction => Self::BlobCreateTransaction,
365            InvalidTransaction::BlobVersionNotSupported => Self::BlobVersionNotSupported,
366            InvalidTransaction::EmptyBlobs => Self::EmptyBlobs,
367            InvalidTransaction::TooManyBlobs { have, max } => Self::TooManyBlobs(have, max),
368            InvalidTransaction::AuthorizationListNotSupported => {
369                Self::AuthorizationListNotSupported
370            }
371            InvalidTransaction::TxGasLimitGreaterThanCap { gas_limit, cap } => {
372                Self::TxGasLimitGreaterThanCap(gas_limit, cap)
373            }
374
375            InvalidTransaction::AuthorizationListInvalidFields
376            | InvalidTransaction::Eip1559NotSupported
377            | InvalidTransaction::Eip2930NotSupported
378            | InvalidTransaction::Eip4844NotSupported
379            | InvalidTransaction::Eip7702NotSupported
380            | InvalidTransaction::EmptyAuthorizationList
381            | InvalidTransaction::Eip7873NotSupported
382            | InvalidTransaction::Eip7873MissingTarget
383            | InvalidTransaction::MissingChainId => Self::Revm(err),
384        }
385    }
386}
387
388impl From<OpTransactionError> for InvalidTransactionError {
389    fn from(value: OpTransactionError) -> Self {
390        match value {
391            OpTransactionError::Base(err) => err.into(),
392            OpTransactionError::DepositSystemTxPostRegolith
393            | OpTransactionError::HaltedDepositPostRegolith => Self::DepositTxErrorPostRegolith,
394        }
395    }
396}
397/// Helper trait to easily convert results to rpc results
398pub(crate) trait ToRpcResponseResult {
399    fn to_rpc_result(self) -> ResponseResult;
400}
401
402/// Converts a serializable value into a `ResponseResult`
403pub fn to_rpc_result<T: Serialize>(val: T) -> ResponseResult {
404    match serde_json::to_value(val) {
405        Ok(success) => ResponseResult::Success(success),
406        Err(err) => {
407            error!(%err, "Failed serialize rpc response");
408            ResponseResult::error(RpcError::internal_error())
409        }
410    }
411}
412
413impl<T: Serialize> ToRpcResponseResult for Result<T> {
414    fn to_rpc_result(self) -> ResponseResult {
415        match self {
416            Ok(val) => to_rpc_result(val),
417            Err(err) => match err {
418                BlockchainError::Pool(err) => {
419                    error!(%err, "txpool error");
420                    match err {
421                        PoolError::CyclicTransaction => {
422                            RpcError::transaction_rejected("Cyclic transaction detected")
423                        }
424                        PoolError::ReplacementUnderpriced(_) => {
425                            RpcError::transaction_rejected("replacement transaction underpriced")
426                        }
427                        PoolError::AlreadyImported(_) => {
428                            RpcError::transaction_rejected("transaction already imported")
429                        }
430                    }
431                }
432                BlockchainError::NoSignerAvailable => {
433                    RpcError::invalid_params("No Signer available")
434                }
435                BlockchainError::ChainIdNotAvailable => {
436                    RpcError::invalid_params("Chain Id not available")
437                }
438                BlockchainError::TransactionConfirmationTimeout { .. } => {
439                    RpcError::internal_error_with("Transaction confirmation timeout")
440                }
441                BlockchainError::InvalidTransaction(err) => match err {
442                    InvalidTransactionError::Revert(data) => {
443                        // this mimics geth revert error
444                        let mut msg = "execution reverted".to_string();
445                        if let Some(reason) = data
446                            .as_ref()
447                            .and_then(|data| RevertDecoder::new().maybe_decode(data, None))
448                        {
449                            msg = format!("{msg}: {reason}");
450                        }
451                        RpcError {
452                            // geth returns this error code on reverts, See <https://eips.ethereum.org/EIPS/eip-1474#specification>
453                            code: ErrorCode::ExecutionError,
454                            message: msg.into(),
455                            data: serde_json::to_value(data).ok(),
456                        }
457                    }
458                    InvalidTransactionError::GasTooLow => {
459                        // <https://eips.ethereum.org/EIPS/eip-1898>
460                        RpcError {
461                            code: ErrorCode::ServerError(-32000),
462                            message: err.to_string().into(),
463                            data: None,
464                        }
465                    }
466                    InvalidTransactionError::GasTooHigh(_) => {
467                        // <https://eips.ethereum.org/EIPS/eip-1898>
468                        RpcError {
469                            code: ErrorCode::ServerError(-32000),
470                            message: err.to_string().into(),
471                            data: None,
472                        }
473                    }
474                    _ => RpcError::transaction_rejected(err.to_string()),
475                },
476                BlockchainError::FeeHistory(err) => RpcError::invalid_params(err.to_string()),
477                BlockchainError::EmptyRawTransactionData => {
478                    RpcError::invalid_params("Empty transaction data")
479                }
480                BlockchainError::FailedToDecodeSignedTransaction => {
481                    RpcError::invalid_params("Failed to decode transaction")
482                }
483                BlockchainError::FailedToDecodeTransaction => {
484                    RpcError::invalid_params("Failed to decode transaction")
485                }
486                BlockchainError::FailedToDecodeReceipt => {
487                    RpcError::invalid_params("Failed to decode receipt")
488                }
489                BlockchainError::FailedToDecodeStateDump => {
490                    RpcError::invalid_params("Failed to decode state dump")
491                }
492                BlockchainError::SignerError(err) => RpcError::invalid_params(err.to_string()),
493                BlockchainError::SignatureError(err) => RpcError::invalid_params(err.to_string()),
494                BlockchainError::RpcUnimplemented => {
495                    RpcError::internal_error_with("Not implemented")
496                }
497                BlockchainError::PrevrandaoNotSet => RpcError::internal_error_with(err.to_string()),
498                BlockchainError::RpcError(err) => err,
499                BlockchainError::InvalidFeeInput => RpcError::invalid_params(
500                    "Invalid input: `max_priority_fee_per_gas` greater than `max_fee_per_gas`",
501                ),
502                BlockchainError::AlloyForkProvider(err) => {
503                    error!(target: "backend", %err, "fork provider error");
504                    match err {
505                        TransportError::ErrorResp(err) => RpcError {
506                            code: ErrorCode::from(err.code),
507                            message: err.message,
508                            data: err.data.and_then(|data| serde_json::to_value(data).ok()),
509                        },
510                        err => RpcError::internal_error_with(format!("Fork Error: {err:?}")),
511                    }
512                }
513                err @ BlockchainError::EvmError(_) => {
514                    RpcError::internal_error_with(err.to_string())
515                }
516                err @ BlockchainError::EvmOverrideError(_) => {
517                    RpcError::invalid_params(err.to_string())
518                }
519                err @ BlockchainError::InvalidUrl(_) => RpcError::invalid_params(err.to_string()),
520                BlockchainError::Internal(err) => RpcError::internal_error_with(err),
521                err @ BlockchainError::BlockOutOfRange(_, _) => {
522                    RpcError::invalid_params(err.to_string())
523                }
524                err @ BlockchainError::BlockNotFound => RpcError {
525                    // <https://eips.ethereum.org/EIPS/eip-1898>
526                    code: ErrorCode::ServerError(-32001),
527                    message: err.to_string().into(),
528                    data: None,
529                },
530                err @ BlockchainError::DataUnavailable => {
531                    RpcError::internal_error_with(err.to_string())
532                }
533                err @ BlockchainError::TrieError(_) => {
534                    RpcError::internal_error_with(err.to_string())
535                }
536                BlockchainError::UintConversion(err) => RpcError::invalid_params(err),
537                err @ BlockchainError::StateOverrideError(_) => {
538                    RpcError::invalid_params(err.to_string())
539                }
540                err @ BlockchainError::TimestampError(_) => {
541                    RpcError::invalid_params(err.to_string())
542                }
543                BlockchainError::DatabaseError(err) => {
544                    RpcError::internal_error_with(err.to_string())
545                }
546                err @ BlockchainError::EIP1559TransactionUnsupportedAtHardfork => {
547                    RpcError::invalid_params(err.to_string())
548                }
549                err @ BlockchainError::EIP2930TransactionUnsupportedAtHardfork => {
550                    RpcError::invalid_params(err.to_string())
551                }
552                err @ BlockchainError::EIP4844TransactionUnsupportedAtHardfork => {
553                    RpcError::invalid_params(err.to_string())
554                }
555                err @ BlockchainError::EIP7702TransactionUnsupportedAtHardfork => {
556                    RpcError::invalid_params(err.to_string())
557                }
558                err @ BlockchainError::DepositTransactionUnsupported => {
559                    RpcError::invalid_params(err.to_string())
560                }
561                err @ BlockchainError::ExcessBlobGasNotSet => {
562                    RpcError::invalid_params(err.to_string())
563                }
564                err @ BlockchainError::Message(_) => RpcError::internal_error_with(err.to_string()),
565                err @ BlockchainError::UnknownTransactionType => {
566                    RpcError::invalid_params(err.to_string())
567                }
568                err @ BlockchainError::MissingRequiredFields => {
569                    RpcError::invalid_params(err.to_string())
570                }
571            }
572            .into(),
573        }
574    }
575}