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