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 th 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("Required data unavailable")]
74 DataUnavailable,
75 #[error("Trie error: {0}")]
76 TrieError(String),
77 #[error("{0}")]
78 UintConversion(&'static str),
79 #[error("State override error: {0}")]
80 StateOverrideError(String),
81 #[error("Timestamp error: {0}")]
82 TimestampError(String),
83 #[error(transparent)]
84 DatabaseError(#[from] DatabaseError),
85 #[error(
86 "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."
87 )]
88 EIP1559TransactionUnsupportedAtHardfork,
89 #[error(
90 "Access list received but is not supported by the current hardfork.\n\nYou can use it by running anvil with '--hardfork berlin' or later."
91 )]
92 EIP2930TransactionUnsupportedAtHardfork,
93 #[error(
94 "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."
95 )]
96 EIP4844TransactionUnsupportedAtHardfork,
97 #[error(
98 "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."
99 )]
100 EIP7702TransactionUnsupportedAtHardfork,
101 #[error(
102 "op-stack deposit tx received but is not supported.\n\nYou can use it by running anvil with '--optimism'."
103 )]
104 DepositTransactionUnsupported,
105 #[error("UnknownTransactionType not supported ")]
106 UnknownTransactionType,
107 #[error("Excess blob gas not set.")]
108 ExcessBlobGasNotSet,
109 #[error("{0}")]
110 Message(String),
111 #[error("Transaction {hash} was added to the mempool but wasn't confirmed within {duration:?}")]
112 TransactionConfirmationTimeout {
113 hash: B256,
115 duration: Duration,
117 },
118 #[error("Failed to parse transaction request: missing required fields")]
119 MissingRequiredFields,
120}
121
122impl From<eyre::Report> for BlockchainError {
123 fn from(err: eyre::Report) -> Self {
124 Self::Message(err.to_string())
125 }
126}
127
128impl From<RpcError> for BlockchainError {
129 fn from(err: RpcError) -> Self {
130 Self::RpcError(err)
131 }
132}
133
134impl<T> From<EVMError<T>> for BlockchainError
135where
136 T: Into<Self>,
137{
138 fn from(err: EVMError<T>) -> Self {
139 match err {
140 EVMError::Transaction(err) => InvalidTransactionError::from(err).into(),
141 EVMError::Header(err) => match err {
142 InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet,
143 InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet,
144 },
145 EVMError::Database(err) => err.into(),
146 EVMError::Custom(err) => Self::Message(err),
147 }
148 }
149}
150
151impl<T> From<EVMError<T, OpTransactionError>> for BlockchainError
152where
153 T: Into<Self>,
154{
155 fn from(err: EVMError<T, OpTransactionError>) -> Self {
156 match err {
157 EVMError::Transaction(err) => match err {
158 OpTransactionError::Base(err) => InvalidTransactionError::from(err).into(),
159 OpTransactionError::DepositSystemTxPostRegolith => {
160 Self::DepositTransactionUnsupported
161 }
162 OpTransactionError::HaltedDepositPostRegolith => {
163 Self::DepositTransactionUnsupported
164 }
165 },
166 EVMError::Header(err) => match err {
167 InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet,
168 InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet,
169 },
170 EVMError::Database(err) => err.into(),
171 EVMError::Custom(err) => Self::Message(err),
172 }
173 }
174}
175
176impl From<WalletError> for BlockchainError {
177 fn from(value: WalletError) -> Self {
178 match value {
179 WalletError::ValueNotZero => Self::Message("tx value not zero".to_string()),
180 WalletError::FromSet => Self::Message("tx from field is set".to_string()),
181 WalletError::NonceSet => Self::Message("tx nonce is set".to_string()),
182 WalletError::InvalidAuthorization => {
183 Self::Message("invalid authorization address".to_string())
184 }
185 WalletError::IllegalDestination => Self::Message(
186 "the destination of the transaction is not a delegated account".to_string(),
187 ),
188 WalletError::InternalError => Self::Message("internal error".to_string()),
189 WalletError::InvalidTransactionRequest => {
190 Self::Message("invalid tx request".to_string())
191 }
192 }
193 }
194}
195
196impl<E> From<StateOverrideError<E>> for BlockchainError
197where
198 E: Into<Self>,
199{
200 fn from(value: StateOverrideError<E>) -> Self {
201 match value {
202 StateOverrideError::InvalidBytecode(err) => Self::StateOverrideError(err.to_string()),
203 StateOverrideError::BothStateAndStateDiff(addr) => Self::StateOverrideError(format!(
204 "state and state_diff can't be used together for account {addr}",
205 )),
206 StateOverrideError::Database(err) => err.into(),
207 }
208 }
209}
210
211#[derive(Debug, thiserror::Error)]
213pub enum PoolError {
214 #[error("Transaction with cyclic dependent transactions")]
215 CyclicTransaction,
216 #[error("Tx: [{0:?}] insufficient gas price to replace existing transaction")]
218 ReplacementUnderpriced(Box<PoolTransaction>),
219 #[error("Tx: [{0:?}] already Imported")]
220 AlreadyImported(Box<PoolTransaction>),
221}
222
223#[derive(Debug, thiserror::Error)]
225pub enum FeeHistoryError {
226 #[error("requested block range is out of bounds")]
227 InvalidBlockRange,
228 #[error("could not find newest block number requested: {0}")]
229 BlockNotFound(BlockNumberOrTag),
230}
231
232#[derive(Debug)]
233pub struct ErrDetail {
234 pub detail: String,
235}
236
237#[derive(Debug, thiserror::Error)]
239pub enum InvalidTransactionError {
240 #[error("nonce too low")]
242 NonceTooLow,
243 #[error("Nonce too high")]
246 NonceTooHigh,
247 #[error("nonce has max value")]
250 NonceMaxValue,
251 #[error("insufficient funds for transfer")]
253 InsufficientFundsForTransfer,
254 #[error("max initcode size exceeded")]
256 MaxInitCodeSizeExceeded,
257 #[error("Insufficient funds for gas * price + value")]
259 InsufficientFunds,
260 #[error("gas uint64 overflow")]
262 GasUintOverflow,
263 #[error("intrinsic gas too low")]
266 GasTooLow,
267 #[error("intrinsic gas too high -- {}",.0.detail)]
269 GasTooHigh(ErrDetail),
270 #[error("max priority fee per gas higher than max fee per gas")]
273 TipAboveFeeCap,
274 #[error("max fee per gas less than block base fee")]
276 FeeCapTooLow,
277 #[error("Out of gas: gas required exceeds allowance: {0:?}")]
279 BasicOutOfGas(u128),
280 #[error("execution reverted: {0:?}")]
282 Revert(Option<Bytes>),
283 #[error("sender not an eoa")]
285 SenderNoEOA,
286 #[error("invalid chain id for signer")]
288 InvalidChainId,
289 #[error("Incompatible EIP-155 transaction, signed for another chain")]
291 IncompatibleEIP155,
292 #[error("Access lists are not supported before the Berlin hardfork")]
294 AccessListNotSupported,
295 #[error("Block `blob_gas_price` is greater than tx-specified `max_fee_per_blob_gas`")]
298 BlobFeeCapTooLow,
299 #[error("Block `blob_versioned_hashes` is not supported before the Cancun hardfork")]
302 BlobVersionedHashesNotSupported,
303 #[error("`max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.")]
305 MaxFeePerBlobGasNotSupported,
306 #[error("`blob_hashes` are required for EIP-4844 transactions")]
308 NoBlobHashes,
309 #[error("too many blobs in one transaction, have: {0}, max: {1}")]
310 TooManyBlobs(usize, usize),
311 #[error(transparent)]
313 BlobTransactionValidationError(#[from] alloy_consensus::BlobTransactionValidationError),
314 #[error("Blob transaction can't be a create transaction. `to` must be present.")]
316 BlobCreateTransaction,
317 #[error("Blob transaction contains a versioned hash with an incorrect version")]
319 BlobVersionNotSupported,
320 #[error("There should be at least one blob in a Blob transaction.")]
322 EmptyBlobs,
323 #[error("EIP-7702 authorization lists are not supported before the Prague hardfork")]
325 AuthorizationListNotSupported,
326 #[error("Transaction gas limit is greater than the block gas limit, gas_limit: {0}, cap: {1}")]
327 TxGasLimitGreaterThanCap(u64, u64),
328 #[error(transparent)]
330 Revm(revm::context_interface::result::InvalidTransaction),
331 #[error("op-deposit failure post regolith")]
333 DepositTxErrorPostRegolith,
334}
335
336impl From<InvalidTransaction> for InvalidTransactionError {
337 fn from(err: InvalidTransaction) -> Self {
338 match err {
339 InvalidTransaction::InvalidChainId => Self::InvalidChainId,
340 InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap,
341 InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow,
342 InvalidTransaction::CallerGasLimitMoreThanBlock => {
343 Self::GasTooHigh(ErrDetail { detail: String::from("CallerGasLimitMoreThanBlock") })
344 }
345 InvalidTransaction::CallGasCostMoreThanGasLimit { .. } => {
346 Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") })
347 }
348 InvalidTransaction::GasFloorMoreThanGasLimit { .. } => {
349 Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") })
350 }
351 InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA,
352 InvalidTransaction::LackOfFundForMaxFee { .. } => Self::InsufficientFunds,
353 InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow,
354 InvalidTransaction::NonceOverflowInTransaction => Self::NonceMaxValue,
355 InvalidTransaction::CreateInitCodeSizeLimit => Self::MaxInitCodeSizeExceeded,
356 InvalidTransaction::NonceTooHigh { .. } => Self::NonceTooHigh,
357 InvalidTransaction::NonceTooLow { .. } => Self::NonceTooLow,
358 InvalidTransaction::AccessListNotSupported => Self::AccessListNotSupported,
359 InvalidTransaction::BlobGasPriceGreaterThanMax => Self::BlobFeeCapTooLow,
360 InvalidTransaction::BlobVersionedHashesNotSupported => {
361 Self::BlobVersionedHashesNotSupported
362 }
363 InvalidTransaction::MaxFeePerBlobGasNotSupported => Self::MaxFeePerBlobGasNotSupported,
364 InvalidTransaction::BlobCreateTransaction => Self::BlobCreateTransaction,
365 InvalidTransaction::BlobVersionNotSupported => Self::BlobVersionNotSupported,
366 InvalidTransaction::EmptyBlobs => Self::EmptyBlobs,
367 InvalidTransaction::TooManyBlobs { have, max } => Self::TooManyBlobs(have, max),
368 InvalidTransaction::AuthorizationListNotSupported => {
369 Self::AuthorizationListNotSupported
370 }
371 InvalidTransaction::TxGasLimitGreaterThanCap { gas_limit, cap } => {
372 Self::TxGasLimitGreaterThanCap(gas_limit, cap)
373 }
374
375 InvalidTransaction::AuthorizationListInvalidFields
376 | InvalidTransaction::Eip1559NotSupported
377 | InvalidTransaction::Eip2930NotSupported
378 | InvalidTransaction::Eip4844NotSupported
379 | InvalidTransaction::Eip7702NotSupported
380 | InvalidTransaction::EmptyAuthorizationList
381 | InvalidTransaction::Eip7873NotSupported
382 | InvalidTransaction::Eip7873MissingTarget
383 | InvalidTransaction::MissingChainId => 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 }
395 }
396}
397pub(crate) trait ToRpcResponseResult {
399 fn to_rpc_result(self) -> ResponseResult;
400}
401
402pub fn to_rpc_result<T: Serialize>(val: T) -> ResponseResult {
404 match serde_json::to_value(val) {
405 Ok(success) => ResponseResult::Success(success),
406 Err(err) => {
407 error!(%err, "Failed serialize rpc response");
408 ResponseResult::error(RpcError::internal_error())
409 }
410 }
411}
412
413impl<T: Serialize> ToRpcResponseResult for Result<T> {
414 fn to_rpc_result(self) -> ResponseResult {
415 match self {
416 Ok(val) => to_rpc_result(val),
417 Err(err) => match err {
418 BlockchainError::Pool(err) => {
419 error!(%err, "txpool error");
420 match err {
421 PoolError::CyclicTransaction => {
422 RpcError::transaction_rejected("Cyclic transaction detected")
423 }
424 PoolError::ReplacementUnderpriced(_) => {
425 RpcError::transaction_rejected("replacement transaction underpriced")
426 }
427 PoolError::AlreadyImported(_) => {
428 RpcError::transaction_rejected("transaction already imported")
429 }
430 }
431 }
432 BlockchainError::NoSignerAvailable => {
433 RpcError::invalid_params("No Signer available")
434 }
435 BlockchainError::ChainIdNotAvailable => {
436 RpcError::invalid_params("Chain Id not available")
437 }
438 BlockchainError::TransactionConfirmationTimeout { .. } => {
439 RpcError::internal_error_with("Transaction confirmation timeout")
440 }
441 BlockchainError::InvalidTransaction(err) => match err {
442 InvalidTransactionError::Revert(data) => {
443 let mut msg = "execution reverted".to_string();
445 if let Some(reason) = data
446 .as_ref()
447 .and_then(|data| RevertDecoder::new().maybe_decode(data, None))
448 {
449 msg = format!("{msg}: {reason}");
450 }
451 RpcError {
452 code: ErrorCode::ExecutionError,
454 message: msg.into(),
455 data: serde_json::to_value(data).ok(),
456 }
457 }
458 InvalidTransactionError::GasTooLow => {
459 RpcError {
461 code: ErrorCode::ServerError(-32000),
462 message: err.to_string().into(),
463 data: None,
464 }
465 }
466 InvalidTransactionError::GasTooHigh(_) => {
467 RpcError {
469 code: ErrorCode::ServerError(-32000),
470 message: err.to_string().into(),
471 data: None,
472 }
473 }
474 _ => RpcError::transaction_rejected(err.to_string()),
475 },
476 BlockchainError::FeeHistory(err) => RpcError::invalid_params(err.to_string()),
477 BlockchainError::EmptyRawTransactionData => {
478 RpcError::invalid_params("Empty transaction data")
479 }
480 BlockchainError::FailedToDecodeSignedTransaction => {
481 RpcError::invalid_params("Failed to decode transaction")
482 }
483 BlockchainError::FailedToDecodeTransaction => {
484 RpcError::invalid_params("Failed to decode transaction")
485 }
486 BlockchainError::FailedToDecodeReceipt => {
487 RpcError::invalid_params("Failed to decode receipt")
488 }
489 BlockchainError::FailedToDecodeStateDump => {
490 RpcError::invalid_params("Failed to decode state dump")
491 }
492 BlockchainError::SignerError(err) => RpcError::invalid_params(err.to_string()),
493 BlockchainError::SignatureError(err) => RpcError::invalid_params(err.to_string()),
494 BlockchainError::RpcUnimplemented => {
495 RpcError::internal_error_with("Not implemented")
496 }
497 BlockchainError::PrevrandaoNotSet => RpcError::internal_error_with(err.to_string()),
498 BlockchainError::RpcError(err) => err,
499 BlockchainError::InvalidFeeInput => RpcError::invalid_params(
500 "Invalid input: `max_priority_fee_per_gas` greater than `max_fee_per_gas`",
501 ),
502 BlockchainError::AlloyForkProvider(err) => {
503 error!(target: "backend", %err, "fork provider error");
504 match err {
505 TransportError::ErrorResp(err) => RpcError {
506 code: ErrorCode::from(err.code),
507 message: err.message,
508 data: err.data.and_then(|data| serde_json::to_value(data).ok()),
509 },
510 err => RpcError::internal_error_with(format!("Fork Error: {err:?}")),
511 }
512 }
513 err @ BlockchainError::EvmError(_) => {
514 RpcError::internal_error_with(err.to_string())
515 }
516 err @ BlockchainError::EvmOverrideError(_) => {
517 RpcError::invalid_params(err.to_string())
518 }
519 err @ BlockchainError::InvalidUrl(_) => RpcError::invalid_params(err.to_string()),
520 BlockchainError::Internal(err) => RpcError::internal_error_with(err),
521 err @ BlockchainError::BlockOutOfRange(_, _) => {
522 RpcError::invalid_params(err.to_string())
523 }
524 err @ BlockchainError::BlockNotFound => RpcError {
525 code: ErrorCode::ServerError(-32001),
527 message: err.to_string().into(),
528 data: None,
529 },
530 err @ BlockchainError::DataUnavailable => {
531 RpcError::internal_error_with(err.to_string())
532 }
533 err @ BlockchainError::TrieError(_) => {
534 RpcError::internal_error_with(err.to_string())
535 }
536 BlockchainError::UintConversion(err) => RpcError::invalid_params(err),
537 err @ BlockchainError::StateOverrideError(_) => {
538 RpcError::invalid_params(err.to_string())
539 }
540 err @ BlockchainError::TimestampError(_) => {
541 RpcError::invalid_params(err.to_string())
542 }
543 BlockchainError::DatabaseError(err) => {
544 RpcError::internal_error_with(err.to_string())
545 }
546 err @ BlockchainError::EIP1559TransactionUnsupportedAtHardfork => {
547 RpcError::invalid_params(err.to_string())
548 }
549 err @ BlockchainError::EIP2930TransactionUnsupportedAtHardfork => {
550 RpcError::invalid_params(err.to_string())
551 }
552 err @ BlockchainError::EIP4844TransactionUnsupportedAtHardfork => {
553 RpcError::invalid_params(err.to_string())
554 }
555 err @ BlockchainError::EIP7702TransactionUnsupportedAtHardfork => {
556 RpcError::invalid_params(err.to_string())
557 }
558 err @ BlockchainError::DepositTransactionUnsupported => {
559 RpcError::invalid_params(err.to_string())
560 }
561 err @ BlockchainError::ExcessBlobGasNotSet => {
562 RpcError::invalid_params(err.to_string())
563 }
564 err @ BlockchainError::Message(_) => RpcError::internal_error_with(err.to_string()),
565 err @ BlockchainError::UnknownTransactionType => {
566 RpcError::invalid_params(err.to_string())
567 }
568 err @ BlockchainError::MissingRequiredFields => {
569 RpcError::invalid_params(err.to_string())
570 }
571 }
572 .into(),
573 }
574 }
575}