1use crate::error::jsonrpsee_error_helpers::{
6 internal_error_with_msg, invalid_params_rpc_err, rpc_err,
7 rpc_error_with_code,
8};
9use alloy_primitives::{hex, Address, Bytes};
10use alloy_rpc_types::error::EthRpcErrorCode;
11use alloy_sol_types::decode_revert_reason;
12use jsonrpsee::types::ErrorObjectOwned;
13use revm_context_interface::result::{HaltReason, OutOfGasError};
14use std::time::Duration;
15
16pub type EthResult<T> = Result<T, EthApiError>;
20
21#[derive(Debug, thiserror::Error)]
23pub enum EthApiError {
24 #[error("empty transaction data")]
26 EmptyRawTransactionData,
27 #[error("failed to decode signed transaction")]
29 FailedToDecodeSignedTransaction,
30 #[error("invalid transaction signature")]
32 InvalidTransactionSignature,
33 #[error(transparent)]
35 PoolError(RpcPoolError),
36 #[error("unknown block number")]
38 UnknownBlockNumber,
39 #[error("unknown block")]
47 UnknownSafeOrFinalizedBlock,
48 #[error("unknown block or tx index")]
50 UnknownBlockOrTxIndex,
51 #[error("invalid block range")]
53 InvalidBlockRange,
54 #[error("prevrandao not in the EVM's environment after merge")]
56 PrevrandaoNotSet,
57 #[error("excess blob gas missing in the EVM's environment after Cancun")]
59 ExcessBlobGasNotSet,
60 #[error(
64 "both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"
65 )]
66 ConflictingFeeFieldsInRequest,
67 #[error(transparent)]
69 InvalidTransaction(#[from] RpcInvalidTransactionError),
70 #[error("account {0:?} has both 'state' and 'stateDiff'")]
76 BothStateAndStateDiffInOverride(Address),
77 #[error("transaction not found")]
85 TransactionNotFound,
86 #[error("unsupported")]
88 Unsupported(&'static str),
89 #[error("{0}")]
91 InvalidParams(String),
92 #[error("invalid tracer config")]
94 InvalidTracerConfig,
95 #[error("invalid reward percentiles")]
97 InvalidRewardPercentiles,
98 #[error("internal blocking task error")]
104 InternalBlockingTaskError,
105 #[error("internal eth error")]
108 InternalEthError,
109 #[error("execution aborted (timeout = {0:?})")]
111 ExecutionTimedOut(Duration),
112 #[error("{0}")]
114 InternalJsTracerError(String),
115 #[error(transparent)]
118 TransactionInputError(#[from] TransactionInputError),
119 #[error("Revm error: {0}")]
121 EvmCustom(String),
122 #[error("Transaction conversion error")]
124 TransactionConversionError,
125 #[error("{0}")]
130 Other(String),
131}
132
133impl From<EthApiError> for ErrorObjectOwned {
134 fn from(error: EthApiError) -> Self {
135 match error {
136 EthApiError::FailedToDecodeSignedTransaction |
137 EthApiError::InvalidTransactionSignature |
138 EthApiError::EmptyRawTransactionData |
139 EthApiError::InvalidBlockRange |
140 EthApiError::ConflictingFeeFieldsInRequest |
141 EthApiError::BothStateAndStateDiffInOverride(_) |
143 EthApiError::InvalidTracerConfig |
144 EthApiError::TransactionConversionError => invalid_params_rpc_err(&error.to_string(), None::<()>),
145 EthApiError::InvalidTransaction(err) => err.into(),
146 EthApiError::PoolError(err) => err.into(),
147 EthApiError::PrevrandaoNotSet |
148 EthApiError::ExcessBlobGasNotSet |
149 EthApiError::TransactionNotFound |
152 EthApiError::EvmCustom(_) |
153 EthApiError::InvalidRewardPercentiles => internal_error_with_msg(error.to_string()),
154 EthApiError::UnknownBlockNumber | EthApiError::UnknownBlockOrTxIndex => {
155 rpc_error_with_code(EthRpcErrorCode::ResourceNotFound.code(), error.to_string())
156 }
157 EthApiError::UnknownSafeOrFinalizedBlock => {
158 rpc_error_with_code(EthRpcErrorCode::UnknownBlock.code(), error.to_string())
159 }
160 EthApiError::Unsupported(msg) => internal_error_with_msg(msg.into()),
161 EthApiError::InternalJsTracerError(msg) => internal_error_with_msg(msg),
162 EthApiError::InvalidParams(msg) => invalid_params_rpc_err(&msg, None::<()>),
163 err @ EthApiError::ExecutionTimedOut(_) => {
164 rpc_error_with_code(-32000, err.to_string()) }
166 err @ EthApiError::InternalBlockingTaskError | err @ EthApiError::InternalEthError => {
167 internal_error_with_msg(err.to_string())
168 }
169 err @ EthApiError::TransactionInputError(_) => invalid_params_rpc_err(&err.to_string(), None::<()>),
170 EthApiError::Other(err) => internal_error_with_msg(err),
171 }
173 }
174}
175
176#[derive(thiserror::Error, Debug)]
192pub enum RpcInvalidTransactionError {
193 #[error("nonce too low")]
196 NonceTooLow,
197 #[error("nonce too high")]
200 NonceTooHigh,
201 #[error("nonce has max value")]
204 NonceMaxValue,
205 #[error("insufficient funds for transfer")]
208 InsufficientFundsForTransfer,
209 #[error("max initcode size exceeded")]
212 MaxInitCodeSizeExceeded,
213 #[error("insufficient funds for gas * price + value")]
216 InsufficientFunds,
217 #[error("gas uint64 overflow")]
219 GasUintOverflow,
220 #[error("intrinsic gas too low")]
223 GasTooLow,
224 #[error("intrinsic gas too high")]
226 GasTooHigh,
227 #[error("transaction type not supported")]
230 TxTypeNotSupported,
231 #[error("max priority fee per gas higher than max fee per gas")]
234 TipAboveFeeCap,
235 #[error("max priority fee per gas higher than 2^256-1")]
237 TipVeryHigh,
238 #[error("max fee per gas higher than 2^256-1")]
240 FeeCapVeryHigh,
241 #[error("max fee per gas less than block base fee")]
244 FeeCapTooLow,
245 #[error("sender is not an EOA")]
247 SenderNoEOA,
248 #[error("out of gas: gas required exceeds allowance: {0}")]
251 BasicOutOfGas(u64),
252 #[error("out of gas: gas exhausted during memory expansion: {0}")]
255 MemoryOutOfGas(u64),
256 #[error(
259 "out of gas: gas exhausted during precompiled contract execution: {0}"
260 )]
261 PrecompileOutOfGas(u64),
262 #[error("out of gas: invalid operand to an opcode; {0}")]
265 InvalidOperandOutOfGas(u64),
266 #[error(transparent)]
268 Revert(RevertError),
269 #[error("EVM error: {0:?}")]
271 EvmHalt(HaltReason),
272 #[error("invalid chain ID")]
274 InvalidChainId,
275 #[error("transactions before Spurious Dragon should not have a chain ID")]
277 OldLegacyChainId,
278 #[error("transactions before Berlin should not have access list")]
280 AccessListNotSupported,
281 #[error("max_fee_per_blob_gas is not supported for blocks before the Cancun hardfork")]
284 MaxFeePerBlobGasNotSupported,
285 #[error("blob_versioned_hashes is not supported for blocks before the Cancun hardfork")]
288 BlobVersionedHashesNotSupported,
289 #[error("max fee per blob gas less than block blob gas fee")]
292 BlobFeeCapTooLow,
293 #[error("blob hash version mismatch")]
295 BlobHashVersionMismatch,
296 #[error("blob transaction missing blob hashes")]
298 BlobTransactionMissingBlobHashes,
299 #[error(
301 "blob transaction exceeds max blobs per block; got {have}, max {max}"
302 )]
303 TooManyBlobs {
304 max: usize,
306 have: usize,
308 },
309 #[error("blob transaction is a create transaction")]
311 BlobTransactionIsCreate,
312 #[error("empty authorization list")]
314 EmptyAuthorizationList,
315 #[error("blob transaction is a create transaction")]
317 PriortyGreaterThanMaxFee,
318}
319
320impl RpcInvalidTransactionError {
321 const fn error_code(&self) -> i32 {
323 match self {
324 Self::InvalidChainId | Self::GasTooLow | Self::GasTooHigh => {
325 EthRpcErrorCode::InvalidInput.code()
326 }
327 Self::Revert(_) => EthRpcErrorCode::ExecutionError.code(),
328 _ => EthRpcErrorCode::TransactionRejected.code(),
329 }
330 }
331
332 #[allow(dead_code)]
337 pub(crate) fn halt(reason: HaltReason, gas_limit: u64) -> Self {
338 match reason {
339 HaltReason::OutOfGas(err) => Self::out_of_gas(err, gas_limit),
340 HaltReason::NonceOverflow => Self::NonceMaxValue,
341 err => Self::EvmHalt(err),
342 }
343 }
344
345 #[allow(dead_code)]
347 pub(crate) const fn out_of_gas(
348 reason: OutOfGasError, gas_limit: u64,
349 ) -> Self {
350 match reason {
351 OutOfGasError::Basic | OutOfGasError::ReentrancySentry => {
352 Self::BasicOutOfGas(gas_limit)
353 }
354 OutOfGasError::Memory | OutOfGasError::MemoryLimit => {
355 Self::MemoryOutOfGas(gas_limit)
356 }
357 OutOfGasError::Precompile => Self::PrecompileOutOfGas(gas_limit),
358 OutOfGasError::InvalidOperand => {
359 Self::InvalidOperandOutOfGas(gas_limit)
360 }
361 }
362 }
363}
364
365impl From<RpcInvalidTransactionError> for ErrorObjectOwned {
366 fn from(e: RpcInvalidTransactionError) -> Self {
367 match e {
368 RpcInvalidTransactionError::Revert(revert) => rpc_err(
369 revert.error_code(),
370 revert.to_string(),
371 revert.output.map(|v| hex::encode_prefixed(v)),
372 ),
373 err => rpc_err(err.error_code(), err.to_string(), None::<()>),
374 }
375 }
376}
377
378#[derive(Debug, Default, thiserror::Error)]
380#[error("both \"data\" and \"input\" are set and not equal. Please use \"input\" to pass transaction call data")]
381#[non_exhaustive]
382pub struct TransactionInputError;
383#[derive(Debug, Clone)]
387pub struct RevertError {
388 output: Option<Bytes>,
392}
393
394impl RevertError {
397 pub fn new(output: Bytes) -> Self {
401 if output.is_empty() {
402 Self { output: None }
403 } else {
404 Self {
405 output: Some(output),
406 }
407 }
408 }
409
410 const fn error_code(&self) -> i32 { EthRpcErrorCode::ExecutionError.code() }
411}
412
413impl std::fmt::Display for RevertError {
414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415 f.write_str("execution reverted")?;
416 if let Some(reason) = self
417 .output
418 .as_ref()
419 .and_then(|bytes| decode_revert_reason(bytes))
420 {
421 write!(f, ": {reason}")?;
422 }
423 Ok(())
424 }
425}
426
427impl std::error::Error for RevertError {}
428
429#[derive(Debug, thiserror::Error)]
432pub enum RpcPoolError {
433 #[error("already known")]
435 AlreadyKnown,
436 #[error("invalid sender")]
438 InvalidSender,
439 #[error("transaction underpriced")]
441 Underpriced,
442 #[error("txpool is full")]
444 TxPoolOverflow,
445 #[error("replacement transaction underpriced")]
447 ReplaceUnderpriced,
448 #[error("exceeds block gas limit")]
450 ExceedsGasLimit,
451 #[error("negative value")]
453 NegativeValue,
454 #[error("oversized data")]
456 OversizedData,
457 #[error("max initcode size exceeded")]
459 ExceedsMaxInitCodeSize,
460 #[error(transparent)]
462 Invalid(#[from] RpcInvalidTransactionError),
463 #[error("address already reserved")]
474 AddressAlreadyReserved,
475 #[error(transparent)]
477 Other(Box<dyn std::error::Error + Send + Sync>),
478}
479
480impl From<RpcPoolError> for ErrorObjectOwned {
481 fn from(e: RpcPoolError) -> Self {
482 match e {
483 RpcPoolError::Invalid(err) => err.into(),
484 error => internal_error_with_msg(error.to_string()),
485 }
486 }
487}