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}
127
128impl From<eyre::Report> for BlockchainError {
129 fn from(err: eyre::Report) -> Self {
130 Self::Message(err.to_string())
131 }
132}
133
134impl From<RpcError> for BlockchainError {
135 fn from(err: RpcError) -> Self {
136 Self::RpcError(err)
137 }
138}
139
140impl<T> From<EVMError<T>> for BlockchainError
141where
142 T: Into<Self>,
143{
144 fn from(err: EVMError<T>) -> Self {
145 match err {
146 EVMError::Transaction(err) => InvalidTransactionError::from(err).into(),
147 EVMError::Header(err) => match err {
148 InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet,
149 InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet,
150 },
151 EVMError::Database(err) => err.into(),
152 EVMError::Custom(err) => Self::Message(err),
153 }
154 }
155}
156
157impl<T> From<EVMError<T, OpTransactionError>> for BlockchainError
158where
159 T: Into<Self>,
160{
161 fn from(err: EVMError<T, OpTransactionError>) -> Self {
162 match err {
163 EVMError::Transaction(err) => match err {
164 OpTransactionError::Base(err) => InvalidTransactionError::from(err).into(),
165 OpTransactionError::DepositSystemTxPostRegolith => {
166 Self::DepositTransactionUnsupported
167 }
168 OpTransactionError::HaltedDepositPostRegolith => {
169 Self::DepositTransactionUnsupported
170 }
171 OpTransactionError::MissingEnvelopedTx => Self::InvalidTransaction(err.into()),
172 },
173 EVMError::Header(err) => match err {
174 InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet,
175 InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet,
176 },
177 EVMError::Database(err) => err.into(),
178 EVMError::Custom(err) => Self::Message(err),
179 }
180 }
181}
182
183impl From<WalletError> for BlockchainError {
184 fn from(value: WalletError) -> Self {
185 Self::Message(value.to_string())
186 }
187}
188
189impl<E> From<StateOverrideError<E>> for BlockchainError
190where
191 E: Into<Self>,
192{
193 fn from(value: StateOverrideError<E>) -> Self {
194 match value {
195 StateOverrideError::InvalidBytecode(err) => Self::StateOverrideError(err.to_string()),
196 StateOverrideError::BothStateAndStateDiff(addr) => Self::StateOverrideError(format!(
197 "state and state_diff can't be used together for account {addr}",
198 )),
199 StateOverrideError::Database(err) => err.into(),
200 }
201 }
202}
203
204#[derive(Debug, thiserror::Error)]
206pub enum PoolError {
207 #[error("Transaction with cyclic dependent transactions")]
208 CyclicTransaction,
209 #[error("Tx: [{0:?}] insufficient gas price to replace existing transaction")]
211 ReplacementUnderpriced(Box<PoolTransaction>),
212 #[error("Tx: [{0:?}] already Imported")]
213 AlreadyImported(Box<PoolTransaction>),
214}
215
216#[derive(Debug, thiserror::Error)]
218pub enum FeeHistoryError {
219 #[error("requested block range is out of bounds")]
220 InvalidBlockRange,
221 #[error("could not find newest block number requested: {0}")]
222 BlockNotFound(BlockNumberOrTag),
223}
224
225#[derive(Debug)]
226pub struct ErrDetail {
227 pub detail: String,
228}
229
230#[derive(Debug, thiserror::Error)]
232pub enum InvalidTransactionError {
233 #[error("nonce too low")]
235 NonceTooLow,
236 #[error("Nonce too high")]
239 NonceTooHigh,
240 #[error("nonce has max value")]
243 NonceMaxValue,
244 #[error("insufficient funds for transfer")]
246 InsufficientFundsForTransfer,
247 #[error("max initcode size exceeded")]
249 MaxInitCodeSizeExceeded,
250 #[error("Insufficient funds for gas * price + value")]
252 InsufficientFunds,
253 #[error("gas uint64 overflow")]
255 GasUintOverflow,
256 #[error("intrinsic gas too low")]
259 GasTooLow,
260 #[error("intrinsic gas too high -- {}",.0.detail)]
262 GasTooHigh(ErrDetail),
263 #[error("max priority fee per gas higher than max fee per gas")]
266 TipAboveFeeCap,
267 #[error("max fee per gas less than block base fee")]
269 FeeCapTooLow,
270 #[error("Out of gas: gas required exceeds allowance: {0:?}")]
272 BasicOutOfGas(u128),
273 #[error("execution reverted: {0:?}")]
275 Revert(Option<Bytes>),
276 #[error("sender not an eoa")]
278 SenderNoEOA,
279 #[error("invalid chain id for signer")]
281 InvalidChainId,
282 #[error("Incompatible EIP-155 transaction, signed for another chain")]
284 IncompatibleEIP155,
285 #[error("Access lists are not supported before the Berlin hardfork")]
287 AccessListNotSupported,
288 #[error("Block `blob_gas_price` is greater than tx-specified `max_fee_per_blob_gas`")]
291 BlobFeeCapTooLow(u128, u128),
292 #[error("Block `blob_versioned_hashes` is not supported before the Cancun hardfork")]
295 BlobVersionedHashesNotSupported,
296 #[error("`max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.")]
298 MaxFeePerBlobGasNotSupported,
299 #[error("`blob_hashes` are required for EIP-4844 transactions")]
301 NoBlobHashes,
302 #[error("too many blobs in one transaction, have: {0}, max: {1}")]
303 TooManyBlobs(usize, usize),
304 #[error(transparent)]
306 BlobTransactionValidationError(#[from] alloy_consensus::BlobTransactionValidationError),
307 #[error("Blob transaction can't be a create transaction. `to` must be present.")]
309 BlobCreateTransaction,
310 #[error("Blob transaction contains a versioned hash with an incorrect version")]
312 BlobVersionNotSupported,
313 #[error("There should be at least one blob in a Blob transaction.")]
315 EmptyBlobs,
316 #[error("EIP-7702 authorization lists are not supported before the Prague hardfork")]
318 AuthorizationListNotSupported,
319 #[error("Transaction gas limit is greater than the block gas limit, gas_limit: {0}, cap: {1}")]
320 TxGasLimitGreaterThanCap(u64, u64),
321 #[error(transparent)]
323 Revm(revm::context_interface::result::InvalidTransaction),
324 #[error("op-deposit failure post regolith")]
326 DepositTxErrorPostRegolith,
327 #[error("missing enveloped transaction")]
329 MissingEnvelopedTx,
330}
331
332impl From<InvalidTransaction> for InvalidTransactionError {
333 fn from(err: InvalidTransaction) -> Self {
334 match err {
335 InvalidTransaction::InvalidChainId => Self::InvalidChainId,
336 InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap,
337 InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow,
338 InvalidTransaction::CallerGasLimitMoreThanBlock => {
339 Self::GasTooHigh(ErrDetail { detail: String::from("CallerGasLimitMoreThanBlock") })
340 }
341 InvalidTransaction::CallGasCostMoreThanGasLimit { .. } => {
342 Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") })
343 }
344 InvalidTransaction::GasFloorMoreThanGasLimit { .. } => {
345 Self::GasTooHigh(ErrDetail { detail: String::from("GasFloorMoreThanGasLimit") })
346 }
347 InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA,
348 InvalidTransaction::LackOfFundForMaxFee { .. } => Self::InsufficientFunds,
349 InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow,
350 InvalidTransaction::NonceOverflowInTransaction => Self::NonceMaxValue,
351 InvalidTransaction::CreateInitCodeSizeLimit => Self::MaxInitCodeSizeExceeded,
352 InvalidTransaction::NonceTooHigh { .. } => Self::NonceTooHigh,
353 InvalidTransaction::NonceTooLow { .. } => Self::NonceTooLow,
354 InvalidTransaction::AccessListNotSupported => Self::AccessListNotSupported,
355 InvalidTransaction::BlobGasPriceGreaterThanMax {
356 block_blob_gas_price,
357 tx_max_fee_per_blob_gas,
358 } => Self::BlobFeeCapTooLow(block_blob_gas_price, tx_max_fee_per_blob_gas),
359 InvalidTransaction::BlobVersionedHashesNotSupported => {
360 Self::BlobVersionedHashesNotSupported
361 }
362 InvalidTransaction::MaxFeePerBlobGasNotSupported => Self::MaxFeePerBlobGasNotSupported,
363 InvalidTransaction::BlobCreateTransaction => Self::BlobCreateTransaction,
364 InvalidTransaction::BlobVersionNotSupported => Self::BlobVersionNotSupported,
365 InvalidTransaction::EmptyBlobs => Self::EmptyBlobs,
366 InvalidTransaction::TooManyBlobs { have, max } => Self::TooManyBlobs(have, max),
367 InvalidTransaction::AuthorizationListNotSupported => {
368 Self::AuthorizationListNotSupported
369 }
370 InvalidTransaction::TxGasLimitGreaterThanCap { gas_limit, cap } => {
371 Self::TxGasLimitGreaterThanCap(gas_limit, cap)
372 }
373
374 InvalidTransaction::AuthorizationListInvalidFields
375 | InvalidTransaction::Eip1559NotSupported
376 | InvalidTransaction::Eip2930NotSupported
377 | InvalidTransaction::Eip4844NotSupported
378 | InvalidTransaction::Eip7702NotSupported
379 | InvalidTransaction::EmptyAuthorizationList
380 | InvalidTransaction::Eip7873NotSupported
381 | InvalidTransaction::Eip7873MissingTarget
382 | InvalidTransaction::MissingChainId
383 | InvalidTransaction::Str(_) => Self::Revm(err),
384 }
385 }
386}
387
388impl From<OpTransactionError> for InvalidTransactionError {
389 fn from(value: OpTransactionError) -> Self {
390 match value {
391 OpTransactionError::Base(err) => err.into(),
392 OpTransactionError::DepositSystemTxPostRegolith
393 | OpTransactionError::HaltedDepositPostRegolith => Self::DepositTxErrorPostRegolith,
394 OpTransactionError::MissingEnvelopedTx => Self::MissingEnvelopedTx,
395 }
396 }
397}
398pub(crate) trait ToRpcResponseResult {
400 fn to_rpc_result(self) -> ResponseResult;
401}
402
403pub fn to_rpc_result<T: Serialize>(val: T) -> ResponseResult {
405 match serde_json::to_value(val) {
406 Ok(success) => ResponseResult::Success(success),
407 Err(err) => {
408 error!(%err, "Failed serialize rpc response");
409 ResponseResult::error(RpcError::internal_error())
410 }
411 }
412}
413
414impl<T: Serialize> ToRpcResponseResult for Result<T> {
415 fn to_rpc_result(self) -> ResponseResult {
416 match self {
417 Ok(val) => to_rpc_result(val),
418 Err(err) => match err {
419 BlockchainError::Pool(err) => {
420 error!(%err, "txpool error");
421 match err {
422 PoolError::CyclicTransaction => {
423 RpcError::transaction_rejected("Cyclic transaction detected")
424 }
425 PoolError::ReplacementUnderpriced(_) => {
426 RpcError::transaction_rejected("replacement transaction underpriced")
427 }
428 PoolError::AlreadyImported(_) => {
429 RpcError::transaction_rejected("transaction already imported")
430 }
431 }
432 }
433 BlockchainError::NoSignerAvailable => {
434 RpcError::invalid_params("No Signer available")
435 }
436 BlockchainError::ChainIdNotAvailable => {
437 RpcError::invalid_params("Chain Id not available")
438 }
439 BlockchainError::TransactionConfirmationTimeout { .. } => {
440 RpcError::internal_error_with("Transaction confirmation timeout")
441 }
442 BlockchainError::InvalidTransaction(err) => match err {
443 InvalidTransactionError::Revert(data) => {
444 let mut msg = "execution reverted".to_string();
446 if let Some(reason) = data
447 .as_ref()
448 .and_then(|data| RevertDecoder::new().maybe_decode(data, None))
449 {
450 msg = format!("{msg}: {reason}");
451 }
452 RpcError {
453 code: ErrorCode::ExecutionError,
455 message: msg.into(),
456 data: serde_json::to_value(data).ok(),
457 }
458 }
459 InvalidTransactionError::GasTooLow => {
460 RpcError {
462 code: ErrorCode::ServerError(-32000),
463 message: err.to_string().into(),
464 data: None,
465 }
466 }
467 InvalidTransactionError::GasTooHigh(_) => {
468 RpcError {
470 code: ErrorCode::ServerError(-32000),
471 message: err.to_string().into(),
472 data: None,
473 }
474 }
475 _ => RpcError::transaction_rejected(err.to_string()),
476 },
477 BlockchainError::FeeHistory(err) => RpcError::invalid_params(err.to_string()),
478 BlockchainError::EmptyRawTransactionData => {
479 RpcError::invalid_params("Empty transaction data")
480 }
481 BlockchainError::FailedToDecodeSignedTransaction => {
482 RpcError::invalid_params("Failed to decode transaction")
483 }
484 BlockchainError::FailedToDecodeTransaction => {
485 RpcError::invalid_params("Failed to decode transaction")
486 }
487 BlockchainError::FailedToDecodeReceipt => {
488 RpcError::invalid_params("Failed to decode receipt")
489 }
490 BlockchainError::FailedToDecodeStateDump => {
491 RpcError::invalid_params("Failed to decode state dump")
492 }
493 BlockchainError::SignerError(err) => RpcError::invalid_params(err.to_string()),
494 BlockchainError::SignatureError(err) => RpcError::invalid_params(err.to_string()),
495 BlockchainError::RpcUnimplemented => {
496 RpcError::internal_error_with("Not implemented")
497 }
498 BlockchainError::PrevrandaoNotSet => RpcError::internal_error_with(err.to_string()),
499 BlockchainError::RpcError(err) => err,
500 BlockchainError::InvalidFeeInput => RpcError::invalid_params(
501 "Invalid input: `max_priority_fee_per_gas` greater than `max_fee_per_gas`",
502 ),
503 BlockchainError::AlloyForkProvider(err) => {
504 error!(target: "backend", %err, "fork provider error");
505 match err {
506 TransportError::ErrorResp(err) => RpcError {
507 code: ErrorCode::from(err.code),
508 message: err.message,
509 data: err.data.and_then(|data| serde_json::to_value(data).ok()),
510 },
511 err => RpcError::internal_error_with(format!("Fork Error: {err:?}")),
512 }
513 }
514 err @ BlockchainError::EvmError(_) => {
515 RpcError::internal_error_with(err.to_string())
516 }
517 err @ BlockchainError::EvmOverrideError(_) => {
518 RpcError::invalid_params(err.to_string())
519 }
520 err @ BlockchainError::InvalidUrl(_) => RpcError::invalid_params(err.to_string()),
521 BlockchainError::Internal(err) => RpcError::internal_error_with(err),
522 err @ BlockchainError::BlockOutOfRange(_, _) => {
523 RpcError::invalid_params(err.to_string())
524 }
525 err @ BlockchainError::BlockNotFound => RpcError {
526 code: ErrorCode::ServerError(-32001),
528 message: err.to_string().into(),
529 data: None,
530 },
531 err @ BlockchainError::TransactionNotFound => RpcError {
532 code: ErrorCode::ServerError(-32001),
533 message: err.to_string().into(),
534 data: None,
535 },
536 err @ BlockchainError::DataUnavailable => {
537 RpcError::internal_error_with(err.to_string())
538 }
539 err @ BlockchainError::TrieError(_) => {
540 RpcError::internal_error_with(err.to_string())
541 }
542 BlockchainError::UintConversion(err) => RpcError::invalid_params(err),
543 err @ BlockchainError::StateOverrideError(_) => {
544 RpcError::invalid_params(err.to_string())
545 }
546 err @ BlockchainError::TimestampError(_) => {
547 RpcError::invalid_params(err.to_string())
548 }
549 BlockchainError::DatabaseError(err) => {
550 RpcError::internal_error_with(err.to_string())
551 }
552 err @ BlockchainError::EIP1559TransactionUnsupportedAtHardfork => {
553 RpcError::invalid_params(err.to_string())
554 }
555 err @ BlockchainError::EIP2930TransactionUnsupportedAtHardfork => {
556 RpcError::invalid_params(err.to_string())
557 }
558 err @ BlockchainError::EIP4844TransactionUnsupportedAtHardfork => {
559 RpcError::invalid_params(err.to_string())
560 }
561 err @ BlockchainError::EIP7702TransactionUnsupportedAtHardfork => {
562 RpcError::invalid_params(err.to_string())
563 }
564 err @ BlockchainError::DepositTransactionUnsupported => {
565 RpcError::invalid_params(err.to_string())
566 }
567 err @ BlockchainError::ExcessBlobGasNotSet => {
568 RpcError::invalid_params(err.to_string())
569 }
570 err @ BlockchainError::Message(_) => RpcError::internal_error_with(err.to_string()),
571 err @ BlockchainError::UnknownTransactionType => {
572 RpcError::invalid_params(err.to_string())
573 }
574 err @ BlockchainError::InvalidTransactionRequest(_) => {
575 RpcError::invalid_params(err.to_string())
576 }
577 err @ BlockchainError::RecoveryError(_) => {
578 RpcError::invalid_params(err.to_string())
579 }
580 }
581 .into(),
582 }
583 }
584}