//! RPC errors specific to OP. use alloy_json_rpc::ErrorPayload; use alloy_rpc_types_eth::{error::EthRpcErrorCode, BlockError}; use alloy_transport::{RpcError, TransportErrorKind}; use jsonrpsee_types::error::{INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE}; use op_revm::{OpHaltReason, OpTransactionError}; use reth_evm::execute::ProviderError; use reth_optimism_evm::OpBlockExecutionError; use reth_rpc_eth_api::{AsEthApiError, EthTxEnvError, TransactionConversionError}; use reth_rpc_eth_types::{error::api::FromEvmHalt, EthApiError}; use reth_rpc_server_types::result::{internal_rpc_err, rpc_err}; use revm::context_interface::result::{EVMError, InvalidTransaction}; use std::fmt::Display; /// Optimism specific errors, that extend [`EthApiError`]. #[derive(Debug, thiserror::Error)] pub enum OpEthApiError { /// L1 ethereum error. #[error(transparent)] Eth(#[from] EthApiError), /// EVM error originating from invalid optimism data. #[error(transparent)] Evm(#[from] OpBlockExecutionError), /// Thrown when calculating L1 gas fee. #[error("failed to calculate l1 gas fee")] L1BlockFeeError, /// Thrown when calculating L1 gas used #[error("failed to calculate l1 gas used")] L1BlockGasError, /// Wrapper for [`revm_primitives::InvalidTransaction`](InvalidTransaction). #[error(transparent)] InvalidTransaction(#[from] OpInvalidTransactionError), /// Sequencer client error. #[error(transparent)] Sequencer(#[from] SequencerClientError), } impl AsEthApiError for OpEthApiError { fn as_err(&self) -> Option<&EthApiError> { match self { Self::Eth(err) => Some(err), _ => None, } } } impl From for jsonrpsee_types::error::ErrorObject<'static> { fn from(err: OpEthApiError) -> Self { match err { OpEthApiError::Eth(err) => err.into(), OpEthApiError::InvalidTransaction(err) => err.into(), OpEthApiError::Evm(_) | OpEthApiError::L1BlockFeeError | OpEthApiError::L1BlockGasError => internal_rpc_err(err.to_string()), OpEthApiError::Sequencer(err) => err.into(), } } } /// Optimism specific invalid transaction errors #[derive(thiserror::Error, Debug)] pub enum OpInvalidTransactionError { /// A deposit transaction was submitted as a system transaction post-regolith. #[error("no system transactions allowed after regolith")] DepositSystemTxPostRegolith, /// A deposit transaction halted post-regolith #[error("deposit transaction halted after regolith")] HaltedDepositPostRegolith, /// Transaction conditional errors. #[error(transparent)] TxConditionalErr(#[from] TxConditionalErr), } impl From for jsonrpsee_types::error::ErrorObject<'static> { fn from(err: OpInvalidTransactionError) -> Self { match err { OpInvalidTransactionError::DepositSystemTxPostRegolith | OpInvalidTransactionError::HaltedDepositPostRegolith => { rpc_err(EthRpcErrorCode::TransactionRejected.code(), err.to_string(), None) } OpInvalidTransactionError::TxConditionalErr(_) => err.into(), } } } impl TryFrom for OpInvalidTransactionError { type Error = InvalidTransaction; fn try_from(err: OpTransactionError) -> Result { match err { OpTransactionError::DepositSystemTxPostRegolith => { Ok(Self::DepositSystemTxPostRegolith) } OpTransactionError::HaltedDepositPostRegolith => Ok(Self::HaltedDepositPostRegolith), OpTransactionError::Base(err) => Err(err), } } } /// Transaction conditional related errors. #[derive(Debug, thiserror::Error)] pub enum TxConditionalErr { /// Transaction conditional cost exceeded maximum allowed #[error("conditional cost exceeded maximum allowed")] ConditionalCostExceeded, /// Invalid conditional parameters #[error("invalid conditional parameters")] InvalidCondition, /// Internal error #[error("internal error: {0}")] Internal(String), /// Thrown if the conditional's storage value doesn't match the latest state's. #[error("storage value mismatch")] StorageValueMismatch, /// Thrown when the conditional's storage root doesn't match the latest state's root. #[error("storage root mismatch")] StorageRootMismatch, } impl TxConditionalErr { /// Creates an internal error variant pub fn internal(err: E) -> Self { Self::Internal(err.to_string()) } } impl From for jsonrpsee_types::error::ErrorObject<'static> { fn from(err: TxConditionalErr) -> Self { let code = match &err { TxConditionalErr::Internal(_) => INTERNAL_ERROR_CODE, _ => INVALID_PARAMS_CODE, }; jsonrpsee_types::error::ErrorObject::owned(code, err.to_string(), None::) } } /// Error type when interacting with the Sequencer #[derive(Debug, thiserror::Error)] pub enum SequencerClientError { /// Wrapper around an [`RpcError`]. #[error(transparent)] HttpError(#[from] RpcError), } impl From for jsonrpsee_types::error::ErrorObject<'static> { fn from(err: SequencerClientError) -> Self { match err { SequencerClientError::HttpError(RpcError::ErrorResp(ErrorPayload { code, message, data, })) => jsonrpsee_types::error::ErrorObject::owned(code as i32, message, data), err => jsonrpsee_types::error::ErrorObject::owned( INTERNAL_ERROR_CODE, err.to_string(), None::, ), } } } impl From> for OpEthApiError where T: Into, { fn from(error: EVMError) -> Self { match error { EVMError::Transaction(err) => match err.try_into() { Ok(err) => Self::InvalidTransaction(err), Err(err) => Self::Eth(EthApiError::InvalidTransaction(err.into())), }, EVMError::Database(err) => Self::Eth(err.into()), EVMError::Header(err) => Self::Eth(err.into()), EVMError::Custom(err) => Self::Eth(EthApiError::EvmCustom(err)), } } } impl FromEvmHalt for OpEthApiError { fn from_evm_halt(halt: OpHaltReason, gas_limit: u64) -> Self { match halt { OpHaltReason::FailedDeposit => { OpInvalidTransactionError::HaltedDepositPostRegolith.into() } OpHaltReason::Base(halt) => EthApiError::from_evm_halt(halt, gas_limit).into(), } } } impl From for OpEthApiError { fn from(value: TransactionConversionError) -> Self { Self::Eth(EthApiError::from(value)) } } impl From for OpEthApiError { fn from(value: EthTxEnvError) -> Self { Self::Eth(EthApiError::from(value)) } } impl From for OpEthApiError { fn from(value: ProviderError) -> Self { Self::Eth(EthApiError::from(value)) } } impl From for OpEthApiError { fn from(value: BlockError) -> Self { Self::Eth(EthApiError::from(value)) } }