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::{
14 backend::DatabaseError,
15 decode::RevertDecoder,
16 revm::{
17 interpreter::InstructionResult,
18 primitives::{EVMError, InvalidHeader},
19 },
20};
21use serde::Serialize;
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("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::Precompile(err) => Self::Message(err),
126 EVMError::Custom(err) => Self::Message(err),
127 }
128 }
129}
130
131impl From<WalletError> for BlockchainError {
132 fn from(value: WalletError) -> Self {
133 match value {
134 WalletError::ValueNotZero => Self::Message("tx value not zero".to_string()),
135 WalletError::FromSet => Self::Message("tx from field is set".to_string()),
136 WalletError::NonceSet => Self::Message("tx nonce is set".to_string()),
137 WalletError::InvalidAuthorization => {
138 Self::Message("invalid authorization address".to_string())
139 }
140 WalletError::IllegalDestination => Self::Message(
141 "the destination of the transaction is not a delegated account".to_string(),
142 ),
143 WalletError::InternalError => Self::Message("internal error".to_string()),
144 WalletError::InvalidTransactionRequest => {
145 Self::Message("invalid tx request".to_string())
146 }
147 }
148 }
149}
150
151#[derive(Debug, thiserror::Error)]
153pub enum PoolError {
154 #[error("Transaction with cyclic dependent transactions")]
155 CyclicTransaction,
156 #[error("Tx: [{0:?}] insufficient gas price to replace existing transaction")]
158 ReplacementUnderpriced(Box<PoolTransaction>),
159 #[error("Tx: [{0:?}] already Imported")]
160 AlreadyImported(Box<PoolTransaction>),
161}
162
163#[derive(Debug, thiserror::Error)]
165pub enum FeeHistoryError {
166 #[error("requested block range is out of bounds")]
167 InvalidBlockRange,
168 #[error("could not find newest block number requested: {0}")]
169 BlockNotFound(BlockNumberOrTag),
170}
171
172#[derive(Debug)]
173pub struct ErrDetail {
174 pub detail: String,
175}
176
177#[derive(Debug, thiserror::Error)]
179pub enum InvalidTransactionError {
180 #[error("nonce too low")]
182 NonceTooLow,
183 #[error("Nonce too high")]
186 NonceTooHigh,
187 #[error("nonce has max value")]
190 NonceMaxValue,
191 #[error("insufficient funds for transfer")]
193 InsufficientFundsForTransfer,
194 #[error("max initcode size exceeded")]
196 MaxInitCodeSizeExceeded,
197 #[error("Insufficient funds for gas * price + value")]
199 InsufficientFunds,
200 #[error("gas uint64 overflow")]
202 GasUintOverflow,
203 #[error("intrinsic gas too low")]
206 GasTooLow,
207 #[error("intrinsic gas too high -- {}",.0.detail)]
209 GasTooHigh(ErrDetail),
210 #[error("max priority fee per gas higher than max fee per gas")]
213 TipAboveFeeCap,
214 #[error("max fee per gas less than block base fee")]
216 FeeCapTooLow,
217 #[error("Out of gas: gas required exceeds allowance: {0:?}")]
219 BasicOutOfGas(u128),
220 #[error("execution reverted: {0:?}")]
222 Revert(Option<Bytes>),
223 #[error("sender not an eoa")]
225 SenderNoEOA,
226 #[error("invalid chain id for signer")]
228 InvalidChainId,
229 #[error("Incompatible EIP-155 transaction, signed for another chain")]
231 IncompatibleEIP155,
232 #[error("Access lists are not supported before the Berlin hardfork")]
234 AccessListNotSupported,
235 #[error("Block `blob_gas_price` is greater than tx-specified `max_fee_per_blob_gas`")]
238 BlobFeeCapTooLow,
239 #[error("Block `blob_versioned_hashes` is not supported before the Cancun hardfork")]
242 BlobVersionedHashesNotSupported,
243 #[error("`max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.")]
245 MaxFeePerBlobGasNotSupported,
246 #[error("`blob_hashes` are required for EIP-4844 transactions")]
248 NoBlobHashes,
249 #[error("too many blobs in one transaction, have: {0}")]
250 TooManyBlobs(usize),
251 #[error(transparent)]
253 BlobTransactionValidationError(#[from] alloy_consensus::BlobTransactionValidationError),
254 #[error("Blob transaction can't be a create transaction. `to` must be present.")]
256 BlobCreateTransaction,
257 #[error("Blob transaction contains a versioned hash with an incorrect version")]
259 BlobVersionNotSupported,
260 #[error("There should be at least one blob in a Blob transaction.")]
262 EmptyBlobs,
263 #[error("EIP-7702 authorization lists are not supported before the Prague hardfork")]
265 AuthorizationListNotSupported,
266 #[error(transparent)]
268 Revm(revm::primitives::InvalidTransaction),
269}
270
271impl From<revm::primitives::InvalidTransaction> for InvalidTransactionError {
272 fn from(err: revm::primitives::InvalidTransaction) -> Self {
273 use revm::primitives::InvalidTransaction;
274 match err {
275 InvalidTransaction::InvalidChainId => Self::InvalidChainId,
276 InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap,
277 InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow,
278 InvalidTransaction::CallerGasLimitMoreThanBlock => {
279 Self::GasTooHigh(ErrDetail { detail: String::from("CallerGasLimitMoreThanBlock") })
280 }
281 InvalidTransaction::CallGasCostMoreThanGasLimit => {
282 Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") })
283 }
284 InvalidTransaction::GasFloorMoreThanGasLimit => {
285 Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") })
286 }
287 InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA,
288 InvalidTransaction::LackOfFundForMaxFee { .. } => Self::InsufficientFunds,
289 InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow,
290 InvalidTransaction::NonceOverflowInTransaction => Self::NonceMaxValue,
291 InvalidTransaction::CreateInitCodeSizeLimit => Self::MaxInitCodeSizeExceeded,
292 InvalidTransaction::NonceTooHigh { .. } => Self::NonceTooHigh,
293 InvalidTransaction::NonceTooLow { .. } => Self::NonceTooLow,
294 InvalidTransaction::AccessListNotSupported => Self::AccessListNotSupported,
295 InvalidTransaction::BlobGasPriceGreaterThanMax => Self::BlobFeeCapTooLow,
296 InvalidTransaction::BlobVersionedHashesNotSupported => {
297 Self::BlobVersionedHashesNotSupported
298 }
299 InvalidTransaction::MaxFeePerBlobGasNotSupported => Self::MaxFeePerBlobGasNotSupported,
300 InvalidTransaction::BlobCreateTransaction => Self::BlobCreateTransaction,
301 InvalidTransaction::BlobVersionNotSupported => Self::BlobVersionNotSupported,
302 InvalidTransaction::EmptyBlobs => Self::EmptyBlobs,
303 InvalidTransaction::TooManyBlobs { have } => Self::TooManyBlobs(have),
304 InvalidTransaction::AuthorizationListNotSupported => {
305 Self::AuthorizationListNotSupported
306 }
307 InvalidTransaction::AuthorizationListInvalidFields |
308 InvalidTransaction::OptimismError(_) |
309 InvalidTransaction::EofCrateShouldHaveToAddress |
310 InvalidTransaction::EmptyAuthorizationList => Self::Revm(err),
311 }
312 }
313}
314
315pub(crate) trait ToRpcResponseResult {
317 fn to_rpc_result(self) -> ResponseResult;
318}
319
320pub fn to_rpc_result<T: Serialize>(val: T) -> ResponseResult {
322 match serde_json::to_value(val) {
323 Ok(success) => ResponseResult::Success(success),
324 Err(err) => {
325 error!(%err, "Failed serialize rpc response");
326 ResponseResult::error(RpcError::internal_error())
327 }
328 }
329}
330
331impl<T: Serialize> ToRpcResponseResult for Result<T> {
332 fn to_rpc_result(self) -> ResponseResult {
333 match self {
334 Ok(val) => to_rpc_result(val),
335 Err(err) => match err {
336 BlockchainError::Pool(err) => {
337 error!(%err, "txpool error");
338 match err {
339 PoolError::CyclicTransaction => {
340 RpcError::transaction_rejected("Cyclic transaction detected")
341 }
342 PoolError::ReplacementUnderpriced(_) => {
343 RpcError::transaction_rejected("replacement transaction underpriced")
344 }
345 PoolError::AlreadyImported(_) => {
346 RpcError::transaction_rejected("transaction already imported")
347 }
348 }
349 }
350 BlockchainError::NoSignerAvailable => {
351 RpcError::invalid_params("No Signer available")
352 }
353 BlockchainError::ChainIdNotAvailable => {
354 RpcError::invalid_params("Chain Id not available")
355 }
356 BlockchainError::InvalidTransaction(err) => match err {
357 InvalidTransactionError::Revert(data) => {
358 let mut msg = "execution reverted".to_string();
360 if let Some(reason) = data
361 .as_ref()
362 .and_then(|data| RevertDecoder::new().maybe_decode(data, None))
363 {
364 msg = format!("{msg}: {reason}");
365 }
366 RpcError {
367 code: ErrorCode::ExecutionError,
369 message: msg.into(),
370 data: serde_json::to_value(data).ok(),
371 }
372 }
373 InvalidTransactionError::GasTooLow => {
374 RpcError {
376 code: ErrorCode::ServerError(-32000),
377 message: err.to_string().into(),
378 data: None,
379 }
380 }
381 InvalidTransactionError::GasTooHigh(_) => {
382 RpcError {
384 code: ErrorCode::ServerError(-32000),
385 message: err.to_string().into(),
386 data: None,
387 }
388 }
389 _ => RpcError::transaction_rejected(err.to_string()),
390 },
391 BlockchainError::FeeHistory(err) => RpcError::invalid_params(err.to_string()),
392 BlockchainError::EmptyRawTransactionData => {
393 RpcError::invalid_params("Empty transaction data")
394 }
395 BlockchainError::FailedToDecodeSignedTransaction => {
396 RpcError::invalid_params("Failed to decode transaction")
397 }
398 BlockchainError::FailedToDecodeTransaction => {
399 RpcError::invalid_params("Failed to decode transaction")
400 }
401 BlockchainError::FailedToDecodeReceipt => {
402 RpcError::invalid_params("Failed to decode receipt")
403 }
404 BlockchainError::FailedToDecodeStateDump => {
405 RpcError::invalid_params("Failed to decode state dump")
406 }
407 BlockchainError::SignerError(err) => RpcError::invalid_params(err.to_string()),
408 BlockchainError::SignatureError(err) => RpcError::invalid_params(err.to_string()),
409 BlockchainError::RpcUnimplemented => {
410 RpcError::internal_error_with("Not implemented")
411 }
412 BlockchainError::PrevrandaoNotSet => RpcError::internal_error_with(err.to_string()),
413 BlockchainError::RpcError(err) => err,
414 BlockchainError::InvalidFeeInput => RpcError::invalid_params(
415 "Invalid input: `max_priority_fee_per_gas` greater than `max_fee_per_gas`",
416 ),
417 BlockchainError::AlloyForkProvider(err) => {
418 error!(target: "backend", %err, "fork provider error");
419 match err {
420 TransportError::ErrorResp(err) => RpcError {
421 code: ErrorCode::from(err.code),
422 message: err.message,
423 data: err.data.and_then(|data| serde_json::to_value(data).ok()),
424 },
425 err => RpcError::internal_error_with(format!("Fork Error: {err:?}")),
426 }
427 }
428 err @ BlockchainError::EvmError(_) => {
429 RpcError::internal_error_with(err.to_string())
430 }
431 err @ BlockchainError::InvalidUrl(_) => RpcError::invalid_params(err.to_string()),
432 BlockchainError::Internal(err) => RpcError::internal_error_with(err),
433 err @ BlockchainError::BlockOutOfRange(_, _) => {
434 RpcError::invalid_params(err.to_string())
435 }
436 err @ BlockchainError::BlockNotFound => RpcError {
437 code: ErrorCode::ServerError(-32001),
439 message: err.to_string().into(),
440 data: None,
441 },
442 err @ BlockchainError::DataUnavailable => {
443 RpcError::internal_error_with(err.to_string())
444 }
445 err @ BlockchainError::TrieError(_) => {
446 RpcError::internal_error_with(err.to_string())
447 }
448 BlockchainError::UintConversion(err) => RpcError::invalid_params(err),
449 err @ BlockchainError::StateOverrideError(_) => {
450 RpcError::invalid_params(err.to_string())
451 }
452 err @ BlockchainError::TimestampError(_) => {
453 RpcError::invalid_params(err.to_string())
454 }
455 BlockchainError::DatabaseError(err) => {
456 RpcError::internal_error_with(err.to_string())
457 }
458 err @ BlockchainError::EIP1559TransactionUnsupportedAtHardfork => {
459 RpcError::invalid_params(err.to_string())
460 }
461 err @ BlockchainError::EIP2930TransactionUnsupportedAtHardfork => {
462 RpcError::invalid_params(err.to_string())
463 }
464 err @ BlockchainError::EIP4844TransactionUnsupportedAtHardfork => {
465 RpcError::invalid_params(err.to_string())
466 }
467 err @ BlockchainError::EIP7702TransactionUnsupportedAtHardfork => {
468 RpcError::invalid_params(err.to_string())
469 }
470 err @ BlockchainError::DepositTransactionUnsupported => {
471 RpcError::invalid_params(err.to_string())
472 }
473 err @ BlockchainError::ExcessBlobGasNotSet => {
474 RpcError::invalid_params(err.to_string())
475 }
476 err @ BlockchainError::Message(_) => RpcError::internal_error_with(err.to_string()),
477 err @ BlockchainError::UnknownTransactionType => {
478 RpcError::invalid_params(err.to_string())
479 }
480 }
481 .into(),
482 }
483 }
484}