Skip to main content

anvil_polkadot/api_server/
error.rs

1use 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
61/// Helper trait to easily convert results to rpc results
62pub(crate) trait ToRpcResponseResult {
63    fn to_rpc_result(self) -> ResponseResult;
64}
65
66/// Converts a serializable value into a `ResponseResult`.
67fn 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                        // This is the error code that revive eth rpc uses for this error.
121                        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}