1use crate::error::jsonrpc_error_helpers::*;
6use alloy_primitives::{hex, Address, Bytes};
7use alloy_rpc_types::error::EthRpcErrorCode;
8use alloy_sol_types::decode_revert_reason;
9use jsonrpc_core::{Error as JsonRpcError, ErrorCode};
10use jsonrpsee::types::ErrorObjectOwned;
11use revm_context_interface::result::{HaltReason, OutOfGasError};
12use std::time::Duration;
13
14pub type EthResult<T> = Result<T, EthApiError>;
18
19#[derive(Debug, thiserror::Error)]
21pub enum EthApiError {
22 #[error("empty transaction data")]
24 EmptyRawTransactionData,
25 #[error("failed to decode signed transaction")]
27 FailedToDecodeSignedTransaction,
28 #[error("invalid transaction signature")]
30 InvalidTransactionSignature,
31 #[error(transparent)]
33 PoolError(RpcPoolError),
34 #[error("unknown block number")]
36 UnknownBlockNumber,
37 #[error("unknown block")]
45 UnknownSafeOrFinalizedBlock,
46 #[error("unknown block or tx index")]
48 UnknownBlockOrTxIndex,
49 #[error("invalid block range")]
51 InvalidBlockRange,
52 #[error("prevrandao not in the EVM's environment after merge")]
54 PrevrandaoNotSet,
55 #[error("excess blob gas missing in the EVM's environment after Cancun")]
57 ExcessBlobGasNotSet,
58 #[error(
62 "both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"
63 )]
64 ConflictingFeeFieldsInRequest,
65 #[error(transparent)]
67 InvalidTransaction(#[from] RpcInvalidTransactionError),
68 #[error("account {0:?} has both 'state' and 'stateDiff'")]
74 BothStateAndStateDiffInOverride(Address),
75 #[error("transaction not found")]
83 TransactionNotFound,
84 #[error("unsupported")]
86 Unsupported(&'static str),
87 #[error("{0}")]
89 InvalidParams(String),
90 #[error("invalid tracer config")]
92 InvalidTracerConfig,
93 #[error("invalid reward percentiles")]
95 InvalidRewardPercentiles,
96 #[error("internal blocking task error")]
102 InternalBlockingTaskError,
103 #[error("internal eth error")]
106 InternalEthError,
107 #[error("execution aborted (timeout = {0:?})")]
109 ExecutionTimedOut(Duration),
110 #[error("{0}")]
112 InternalJsTracerError(String),
113 #[error(transparent)]
116 TransactionInputError(#[from] TransactionInputError),
117 #[error("Revm error: {0}")]
119 EvmCustom(String),
120 #[error("Transaction conversion error")]
122 TransactionConversionError,
123 #[error("{0}")]
128 Other(String),
129}
130
131impl From<EthApiError> for JsonRpcError {
132 fn from(error: EthApiError) -> Self {
133 match error {
134 EthApiError::FailedToDecodeSignedTransaction |
135 EthApiError::InvalidTransactionSignature |
136 EthApiError::EmptyRawTransactionData |
137 EthApiError::InvalidBlockRange |
138 EthApiError::ConflictingFeeFieldsInRequest |
139 EthApiError::BothStateAndStateDiffInOverride(_) |
141 EthApiError::InvalidTracerConfig |
142 EthApiError::TransactionConversionError => invalid_params_rpc_err(error.to_string()),
143 EthApiError::InvalidTransaction(err) => err.into(),
144 EthApiError::PoolError(err) => err.into(),
145 EthApiError::PrevrandaoNotSet |
146 EthApiError::ExcessBlobGasNotSet |
147 EthApiError::TransactionNotFound |
150 EthApiError::EvmCustom(_) |
151 EthApiError::InvalidRewardPercentiles => internal_rpc_err(error.to_string()),
152 EthApiError::UnknownBlockNumber | EthApiError::UnknownBlockOrTxIndex => {
153 build_rpc_server_error(EthRpcErrorCode::ResourceNotFound.code() as i64, error.to_string())
154 }
155 EthApiError::UnknownSafeOrFinalizedBlock => {
156 build_rpc_server_error(EthRpcErrorCode::UnknownBlock.code() as i64, error.to_string())
157 }
158 EthApiError::Unsupported(msg) => internal_rpc_err(msg),
159 EthApiError::InternalJsTracerError(msg) => internal_rpc_err(msg),
160 EthApiError::InvalidParams(msg) => invalid_params_rpc_err(msg),
161 err @ EthApiError::ExecutionTimedOut(_) => {
162 build_rpc_server_error(-32000, err.to_string()) }
164 err @ EthApiError::InternalBlockingTaskError | err @ EthApiError::InternalEthError => {
165 internal_rpc_err(err.to_string())
166 }
167 err @ EthApiError::TransactionInputError(_) => invalid_params_rpc_err(err.to_string()),
168 EthApiError::Other(err) => internal_rpc_err(err),
169 }
171 }
172}
173
174impl From<EthApiError> for ErrorObjectOwned {
175 fn from(e: EthApiError) -> Self {
176 let err = JsonRpcError::from(e);
177 ErrorObjectOwned::owned(err.code.code() as i32, err.message, err.data)
178 }
179}
180
181#[derive(thiserror::Error, Debug)]
197pub enum RpcInvalidTransactionError {
198 #[error("nonce too low")]
201 NonceTooLow,
202 #[error("nonce too high")]
205 NonceTooHigh,
206 #[error("nonce has max value")]
209 NonceMaxValue,
210 #[error("insufficient funds for transfer")]
213 InsufficientFundsForTransfer,
214 #[error("max initcode size exceeded")]
217 MaxInitCodeSizeExceeded,
218 #[error("insufficient funds for gas * price + value")]
221 InsufficientFunds,
222 #[error("gas uint64 overflow")]
224 GasUintOverflow,
225 #[error("intrinsic gas too low")]
228 GasTooLow,
229 #[error("intrinsic gas too high")]
231 GasTooHigh,
232 #[error("transaction type not supported")]
235 TxTypeNotSupported,
236 #[error("max priority fee per gas higher than max fee per gas")]
239 TipAboveFeeCap,
240 #[error("max priority fee per gas higher than 2^256-1")]
242 TipVeryHigh,
243 #[error("max fee per gas higher than 2^256-1")]
245 FeeCapVeryHigh,
246 #[error("max fee per gas less than block base fee")]
249 FeeCapTooLow,
250 #[error("sender is not an EOA")]
252 SenderNoEOA,
253 #[error("out of gas: gas required exceeds allowance: {0}")]
256 BasicOutOfGas(u64),
257 #[error("out of gas: gas exhausted during memory expansion: {0}")]
260 MemoryOutOfGas(u64),
261 #[error(
264 "out of gas: gas exhausted during precompiled contract execution: {0}"
265 )]
266 PrecompileOutOfGas(u64),
267 #[error("out of gas: invalid operand to an opcode; {0}")]
270 InvalidOperandOutOfGas(u64),
271 #[error(transparent)]
273 Revert(RevertError),
274 #[error("EVM error: {0:?}")]
276 EvmHalt(HaltReason),
277 #[error("invalid chain ID")]
279 InvalidChainId,
280 #[error("transactions before Spurious Dragon should not have a chain ID")]
282 OldLegacyChainId,
283 #[error("transactions before Berlin should not have access list")]
285 AccessListNotSupported,
286 #[error("max_fee_per_blob_gas is not supported for blocks before the Cancun hardfork")]
289 MaxFeePerBlobGasNotSupported,
290 #[error("blob_versioned_hashes is not supported for blocks before the Cancun hardfork")]
293 BlobVersionedHashesNotSupported,
294 #[error("max fee per blob gas less than block blob gas fee")]
297 BlobFeeCapTooLow,
298 #[error("blob hash version mismatch")]
300 BlobHashVersionMismatch,
301 #[error("blob transaction missing blob hashes")]
303 BlobTransactionMissingBlobHashes,
304 #[error(
306 "blob transaction exceeds max blobs per block; got {have}, max {max}"
307 )]
308 TooManyBlobs {
309 max: usize,
311 have: usize,
313 },
314 #[error("blob transaction is a create transaction")]
316 BlobTransactionIsCreate,
317 #[error("empty authorization list")]
319 EmptyAuthorizationList,
320 #[error("blob transaction is a create transaction")]
322 PriortyGreaterThanMaxFee,
323}
324
325impl RpcInvalidTransactionError {
326 const fn error_code(&self) -> i32 {
328 match self {
329 Self::InvalidChainId | Self::GasTooLow | Self::GasTooHigh => {
330 EthRpcErrorCode::InvalidInput.code()
331 }
332 Self::Revert(_) => EthRpcErrorCode::ExecutionError.code(),
333 _ => EthRpcErrorCode::TransactionRejected.code(),
334 }
335 }
336
337 #[allow(dead_code)]
342 pub(crate) fn halt(reason: HaltReason, gas_limit: u64) -> Self {
343 match reason {
344 HaltReason::OutOfGas(err) => Self::out_of_gas(err, gas_limit),
345 HaltReason::NonceOverflow => Self::NonceMaxValue,
346 err => Self::EvmHalt(err),
347 }
348 }
349
350 #[allow(dead_code)]
352 pub(crate) const fn out_of_gas(
353 reason: OutOfGasError, gas_limit: u64,
354 ) -> Self {
355 match reason {
356 OutOfGasError::Basic | OutOfGasError::ReentrancySentry => {
357 Self::BasicOutOfGas(gas_limit)
358 }
359 OutOfGasError::Memory | OutOfGasError::MemoryLimit => {
360 Self::MemoryOutOfGas(gas_limit)
361 }
362 OutOfGasError::Precompile => Self::PrecompileOutOfGas(gas_limit),
363 OutOfGasError::InvalidOperand => {
364 Self::InvalidOperandOutOfGas(gas_limit)
365 }
366 }
367 }
368}
369
370impl From<RpcInvalidTransactionError> for JsonRpcError {
371 fn from(e: RpcInvalidTransactionError) -> Self {
372 match e {
373 RpcInvalidTransactionError::Revert(revert) => JsonRpcError {
374 code: ErrorCode::ServerError(revert.error_code() as i64),
375 message: revert.to_string(),
376 data: revert.output.as_ref().map(|out| out.as_ref()).map(|v| {
377 serde_json::Value::String(hex::encode_prefixed(v))
378 }),
379 },
380 err => JsonRpcError {
381 code: ErrorCode::ServerError(err.error_code() as i64),
382 message: err.to_string(),
383 data: None,
384 },
385 }
386 }
387}
388
389#[derive(Debug, Default, thiserror::Error)]
391#[error("both \"data\" and \"input\" are set and not equal. Please use \"input\" to pass transaction call data")]
392#[non_exhaustive]
393pub struct TransactionInputError;
394#[derive(Debug, Clone)]
398pub struct RevertError {
399 output: Option<Bytes>,
403}
404
405impl RevertError {
408 pub fn new(output: Bytes) -> Self {
412 if output.is_empty() {
413 Self { output: None }
414 } else {
415 Self {
416 output: Some(output),
417 }
418 }
419 }
420
421 const fn error_code(&self) -> i32 { EthRpcErrorCode::ExecutionError.code() }
422}
423
424impl std::fmt::Display for RevertError {
425 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
426 f.write_str("execution reverted")?;
427 if let Some(reason) = self
428 .output
429 .as_ref()
430 .and_then(|bytes| decode_revert_reason(bytes))
431 {
432 write!(f, ": {reason}")?;
433 }
434 Ok(())
435 }
436}
437
438impl std::error::Error for RevertError {}
439
440#[derive(Debug, thiserror::Error)]
443pub enum RpcPoolError {
444 #[error("already known")]
446 AlreadyKnown,
447 #[error("invalid sender")]
449 InvalidSender,
450 #[error("transaction underpriced")]
452 Underpriced,
453 #[error("txpool is full")]
455 TxPoolOverflow,
456 #[error("replacement transaction underpriced")]
458 ReplaceUnderpriced,
459 #[error("exceeds block gas limit")]
461 ExceedsGasLimit,
462 #[error("negative value")]
464 NegativeValue,
465 #[error("oversized data")]
467 OversizedData,
468 #[error("max initcode size exceeded")]
470 ExceedsMaxInitCodeSize,
471 #[error(transparent)]
473 Invalid(#[from] RpcInvalidTransactionError),
474 #[error("address already reserved")]
485 AddressAlreadyReserved,
486 #[error(transparent)]
488 Other(Box<dyn std::error::Error + Send + Sync>),
489}
490
491impl From<RpcPoolError> for JsonRpcError {
492 fn from(e: RpcPoolError) -> Self {
493 match e {
494 RpcPoolError::Invalid(err) => err.into(),
495 error => JsonRpcError {
496 code: ErrorCode::InternalError,
497 message: error.to_string(),
498 data: None,
499 },
500 }
501 }
502}