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