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