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