anvil_zksync_api_server/
error.rs

1//!
2//! Error handling utilities for the JSON-RPC API server.
3//!
4
5use jsonrpsee::{
6    core::Serialize,
7    types::{ErrorCode, ErrorObject, ErrorObjectOwned},
8};
9use zksync_error::{
10    anvil_zksync::{gas_estim::GasEstimationError, node::AnvilNodeError, state::StateLoaderError},
11    ICustomError, IError as _, ZksyncError,
12};
13use zksync_web3_decl::error::Web3Error;
14
15use jsonrpsee::types::ErrorCode as RpcErrorCode;
16
17/// An utility method to convert an error from zksync-error format to a JSON-RPC error object.
18///
19/// # Arguments
20///
21/// * `code` - Optional error code to use. If None, the error code is derived from the error's identifier.
22/// * `error` - The error to convert.
23///
24/// # Returns
25///
26/// A JSON-RPC error object.
27fn to_rpc<I: Serialize + ICustomError<ZksyncError, ZksyncError>>(
28    code: Option<i32>,
29    error: I,
30) -> ErrorObjectOwned {
31    ErrorObject::owned(
32        code.unwrap_or(error.to_unified().get_identifier().encode() as i32),
33        error.to_unified().get_message(),
34        Some(error),
35    )
36}
37
38/// Trait for converting custom errors into JSON-RPC error objects.
39///
40/// This trait allows different error types to be converted into a format
41/// suitable for JSON-RPC responses.
42pub trait RpcErrorAdapter {
43    /// Converts the error into a JSON-RPC error object.
44    ///
45    /// # Arguments
46    ///
47    /// * `error` - The error to convert.
48    ///
49    /// # Returns
50    ///
51    /// A JSON-RPC error object.
52    fn into(error: Self) -> ErrorObjectOwned;
53}
54
55/// Maps state loader errors to appropriate JSON-RPC error codes based on the error type.
56impl RpcErrorAdapter for StateLoaderError {
57    fn into(error: Self) -> ErrorObjectOwned {
58        let error_code = match error {
59            StateLoaderError::LoadingStateOverExistingState
60            | StateLoaderError::LoadEmptyState
61            | StateLoaderError::StateDecompression { .. }
62            | StateLoaderError::StateDeserialization { .. }
63            | StateLoaderError::UnknownStateVersion { .. }
64            | StateLoaderError::StateFileAccess { .. } => ErrorCode::InvalidParams,
65            StateLoaderError::GenericError { .. } => ErrorCode::InternalError,
66            _ => ErrorCode::InternalError,
67        };
68        to_rpc(Some(error_code.code()), error)
69    }
70}
71
72impl RpcErrorAdapter for AnvilNodeError {
73    fn into(error: Self) -> ErrorObjectOwned {
74        // Map the Web3Error to an appropriate RPC error code
75        match &error {
76            AnvilNodeError::TransactionGasEstimationFailed { inner, .. } => {
77                // We keep previously used `Web3Error::SubmitTransactionError`
78                // for an outside user.
79                RpcErrorAdapter::into(Web3Error::SubmitTransactionError(
80                    error.to_unified().get_message(),
81                    match &**inner {
82                        GasEstimationError::TransactionRevert { data, .. }
83                        | GasEstimationError::TransactionAlwaysReverts { data, .. } => data.clone(),
84                        _ => vec![],
85                    },
86                ))
87            }
88            AnvilNodeError::SerializationError { .. } => {
89                // We keep previously used `Web3Error::SubmitTransactionError`
90                // for an outside user.
91                RpcErrorAdapter::into(Web3Error::SubmitTransactionError(
92                    error.to_unified().get_message(),
93                    vec![],
94                ))
95            }
96            _ => to_rpc(Some(RpcErrorCode::InternalError.code()), error),
97        }
98    }
99}
100
101/// Maps Web3 errors to appropriate JSON-RPC error codes based on the error type.
102impl RpcErrorAdapter for Web3Error {
103    fn into(error: Self) -> ErrorObjectOwned {
104        let web3_error = &error;
105        // Map the Web3Error to an appropriate RPC error code
106        let code: RpcErrorCode = match web3_error {
107            Web3Error::InternalError(_) => RpcErrorCode::InternalError,
108            Web3Error::MethodNotImplemented => RpcErrorCode::MethodNotFound,
109            Web3Error::NoBlock
110            | Web3Error::PrunedBlock(_)
111            | Web3Error::PrunedL1Batch(_)
112            | Web3Error::ProxyError(_)
113            | Web3Error::TooManyTopics
114            | Web3Error::FilterNotFound
115            | Web3Error::LogsLimitExceeded(_, _, _)
116            | Web3Error::InvalidFilterBlockHash
117            | Web3Error::TreeApiUnavailable => RpcErrorCode::InvalidParams,
118            Web3Error::SubmitTransactionError(_, _) | Web3Error::SerializationError(_) => {
119                RpcErrorCode::ServerError(3)
120            }
121            Web3Error::ServerShuttingDown => RpcErrorCode::ServerIsBusy,
122        };
123
124        // For transaction submission errors, include the transaction data as part of the error
125        let data = match &error {
126            Web3Error::SubmitTransactionError(_, data) => Some(format!("0x{}", hex::encode(data))),
127            _ => None,
128        };
129
130        // Format the error message
131        let message = match web3_error {
132            Web3Error::InternalError(e) => e.to_string(),
133            _ => web3_error.to_string(),
134        };
135
136        ErrorObject::owned(code.code(), message, data)
137    }
138}
139
140/// All anyhow errors are treated as internal errors.
141impl RpcErrorAdapter for anyhow::Error {
142    fn into(error: Self) -> ErrorObjectOwned {
143        ErrorObjectOwned::owned(
144            ErrorCode::InternalError.code(),
145            error.to_string(),
146            None::<()>,
147        )
148    }
149}
150
151/// Creates a JSON-RPC error object for invalid parameters.
152///
153/// # Arguments
154///
155/// * `msg` - The error message.
156///
157/// # Returns
158///
159/// A JSON-RPC error object with the InvalidParams error code.
160pub(crate) fn rpc_invalid_params(msg: String) -> ErrorObjectOwned {
161    ErrorObjectOwned::owned(ErrorCode::InvalidParams.code(), msg, None::<()>)
162}
163
164/// Creates a JSON-RPC error result for unsupported methods.
165///
166/// # Arguments
167///
168/// * `method_name` - The name of the unsupported method.
169///
170/// # Returns
171///
172/// A JSON-RPC error result with the MethodNotFound error code.
173pub(crate) fn rpc_unsupported<T>(method_name: &str) -> jsonrpsee::core::RpcResult<T> {
174    Err(ErrorObject::owned(
175        ErrorCode::MethodNotFound.code(),
176        format!("Method not found: {}", method_name),
177        None::<()>,
178    ))
179}