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