1pub mod api;
4use alloy_eips::BlockId;
5use alloy_evm::{call::CallError, overrides::StateOverrideError};
6use alloy_primitives::{Address, Bytes, B256, U256};
7use alloy_rpc_types_eth::{error::EthRpcErrorCode, request::TransactionInputError, BlockError};
8use alloy_sol_types::{ContractError, RevertReason};
9use alloy_transport::{RpcError, TransportErrorKind};
10pub use api::{AsEthApiError, FromEthApiError, FromEvmError, IntoEthApiError};
11use core::time::Duration;
12use reth_errors::{BlockExecutionError, BlockValidationError, RethError};
13use reth_primitives_traits::transaction::{error::InvalidTransactionError, signed::RecoveryError};
14use reth_revm::db::bal::EvmDatabaseError;
15use reth_rpc_convert::{CallFeesError, EthTxEnvError, TransactionConversionError};
16use reth_rpc_server_types::result::{
17 block_id_to_str, internal_rpc_err, invalid_params_rpc_err, rpc_err, rpc_error_with_code,
18};
19use reth_transaction_pool::error::{
20 Eip4844PoolTransactionError, Eip7702PoolTransactionError, InvalidPoolTransactionError,
21 PoolError, PoolErrorKind, PoolTransactionError,
22};
23use revm::{
24 context_interface::result::{
25 EVMError, HaltReason, InvalidHeader, InvalidTransaction, OutOfGasError,
26 },
27 state::bal::BalError,
28};
29use revm_inspectors::tracing::{DebugInspectorError, MuxError};
30use std::convert::Infallible;
31use tokio::sync::oneshot::error::RecvError;
32
33pub trait ToRpcError: core::error::Error + Send + Sync + 'static {
35 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
58pub type EthResult<T> = Result<T, EthApiError>;
60
61#[derive(Debug, thiserror::Error)]
63pub enum EthApiError {
64 #[error("empty transaction data")]
66 EmptyRawTransactionData,
67 #[error("failed to decode signed transaction")]
69 FailedToDecodeSignedTransaction,
70 #[error("invalid transaction signature")]
72 InvalidTransactionSignature,
73 #[error(transparent)]
75 PoolError(#[from] RpcPoolError),
76 #[error("header not found")]
78 HeaderNotFound(BlockId),
79 #[error("header range not found, start block {0:?}, end block {1:?}")]
81 HeaderRangeNotFound(BlockId, BlockId),
82 #[error("pruned history unavailable")]
89 PrunedHistoryUnavailable,
90 #[error("receipts not found")]
92 ReceiptsNotFound(BlockId),
93 #[error("unknown block or tx index")]
95 UnknownBlockOrTxIndex,
96 #[error("invalid block range")]
98 InvalidBlockRange,
99 #[error("request beyond head block: requested {requested}, head {head}")]
101 RequestBeyondHead {
102 requested: u64,
104 head: u64,
106 },
107 #[error("distance to target block exceeds maximum proof window")]
109 ExceedsMaxProofWindow,
110 #[error("prevrandao not in the EVM's environment after merge")]
112 PrevrandaoNotSet,
113 #[error("excess blob gas missing in the EVM's environment after Cancun")]
115 ExcessBlobGasNotSet,
116 #[error("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")]
119 ConflictingFeeFieldsInRequest,
120 #[error(transparent)]
122 InvalidTransaction(#[from] RpcInvalidTransactionError),
123 #[error(transparent)]
125 InvalidBlockData(#[from] BlockError),
126 #[error("account {0:?} has both 'state' and 'stateDiff'")]
128 BothStateAndStateDiffInOverride(Address),
129 #[error(transparent)]
131 Internal(RethError),
132 #[error(transparent)]
134 Signing(#[from] SignError),
135 #[error("transaction not found")]
137 TransactionNotFound,
138 #[error("unsupported")]
140 Unsupported(&'static str),
141 #[error("{0}")]
143 InvalidParams(String),
144 #[error("invalid tracer config")]
146 InvalidTracerConfig,
147 #[error("invalid reward percentiles")]
149 InvalidRewardPercentiles,
150 #[error("internal blocking task error")]
155 InternalBlockingTaskError,
156 #[error("internal eth error")]
158 InternalEthError,
159 #[error("execution aborted (timeout = {0:?})")]
161 ExecutionTimedOut(Duration),
162 #[error("{0}")]
164 InternalJsTracerError(String),
165 #[error(transparent)]
166 TransactionInputError(#[from] TransactionInputError),
168 #[error("Revm error: {0}")]
170 EvmCustom(String),
171 #[error("Invalid bytecode: {0}")]
177 InvalidBytecode(String),
178 #[error(transparent)]
180 TransactionConversionError(#[from] TransactionConversionError),
181 #[error(transparent)]
183 MuxTracerError(#[from] MuxError),
184 #[error(
186 "Transaction {hash} was added to the mempool but wasn't confirmed within {duration:?}."
187 )]
188 TransactionConfirmationTimeout {
189 hash: B256,
191 duration: Duration,
193 },
194 #[error(transparent)]
196 BatchTxRecvError(#[from] RecvError),
197 #[error("Batch transaction sender channel closed")]
199 BatchTxSendError,
200 #[error("call_many error in bundle {bundle_index} and transaction {tx_index}: {}", .error.message())]
202 CallManyError {
203 bundle_index: usize,
205 tx_index: usize,
207 error: jsonrpsee_types::ErrorObject<'static>,
209 },
210 #[error("Block access list not available for pre-Amsterdam blocks")]
212 BlockAccessListNotAvailablePreAmsterdam,
213 #[error("{0}")]
215 Other(Box<dyn ToRpcError>),
216}
217
218impl EthApiError {
219 pub fn other<E: ToRpcError>(err: E) -> Self {
221 Self::Other(Box::new(err))
222 }
223
224 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 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 pub const fn is_gas_too_low(&self) -> bool {
246 matches!(self, Self::InvalidTransaction(RpcInvalidTransactionError::GasTooLow))
247 }
248
249 pub const fn as_invalid_transaction(&self) -> Option<&RpcInvalidTransactionError> {
251 match self {
252 Self::InvalidTransaction(e) => Some(e),
253 _ => None,
254 }
255 }
256
257 pub fn from_state_overrides_err<E>(err: StateOverrideError<E>) -> Self
259 where
260 E: Into<Self>,
261 {
262 err.into()
263 }
264
265 pub fn from_call_err<E>(err: CallError<E>) -> Self
267 where
268 E: Into<Self>,
269 {
270 err.into()
271 }
272
273 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 if let Some(eth_tx_err) = invalid_tx.as_invalid_tx_err() {
543 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 Self::EvmCustom(invalid_tx.to_string())
557 }
558 }
559 EVMError::Header(err) => err.into(),
560 EVMError::Database(err) => err.into(),
561 EVMError::Custom(err) => Self::EvmCustom(err),
562 EVMError::CustomAny(err) => Self::EvmCustom(err.to_string()),
563 }
564 }
565}
566
567impl From<RecoveryError> for EthApiError {
568 fn from(_: RecoveryError) -> Self {
569 Self::InvalidTransactionSignature
570 }
571}
572
573impl From<Infallible> for EthApiError {
574 fn from(_: Infallible) -> Self {
575 unreachable!()
576 }
577}
578
579#[derive(thiserror::Error, Debug)]
594pub enum RpcInvalidTransactionError {
595 #[error("nonce too low: next nonce {state}, tx nonce {tx}")]
597 NonceTooLow {
598 tx: u64,
600 state: u64,
602 },
603 #[error("nonce too high")]
606 NonceTooHigh,
607 #[error("nonce has max value")]
610 NonceMaxValue,
611 #[error("insufficient funds for transfer")]
613 InsufficientFundsForTransfer,
614 #[error("max initcode size exceeded")]
616 MaxInitCodeSizeExceeded,
617 #[error("insufficient funds for gas * price + value: have {balance} want {cost}")]
619 InsufficientFunds {
620 cost: U256,
622 balance: U256,
624 },
625 #[error("gas required exceeds allowance ({gas_limit})")]
631 GasRequiredExceedsAllowance {
632 gas_limit: u64,
634 },
635 #[error("gas uint64 overflow")]
637 GasUintOverflow,
638 #[error("intrinsic gas too low")]
641 GasTooLow,
642 #[error("intrinsic gas too high")]
644 GasTooHigh,
645 #[error("gas limit too high")]
647 GasLimitTooHigh,
648 #[error("transaction type not supported")]
650 TxTypeNotSupported,
651 #[error("max priority fee per gas higher than max fee per gas")]
654 TipAboveFeeCap,
655 #[error("max priority fee per gas higher than 2^256-1")]
657 TipVeryHigh,
658 #[error("max fee per gas higher than 2^256-1")]
660 FeeCapVeryHigh,
661 #[error("max fee per gas less than block base fee")]
663 FeeCapTooLow,
664 #[error("sender is not an EOA")]
666 SenderNoEOA,
667 #[error("out of gas: gas required exceeds: {0}")]
670 BasicOutOfGas(u64),
671 #[error("out of gas: gas exhausted during memory expansion: {0}")]
674 MemoryOutOfGas(u64),
675 #[error("out of memory: memory limit exceeded during memory expansion")]
677 MemoryLimitOutOfGas,
678 #[error("out of gas: gas exhausted during precompiled contract execution: {0}")]
681 PrecompileOutOfGas(u64),
682 #[error("out of gas: invalid operand to an opcode: {0}")]
685 InvalidOperandOutOfGas(u64),
686 #[error(transparent)]
688 Revert(RevertError),
689 #[error("EVM error: {0:?}")]
691 EvmHalt(HaltReason),
692 #[error("invalid chain ID")]
694 InvalidChainId,
695 #[error("transactions before Spurious Dragon should not have a chain ID")]
697 OldLegacyChainId,
698 #[error("transactions before Berlin should not have access list")]
700 AccessListNotSupported,
701 #[error("max_fee_per_blob_gas is not supported for blocks before the Cancun hardfork")]
703 MaxFeePerBlobGasNotSupported,
704 #[error("blob_versioned_hashes is not supported for blocks before the Cancun hardfork")]
707 BlobVersionedHashesNotSupported,
708 #[error("max fee per blob gas less than block blob gas fee")]
710 BlobFeeCapTooLow,
711 #[error("blob hash version mismatch")]
713 BlobHashVersionMismatch,
714 #[error("blob transaction missing blob hashes")]
716 BlobTransactionMissingBlobHashes,
717 #[error("blob transaction exceeds max blobs per block; got {have}")]
719 TooManyBlobs {
720 have: usize,
722 },
723 #[error("blob transaction is a create transaction")]
725 BlobTransactionIsCreate,
726 #[error("EIP-7702 authorization list not supported")]
728 AuthorizationListNotSupported,
729 #[error("EIP-7702 authorization list has invalid fields")]
731 AuthorizationListInvalidFields,
732 #[error("transaction priority fee below minimum required priority fee {minimum_priority_fee}")]
734 PriorityFeeBelowMinimum {
735 minimum_priority_fee: u128,
737 },
738 #[error("{0}")]
740 Other(Box<dyn ToRpcError>),
741}
742
743impl RpcInvalidTransactionError {
744 pub fn other<E: ToRpcError>(err: E) -> Self {
746 Self::Other(Box::new(err))
747 }
748
749 pub const fn error_code(&self) -> i32 {
751 match self {
752 Self::InvalidChainId |
753 Self::GasTooLow |
754 Self::GasTooHigh |
755 Self::GasRequiredExceedsAllowance { .. } |
756 Self::NonceTooLow { .. } |
757 Self::NonceTooHigh { .. } |
758 Self::FeeCapTooLow |
759 Self::FeeCapVeryHigh => EthRpcErrorCode::InvalidInput.code(),
760 Self::Revert(_) => EthRpcErrorCode::ExecutionError.code(),
761 _ => EthRpcErrorCode::TransactionRejected.code(),
762 }
763 }
764
765 pub fn halt(reason: HaltReason, gas_limit: u64) -> Self {
769 match reason {
770 HaltReason::OutOfGas(err) => Self::out_of_gas(err, gas_limit),
771 HaltReason::NonceOverflow => Self::NonceMaxValue,
772 err => Self::EvmHalt(err),
773 }
774 }
775
776 pub const fn out_of_gas(reason: OutOfGasError, gas_limit: u64) -> Self {
778 match reason {
779 OutOfGasError::Basic | OutOfGasError::ReentrancySentry => {
780 Self::BasicOutOfGas(gas_limit)
781 }
782 OutOfGasError::Memory => Self::MemoryOutOfGas(gas_limit),
783 OutOfGasError::MemoryLimit => Self::MemoryLimitOutOfGas,
784 OutOfGasError::Precompile => Self::PrecompileOutOfGas(gas_limit),
785 OutOfGasError::InvalidOperand => Self::InvalidOperandOutOfGas(gas_limit),
786 }
787 }
788
789 pub fn into_rpc_err(self) -> jsonrpsee_types::error::ErrorObject<'static> {
791 self.into()
792 }
793}
794
795impl From<RpcInvalidTransactionError> for jsonrpsee_types::error::ErrorObject<'static> {
796 fn from(err: RpcInvalidTransactionError) -> Self {
797 match err {
798 RpcInvalidTransactionError::Revert(revert) => {
799 rpc_err(
801 revert.error_code(),
802 revert.to_string(),
803 revert.output.as_ref().map(|out| out.as_ref()),
804 )
805 }
806 RpcInvalidTransactionError::Other(err) => err.to_rpc_error(),
807 err => rpc_err(err.error_code(), err.to_string(), None),
808 }
809 }
810}
811
812impl From<InvalidTransaction> for RpcInvalidTransactionError {
813 fn from(err: InvalidTransaction) -> Self {
814 match err {
815 InvalidTransaction::InvalidChainId | InvalidTransaction::MissingChainId => {
816 Self::InvalidChainId
817 }
818 InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap,
819 InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow,
820 InvalidTransaction::CallerGasLimitMoreThanBlock |
821 InvalidTransaction::TxGasLimitGreaterThanCap { .. } => {
822 Self::GasTooHigh
824 }
825 InvalidTransaction::CallGasCostMoreThanGasLimit { .. } => {
826 Self::GasTooLow
828 }
829 InvalidTransaction::GasFloorMoreThanGasLimit { .. } => {
830 Self::GasTooLow
835 }
836 InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA,
837 InvalidTransaction::LackOfFundForMaxFee { fee, balance } => {
838 Self::InsufficientFunds { cost: *fee, balance: *balance }
839 }
840 InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow,
841 InvalidTransaction::NonceOverflowInTransaction => Self::NonceMaxValue,
842 InvalidTransaction::CreateInitCodeSizeLimit => Self::MaxInitCodeSizeExceeded,
843 InvalidTransaction::NonceTooHigh { .. } => Self::NonceTooHigh,
844 InvalidTransaction::NonceTooLow { tx, state } => Self::NonceTooLow { tx, state },
845 InvalidTransaction::AccessListNotSupported => Self::AccessListNotSupported,
846 InvalidTransaction::MaxFeePerBlobGasNotSupported => Self::MaxFeePerBlobGasNotSupported,
847 InvalidTransaction::BlobVersionedHashesNotSupported => {
848 Self::BlobVersionedHashesNotSupported
849 }
850 InvalidTransaction::BlobGasPriceGreaterThanMax { .. } => Self::BlobFeeCapTooLow,
851 InvalidTransaction::EmptyBlobs => Self::BlobTransactionMissingBlobHashes,
852 InvalidTransaction::BlobVersionNotSupported => Self::BlobHashVersionMismatch,
853 InvalidTransaction::TooManyBlobs { have, .. } => Self::TooManyBlobs { have },
854 InvalidTransaction::BlobCreateTransaction => Self::BlobTransactionIsCreate,
855 InvalidTransaction::AuthorizationListNotSupported => {
856 Self::AuthorizationListNotSupported
857 }
858 InvalidTransaction::AuthorizationListInvalidFields |
859 InvalidTransaction::EmptyAuthorizationList => Self::AuthorizationListInvalidFields,
860 InvalidTransaction::Eip2930NotSupported |
861 InvalidTransaction::Eip1559NotSupported |
862 InvalidTransaction::Eip4844NotSupported |
863 InvalidTransaction::Eip7702NotSupported |
864 InvalidTransaction::Eip7873NotSupported => Self::TxTypeNotSupported,
865 InvalidTransaction::Eip7873MissingTarget => {
866 Self::other(internal_rpc_err(err.to_string()))
867 }
868 InvalidTransaction::Str(_) => Self::other(internal_rpc_err(err.to_string())),
869 }
870 }
871}
872
873impl From<InvalidTransactionError> for RpcInvalidTransactionError {
874 fn from(err: InvalidTransactionError) -> Self {
875 use InvalidTransactionError;
876 match err {
879 InvalidTransactionError::InsufficientFunds(res) => {
880 Self::InsufficientFunds { cost: res.expected, balance: res.got }
881 }
882 InvalidTransactionError::NonceNotConsistent { tx, state } => {
883 Self::NonceTooLow { tx, state }
884 }
885 InvalidTransactionError::OldLegacyChainId => {
886 Self::OldLegacyChainId
888 }
889 InvalidTransactionError::ChainIdMismatch => Self::InvalidChainId,
890 InvalidTransactionError::Eip2930Disabled |
891 InvalidTransactionError::Eip1559Disabled |
892 InvalidTransactionError::Eip4844Disabled |
893 InvalidTransactionError::Eip7702Disabled |
894 InvalidTransactionError::TxTypeNotSupported => Self::TxTypeNotSupported,
895 InvalidTransactionError::GasUintOverflow => Self::GasUintOverflow,
896 InvalidTransactionError::GasTooLow => Self::GasTooLow,
897 InvalidTransactionError::GasTooHigh => Self::GasTooHigh,
898 InvalidTransactionError::TipAboveFeeCap => Self::TipAboveFeeCap,
899 InvalidTransactionError::FeeCapTooLow => Self::FeeCapTooLow,
900 InvalidTransactionError::SignerAccountHasBytecode => Self::SenderNoEOA,
901 InvalidTransactionError::GasLimitTooHigh => Self::GasLimitTooHigh,
902 }
903 }
904}
905
906#[derive(Debug, Clone, thiserror::Error)]
910pub struct RevertError {
911 output: Option<Bytes>,
915}
916
917impl RevertError {
920 pub fn new(output: Bytes) -> Self {
924 if output.is_empty() {
925 Self { output: None }
926 } else {
927 Self { output: Some(output) }
928 }
929 }
930
931 pub const fn error_code(&self) -> i32 {
933 EthRpcErrorCode::ExecutionError.code()
934 }
935}
936
937impl std::fmt::Display for RevertError {
938 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
939 f.write_str("execution reverted")?;
940 if let Some(reason) = self.output.as_ref().and_then(|out| RevertReason::decode(out)) {
941 let error = reason.to_string();
942 let mut error = error.as_str();
943 if matches!(reason, RevertReason::ContractError(ContractError::Revert(_))) {
944 error = error.trim_start_matches("revert: ");
946 }
947 write!(f, ": {error}")?;
948 }
949 Ok(())
950 }
951}
952
953#[derive(Debug, thiserror::Error)]
955pub enum RpcPoolError {
956 #[error("already known")]
958 AlreadyKnown,
959 #[error("invalid sender")]
961 InvalidSender,
962 #[error("transaction underpriced")]
964 Underpriced,
965 #[error("txpool is full")]
967 TxPoolOverflow,
968 #[error("replacement transaction underpriced")]
970 ReplaceUnderpriced,
971 #[error("exceeds block gas limit")]
973 ExceedsGasLimit,
974 #[error("exceeds max transaction gas limit")]
976 MaxTxGasLimitExceeded,
977 #[error("tx fee ({max_tx_fee_wei} wei) exceeds the configured cap ({tx_fee_cap_wei} wei)")]
980 ExceedsFeeCap {
981 max_tx_fee_wei: u128,
983 tx_fee_cap_wei: u128,
985 },
986 #[error("negative value")]
988 NegativeValue,
989 #[error("oversized data: transaction size {size}, limit {limit}")]
991 OversizedData {
992 size: usize,
994 limit: usize,
996 },
997 #[error("max initcode size exceeded")]
999 ExceedsMaxInitCodeSize,
1000 #[error(transparent)]
1002 Invalid(#[from] RpcInvalidTransactionError),
1003 #[error(transparent)]
1005 PoolTransactionError(Box<dyn PoolTransactionError>),
1006 #[error(transparent)]
1008 Eip4844(#[from] Eip4844PoolTransactionError),
1009 #[error(transparent)]
1011 Eip7702(#[from] Eip7702PoolTransactionError),
1012 #[error("address already reserved")]
1017 AddressAlreadyReserved,
1018 #[error(transparent)]
1020 Other(Box<dyn core::error::Error + Send + Sync>),
1021}
1022
1023impl From<RpcPoolError> for jsonrpsee_types::error::ErrorObject<'static> {
1024 fn from(error: RpcPoolError) -> Self {
1025 match error {
1026 RpcPoolError::Invalid(err) => err.into(),
1027 RpcPoolError::TxPoolOverflow => {
1028 rpc_error_with_code(EthRpcErrorCode::TransactionRejected.code(), error.to_string())
1029 }
1030 RpcPoolError::AlreadyKnown |
1031 RpcPoolError::InvalidSender |
1032 RpcPoolError::Underpriced |
1033 RpcPoolError::ReplaceUnderpriced |
1034 RpcPoolError::ExceedsGasLimit |
1035 RpcPoolError::MaxTxGasLimitExceeded |
1036 RpcPoolError::ExceedsFeeCap { .. } |
1037 RpcPoolError::NegativeValue |
1038 RpcPoolError::OversizedData { .. } |
1039 RpcPoolError::ExceedsMaxInitCodeSize |
1040 RpcPoolError::PoolTransactionError(_) |
1041 RpcPoolError::Eip4844(_) |
1042 RpcPoolError::Eip7702(_) |
1043 RpcPoolError::AddressAlreadyReserved => {
1044 rpc_error_with_code(EthRpcErrorCode::InvalidInput.code(), error.to_string())
1045 }
1046 RpcPoolError::Other(other) => internal_rpc_err(other.to_string()),
1047 }
1048 }
1049}
1050
1051impl From<PoolError> for RpcPoolError {
1052 fn from(err: PoolError) -> Self {
1053 match err.kind {
1054 PoolErrorKind::ReplacementUnderpriced => Self::ReplaceUnderpriced,
1055 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(_) => Self::Underpriced,
1056 PoolErrorKind::SpammerExceededCapacity(_) | PoolErrorKind::DiscardedOnInsert => {
1057 Self::TxPoolOverflow
1058 }
1059 PoolErrorKind::InvalidTransaction(err) => err.into(),
1060 PoolErrorKind::Other(err) => Self::Other(err),
1061 PoolErrorKind::AlreadyImported => Self::AlreadyKnown,
1062 PoolErrorKind::ExistingConflictingTransactionType(_, _) => Self::AddressAlreadyReserved,
1063 }
1064 }
1065}
1066
1067impl From<InvalidPoolTransactionError> for RpcPoolError {
1068 fn from(err: InvalidPoolTransactionError) -> Self {
1069 match err {
1070 InvalidPoolTransactionError::Consensus(err) => Self::Invalid(err.into()),
1071 InvalidPoolTransactionError::ExceedsGasLimit(_, _) => Self::ExceedsGasLimit,
1072 InvalidPoolTransactionError::MaxTxGasLimitExceeded(_, _) => Self::MaxTxGasLimitExceeded,
1073 InvalidPoolTransactionError::ExceedsFeeCap { max_tx_fee_wei, tx_fee_cap_wei } => {
1074 Self::ExceedsFeeCap { max_tx_fee_wei, tx_fee_cap_wei }
1075 }
1076 InvalidPoolTransactionError::ExceedsMaxInitCodeSize(_, _) => {
1077 Self::ExceedsMaxInitCodeSize
1078 }
1079 InvalidPoolTransactionError::IntrinsicGasTooLow => {
1080 Self::Invalid(RpcInvalidTransactionError::GasTooLow)
1081 }
1082 InvalidPoolTransactionError::OversizedData { size, limit } => {
1083 Self::OversizedData { size, limit }
1084 }
1085 InvalidPoolTransactionError::Underpriced => Self::Underpriced,
1086 InvalidPoolTransactionError::Eip2681 => {
1087 Self::Invalid(RpcInvalidTransactionError::NonceMaxValue)
1088 }
1089 InvalidPoolTransactionError::Other(err) => Self::PoolTransactionError(err),
1090 InvalidPoolTransactionError::Eip4844(err) => Self::Eip4844(err),
1091 InvalidPoolTransactionError::Eip7702(err) => Self::Eip7702(err),
1092 InvalidPoolTransactionError::Overdraft { cost, balance } => {
1093 Self::Invalid(RpcInvalidTransactionError::InsufficientFunds { cost, balance })
1094 }
1095 InvalidPoolTransactionError::PriorityFeeBelowMinimum { minimum_priority_fee } => {
1096 Self::Invalid(RpcInvalidTransactionError::PriorityFeeBelowMinimum {
1097 minimum_priority_fee,
1098 })
1099 }
1100 }
1101 }
1102}
1103
1104impl From<PoolError> for EthApiError {
1105 fn from(err: PoolError) -> Self {
1106 Self::PoolError(RpcPoolError::from(err))
1107 }
1108}
1109
1110#[derive(Debug, thiserror::Error)]
1112pub enum SignError {
1113 #[error("could not sign")]
1115 CouldNotSign,
1116 #[error("unknown account")]
1118 NoAccount,
1119 #[error("given typed data is not valid")]
1121 InvalidTypedData,
1122 #[error("invalid transaction request")]
1124 InvalidTransactionRequest,
1125 #[error("no chainid")]
1127 NoChainId,
1128}
1129
1130#[cfg(test)]
1131mod tests {
1132 use super::*;
1133 use alloy_sol_types::{Revert, SolError};
1134 use revm::primitives::b256;
1135
1136 #[test]
1137 fn timed_out_error() {
1138 let err = EthApiError::ExecutionTimedOut(Duration::from_secs(10));
1139 assert_eq!(err.to_string(), "execution aborted (timeout = 10s)");
1140 }
1141
1142 #[test]
1143 fn header_not_found_message() {
1144 let err: jsonrpsee_types::error::ErrorObject<'static> =
1145 EthApiError::HeaderNotFound(BlockId::hash(b256!(
1146 "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1147 )))
1148 .into();
1149 assert_eq!(
1150 err.message(),
1151 "block not found: hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1152 );
1153 let err: jsonrpsee_types::error::ErrorObject<'static> =
1154 EthApiError::HeaderNotFound(BlockId::hash_canonical(b256!(
1155 "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1156 )))
1157 .into();
1158 assert_eq!(
1159 err.message(),
1160 "block not found: canonical hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1161 );
1162 let err: jsonrpsee_types::error::ErrorObject<'static> =
1163 EthApiError::HeaderNotFound(BlockId::number(100000)).into();
1164 assert_eq!(err.message(), "block not found: 0x186a0");
1165 let err: jsonrpsee_types::error::ErrorObject<'static> =
1166 EthApiError::HeaderNotFound(BlockId::latest()).into();
1167 assert_eq!(err.message(), "block not found: latest");
1168 let err: jsonrpsee_types::error::ErrorObject<'static> =
1169 EthApiError::HeaderNotFound(BlockId::safe()).into();
1170 assert_eq!(err.message(), "block not found: safe");
1171 let err: jsonrpsee_types::error::ErrorObject<'static> =
1172 EthApiError::HeaderNotFound(BlockId::finalized()).into();
1173 assert_eq!(err.message(), "block not found: finalized");
1174 }
1175
1176 #[test]
1177 fn receipts_not_found_message() {
1178 let err: jsonrpsee_types::error::ErrorObject<'static> =
1179 EthApiError::ReceiptsNotFound(BlockId::hash(b256!(
1180 "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1181 )))
1182 .into();
1183 assert_eq!(
1184 err.message(),
1185 "block not found: hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1186 );
1187 let err: jsonrpsee_types::error::ErrorObject<'static> =
1188 EthApiError::ReceiptsNotFound(BlockId::hash_canonical(b256!(
1189 "0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1190 )))
1191 .into();
1192 assert_eq!(
1193 err.message(),
1194 "block not found: canonical hash 0x1a15e3c30cf094a99826869517b16d185d45831d3a494f01030b0001a9d3ebb9"
1195 );
1196 let err: jsonrpsee_types::error::ErrorObject<'static> =
1197 EthApiError::ReceiptsNotFound(BlockId::number(100000)).into();
1198 assert_eq!(err.code(), EthRpcErrorCode::ResourceNotFound.code());
1199 assert_eq!(err.message(), "block not found: 0x186a0");
1200 let err: jsonrpsee_types::error::ErrorObject<'static> =
1201 EthApiError::ReceiptsNotFound(BlockId::latest()).into();
1202 assert_eq!(err.message(), "block not found: latest");
1203 let err: jsonrpsee_types::error::ErrorObject<'static> =
1204 EthApiError::ReceiptsNotFound(BlockId::safe()).into();
1205 assert_eq!(err.message(), "block not found: safe");
1206 let err: jsonrpsee_types::error::ErrorObject<'static> =
1207 EthApiError::ReceiptsNotFound(BlockId::finalized()).into();
1208 assert_eq!(err.message(), "block not found: finalized");
1209 let err: jsonrpsee_types::error::ErrorObject<'static> =
1210 EthApiError::ReceiptsNotFound(BlockId::pending()).into();
1211 assert_eq!(err.message(), "block not found: pending");
1212 let err: jsonrpsee_types::error::ErrorObject<'static> =
1213 EthApiError::ReceiptsNotFound(BlockId::earliest()).into();
1214 assert_eq!(err.message(), "block not found: earliest");
1215 }
1216
1217 #[test]
1218 fn revert_err_display() {
1219 let revert = Revert::from("test_revert_reason");
1220 let err = RevertError::new(revert.abi_encode().into());
1221 let msg = err.to_string();
1222 assert_eq!(msg, "execution reverted: test_revert_reason");
1223 }
1224}