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