anvil_polkadot/api_server/
error.rs1use std::time::Duration;
2
3use crate::substrate_node::{mining_engine::MiningError, service::BackendError};
4use alloy_primitives::{B256, hex};
5use anvil_rpc::{
6 error::{ErrorCode, RpcError},
7 response::ResponseResult,
8};
9use pallet_revive_eth_rpc::{EthRpcError, client::ClientError};
10use polkadot_sdk::{
11 pallet_revive::{EthTransactError, evm::decode_revert_reason},
12 sp_api,
13};
14use serde::Serialize;
15
16#[derive(Debug, thiserror::Error)]
17pub enum Error {
18 #[error("Block mining failed: {0}")]
19 Mining(#[from] MiningError),
20 #[error("Rpc Endpoint not implemented")]
21 RpcUnimplemented,
22 #[error("Invalid params: {0}")]
23 InvalidParams(String),
24 #[error("Revive call failed: {0}")]
25 ReviveRpc(#[from] EthRpcError),
26 #[error(transparent)]
27 Backend(#[from] BackendError),
28 #[error("Nonce overflowing the substrate nonce type")]
29 NonceOverflow,
30 #[error(transparent)]
31 RuntimeApi(#[from] sp_api::ApiError),
32 #[error("Error encountered while creating a BalanceWithDust from a U256 balance")]
33 BalanceConversion,
34 #[error("Internal error: {0}")]
35 InternalError(String),
36 #[error("No signer available")]
37 NoSignerAvailable,
38 #[error("Contract reverted: {0:?}")]
39 EthTransact(EthTransactError),
40 #[error("Transaction {hash} was added to the mempool but wasn't confirmed within {duration:?}")]
41 TransactionConfirmationTimeout { hash: B256, duration: Duration },
42}
43
44impl From<subxt::Error> for Error {
45 fn from(err: subxt::Error) -> Self {
46 Self::ReviveRpc(EthRpcError::ClientError(err.into()))
47 }
48}
49
50impl From<ClientError> for Error {
51 fn from(err: ClientError) -> Self {
52 match err {
53 ClientError::TransactError(err) => Self::EthTransact(err),
54 err => Self::ReviveRpc(EthRpcError::ClientError(err)),
55 }
56 }
57}
58
59pub type Result<T> = std::result::Result<T, Error>;
60
61pub(crate) trait ToRpcResponseResult {
63 fn to_rpc_result(self) -> ResponseResult;
64}
65
66fn to_rpc_result<T: Serialize>(val: T) -> ResponseResult {
68 match serde_json::to_value(val) {
69 Ok(success) => ResponseResult::Success(success),
70 Err(err) => {
71 error!(%err, "Failed serialize rpc response");
72 ResponseResult::error(RpcError::internal_error())
73 }
74 }
75}
76
77impl<T: Serialize> ToRpcResponseResult for Result<T> {
78 fn to_rpc_result(self) -> ResponseResult {
79 match self {
80 Ok(val) => to_rpc_result(val),
81 Err(err) => match err {
82 Error::Mining(mining_error) => match mining_error {
83 MiningError::BlockProducing(error) => {
84 RpcError::internal_error_with(format!("Failed to produce a block: {error}"))
85 .into()
86 }
87 MiningError::MiningModeMismatch => {
88 RpcError::invalid_params("Current mining mode can not answer this query.")
89 .into()
90 }
91 MiningError::Timestamp => {
92 RpcError::invalid_params("Current timestamp is newer.").into()
93 }
94 MiningError::ClosedChannel => {
95 RpcError::internal_error_with("Communication channel was dropped.").into()
96 }
97 },
98 Error::RpcUnimplemented => RpcError::internal_error_with("Not implemented").into(),
99 Error::InvalidParams(error_message) => {
100 RpcError::invalid_params(error_message).into()
101 }
102 Error::EthTransact(EthTransactError::Data(data)) => {
103 let message = match decode_revert_reason(&data) {
104 Some(reason) => format!("execution reverted: {reason}"),
105 None => "execution reverted".to_string(),
106 };
107
108 let data = format!("0x{}", hex::encode(data));
109 RpcError {
110 code: ErrorCode::ExecutionError,
111 message: message.into(),
112 data: Some(data.into()),
113 }
114 .into()
115 }
116 Error::EthTransact(EthTransactError::Message(message)) => {
117 let message = format!("execution reverted: {message}");
118
119 RpcError {
120 code: ErrorCode::ServerError(-32000),
122 message: message.into(),
123 data: None,
124 }
125 .into()
126 }
127 err => RpcError::internal_error_with(format!("{err}")).into(),
128 },
129 }
130 }
131}