openzeppelin_relayer/utils/
json_rpc_error.rs

1//! JSON-RPC error codes from JSON-RPC 2.0 and EIP-1474
2
3/// Standard JSON-RPC 2.0 error codes
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum StandardJsonRpcError {
6    /// Invalid JSON was received by the server
7    ParseError = -32700,
8    /// The JSON sent is not a valid Request object
9    InvalidRequest = -32600,
10    /// The method does not exist / is not available
11    MethodNotFound = -32601,
12    /// Invalid method parameter(s)
13    InvalidParams = -32602,
14    /// Internal JSON-RPC error
15    InternalError = -32603,
16}
17
18/// Ethereum-specific JSON-RPC error codes (EIP-1474)
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum EthereumJsonRpcError {
21    /// Invalid input
22    InvalidInput = -32000,
23    /// Resource not found
24    ResourceNotFound = -32001,
25    /// Resource unavailable
26    ResourceUnavailable = -32002,
27    /// Transaction rejected
28    TransactionRejected = -32003,
29    /// Method not supported
30    MethodNotSupported = -32004,
31    /// Request limit exceeded
32    LimitExceeded = -32005,
33    /// JSON-RPC version not supported
34    JsonRpcVersionNotSupported = -32006,
35}
36
37impl StandardJsonRpcError {
38    pub const fn code(self) -> i64 {
39        self as i64
40    }
41
42    pub const fn from_code(code: i64) -> Option<Self> {
43        match code {
44            -32700 => Some(Self::ParseError),
45            -32600 => Some(Self::InvalidRequest),
46            -32601 => Some(Self::MethodNotFound),
47            -32602 => Some(Self::InvalidParams),
48            -32603 => Some(Self::InternalError),
49            _ => None,
50        }
51    }
52}
53
54impl EthereumJsonRpcError {
55    pub const fn code(self) -> i64 {
56        self as i64
57    }
58
59    pub const fn from_code(code: i64) -> Option<Self> {
60        match code {
61            -32000 => Some(Self::InvalidInput),
62            -32001 => Some(Self::ResourceNotFound),
63            -32002 => Some(Self::ResourceUnavailable),
64            -32003 => Some(Self::TransactionRejected),
65            -32004 => Some(Self::MethodNotSupported),
66            -32005 => Some(Self::LimitExceeded),
67            -32006 => Some(Self::JsonRpcVersionNotSupported),
68            _ => None,
69        }
70    }
71}
72
73/// Check if a JSON-RPC error code represents a retriable error.
74pub const fn is_retriable_error_code(code: i64) -> bool {
75    code == StandardJsonRpcError::InternalError as i64
76        || code == EthereumJsonRpcError::ResourceUnavailable as i64
77        || code == EthereumJsonRpcError::LimitExceeded as i64
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn test_standard_error_code_values() {
86        assert_eq!(StandardJsonRpcError::ParseError.code(), -32700);
87        assert_eq!(StandardJsonRpcError::InvalidRequest.code(), -32600);
88        assert_eq!(StandardJsonRpcError::MethodNotFound.code(), -32601);
89        assert_eq!(StandardJsonRpcError::InvalidParams.code(), -32602);
90        assert_eq!(StandardJsonRpcError::InternalError.code(), -32603);
91    }
92
93    #[test]
94    fn test_ethereum_error_code_values() {
95        assert_eq!(EthereumJsonRpcError::InvalidInput.code(), -32000);
96        assert_eq!(EthereumJsonRpcError::ResourceNotFound.code(), -32001);
97        assert_eq!(EthereumJsonRpcError::ResourceUnavailable.code(), -32002);
98        assert_eq!(EthereumJsonRpcError::TransactionRejected.code(), -32003);
99        assert_eq!(EthereumJsonRpcError::MethodNotSupported.code(), -32004);
100        assert_eq!(EthereumJsonRpcError::LimitExceeded.code(), -32005);
101        assert_eq!(
102            EthereumJsonRpcError::JsonRpcVersionNotSupported.code(),
103            -32006
104        );
105    }
106
107    #[test]
108    fn test_standard_error_from_code_valid() {
109        assert_eq!(
110            StandardJsonRpcError::from_code(-32700),
111            Some(StandardJsonRpcError::ParseError)
112        );
113        assert_eq!(
114            StandardJsonRpcError::from_code(-32600),
115            Some(StandardJsonRpcError::InvalidRequest)
116        );
117        assert_eq!(
118            StandardJsonRpcError::from_code(-32601),
119            Some(StandardJsonRpcError::MethodNotFound)
120        );
121        assert_eq!(
122            StandardJsonRpcError::from_code(-32602),
123            Some(StandardJsonRpcError::InvalidParams)
124        );
125        assert_eq!(
126            StandardJsonRpcError::from_code(-32603),
127            Some(StandardJsonRpcError::InternalError)
128        );
129    }
130
131    #[test]
132    fn test_standard_error_from_code_invalid() {
133        assert_eq!(StandardJsonRpcError::from_code(-32000), None);
134        assert_eq!(StandardJsonRpcError::from_code(-32699), None);
135        assert_eq!(StandardJsonRpcError::from_code(0), None);
136        assert_eq!(StandardJsonRpcError::from_code(500), None);
137    }
138
139    #[test]
140    fn test_ethereum_error_from_code_valid() {
141        assert_eq!(
142            EthereumJsonRpcError::from_code(-32000),
143            Some(EthereumJsonRpcError::InvalidInput)
144        );
145        assert_eq!(
146            EthereumJsonRpcError::from_code(-32001),
147            Some(EthereumJsonRpcError::ResourceNotFound)
148        );
149        assert_eq!(
150            EthereumJsonRpcError::from_code(-32002),
151            Some(EthereumJsonRpcError::ResourceUnavailable)
152        );
153        assert_eq!(
154            EthereumJsonRpcError::from_code(-32003),
155            Some(EthereumJsonRpcError::TransactionRejected)
156        );
157        assert_eq!(
158            EthereumJsonRpcError::from_code(-32004),
159            Some(EthereumJsonRpcError::MethodNotSupported)
160        );
161        assert_eq!(
162            EthereumJsonRpcError::from_code(-32005),
163            Some(EthereumJsonRpcError::LimitExceeded)
164        );
165        assert_eq!(
166            EthereumJsonRpcError::from_code(-32006),
167            Some(EthereumJsonRpcError::JsonRpcVersionNotSupported)
168        );
169    }
170
171    #[test]
172    fn test_ethereum_error_from_code_invalid() {
173        assert_eq!(EthereumJsonRpcError::from_code(-32700), None);
174        assert_eq!(EthereumJsonRpcError::from_code(-32007), None);
175        assert_eq!(EthereumJsonRpcError::from_code(-31999), None);
176        assert_eq!(EthereumJsonRpcError::from_code(0), None);
177    }
178
179    #[test]
180    fn test_is_retriable_error_code_retriable() {
181        assert!(is_retriable_error_code(-32603));
182        assert!(is_retriable_error_code(-32002));
183        assert!(is_retriable_error_code(-32005));
184    }
185
186    #[test]
187    fn test_is_retriable_error_code_non_retriable_standard() {
188        assert!(!is_retriable_error_code(-32700));
189        assert!(!is_retriable_error_code(-32600));
190        assert!(!is_retriable_error_code(-32601));
191        assert!(!is_retriable_error_code(-32602));
192    }
193
194    #[test]
195    fn test_is_retriable_error_code_non_retriable_ethereum() {
196        assert!(!is_retriable_error_code(-32000));
197        assert!(!is_retriable_error_code(-32001));
198        assert!(!is_retriable_error_code(-32003));
199        assert!(!is_retriable_error_code(-32004));
200        assert!(!is_retriable_error_code(-32006));
201    }
202
203    #[test]
204    fn test_is_retriable_error_code_unknown() {
205        assert!(!is_retriable_error_code(0));
206        assert!(!is_retriable_error_code(404));
207        assert!(!is_retriable_error_code(500));
208        assert!(!is_retriable_error_code(-1));
209        assert!(!is_retriable_error_code(-32100));
210        assert!(!is_retriable_error_code(-32769));
211        assert!(!is_retriable_error_code(-31999));
212    }
213
214    #[test]
215    fn test_standard_error_roundtrip() {
216        let errors = [
217            StandardJsonRpcError::ParseError,
218            StandardJsonRpcError::InvalidRequest,
219            StandardJsonRpcError::MethodNotFound,
220            StandardJsonRpcError::InvalidParams,
221            StandardJsonRpcError::InternalError,
222        ];
223
224        for error in errors {
225            let code = error.code();
226            let reconstructed = StandardJsonRpcError::from_code(code);
227            assert_eq!(reconstructed, Some(error));
228        }
229    }
230
231    #[test]
232    fn test_ethereum_error_roundtrip() {
233        let errors = [
234            EthereumJsonRpcError::InvalidInput,
235            EthereumJsonRpcError::ResourceNotFound,
236            EthereumJsonRpcError::ResourceUnavailable,
237            EthereumJsonRpcError::TransactionRejected,
238            EthereumJsonRpcError::MethodNotSupported,
239            EthereumJsonRpcError::LimitExceeded,
240            EthereumJsonRpcError::JsonRpcVersionNotSupported,
241        ];
242
243        for error in errors {
244            let code = error.code();
245            let reconstructed = EthereumJsonRpcError::from_code(code);
246            assert_eq!(reconstructed, Some(error));
247        }
248    }
249}