anvil_rpc/
request.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4/// A JSON-RPC request object, a method call
5#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(deny_unknown_fields)]
7pub struct RpcMethodCall {
8    /// The version of the protocol
9    pub jsonrpc: Version,
10    /// The name of the method to execute
11    pub method: String,
12    /// An array or object containing the parameters to be passed to the function.
13    #[serde(default = "no_params")]
14    pub params: RequestParams,
15    /// The identifier for this request issued by the client,
16    /// An [Id] must be a String, null or a number.
17    /// If missing it's considered a notification in [Version::V2]
18    pub id: Id,
19}
20
21impl RpcMethodCall {
22    pub fn id(&self) -> Id {
23        self.id.clone()
24    }
25}
26
27/// Represents a JSON-RPC request which is considered a notification (missing [Id] optional
28/// [Version])
29#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(deny_unknown_fields)]
31pub struct RpcNotification {
32    pub jsonrpc: Option<Version>,
33    pub method: String,
34    #[serde(default = "no_params")]
35    pub params: RequestParams,
36}
37
38/// Representation of a single JSON-RPC call
39#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
40#[serde(untagged)]
41pub enum RpcCall {
42    /// the RPC method to invoke
43    MethodCall(RpcMethodCall),
44    /// A notification (no [Id] provided)
45    Notification(RpcNotification),
46    /// Invalid call
47    Invalid {
48        /// id or [Id::Null]
49        #[serde(default = "null_id")]
50        id: Id,
51    },
52}
53
54/// Represents a JSON-RPC request.
55#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
56#[serde(deny_unknown_fields)]
57#[serde(untagged)]
58pub enum Request {
59    /// single json rpc request [RpcCall]
60    Single(RpcCall),
61    /// batch of several requests
62    Batch(Vec<RpcCall>),
63}
64
65/// Request parameters
66#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(untagged, deny_unknown_fields)]
68pub enum RequestParams {
69    /// no parameters provided
70    None,
71    /// An array of JSON values
72    Array(Vec<serde_json::Value>),
73    /// a map of JSON values
74    Object(serde_json::Map<String, serde_json::Value>),
75}
76
77impl From<RequestParams> for serde_json::Value {
78    fn from(params: RequestParams) -> Self {
79        match params {
80            RequestParams::None => Self::Null,
81            RequestParams::Array(arr) => arr.into(),
82            RequestParams::Object(obj) => obj.into(),
83        }
84    }
85}
86
87fn no_params() -> RequestParams {
88    RequestParams::None
89}
90
91/// Represents the version of the RPC protocol
92#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
93pub enum Version {
94    #[serde(rename = "2.0")]
95    V2,
96}
97
98#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
99#[serde(untagged)]
100pub enum Id {
101    String(String),
102    Number(i64),
103    Null,
104}
105
106impl fmt::Display for Id {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        match self {
109            Self::String(s) => s.fmt(f),
110            Self::Number(n) => n.fmt(f),
111            Self::Null => f.write_str("null"),
112        }
113    }
114}
115
116fn null_id() -> Id {
117    Id::Null
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn can_serialize_batch() {
126        let batch = Request::Batch(vec![
127            RpcCall::MethodCall(RpcMethodCall {
128                jsonrpc: Version::V2,
129                method: "eth_method".to_owned(),
130                params: RequestParams::Array(vec![
131                    serde_json::Value::from(999),
132                    serde_json::Value::from(1337),
133                ]),
134                id: Id::Number(1),
135            }),
136            RpcCall::Notification(RpcNotification {
137                jsonrpc: Some(Version::V2),
138                method: "eth_method".to_owned(),
139                params: RequestParams::Array(vec![serde_json::Value::from(999)]),
140            }),
141        ]);
142
143        let obj = serde_json::to_string(&batch).unwrap();
144        assert_eq!(
145            obj,
146            r#"[{"jsonrpc":"2.0","method":"eth_method","params":[999,1337],"id":1},{"jsonrpc":"2.0","method":"eth_method","params":[999]}]"#
147        );
148    }
149
150    #[test]
151    fn can_deserialize_batch() {
152        let s = r#"[{}, {"jsonrpc": "2.0", "method": "eth_call", "params": [1337,420], "id": 1},{"jsonrpc": "2.0", "method": "notify", "params": [999]}]"#;
153        let obj: Request = serde_json::from_str(s).unwrap();
154        assert_eq!(
155            obj,
156            Request::Batch(vec![
157                RpcCall::Invalid { id: Id::Null },
158                RpcCall::MethodCall(RpcMethodCall {
159                    jsonrpc: Version::V2,
160                    method: "eth_call".to_owned(),
161                    params: RequestParams::Array(vec![
162                        serde_json::Value::from(1337),
163                        serde_json::Value::from(420)
164                    ]),
165                    id: Id::Number(1)
166                }),
167                RpcCall::Notification(RpcNotification {
168                    jsonrpc: Some(Version::V2),
169                    method: "notify".to_owned(),
170                    params: RequestParams::Array(vec![serde_json::Value::from(999)])
171                })
172            ])
173        )
174    }
175
176    #[test]
177    fn can_serialize_method() {
178        let m = RpcMethodCall {
179            jsonrpc: Version::V2,
180            method: "eth_method".to_owned(),
181            params: RequestParams::Array(vec![
182                serde_json::Value::from(999),
183                serde_json::Value::from(1337),
184            ]),
185            id: Id::Number(1),
186        };
187
188        let obj = serde_json::to_string(&m).unwrap();
189        assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999,1337],"id":1}"#);
190    }
191
192    #[test]
193    fn can_serialize_call_notification() {
194        let n = RpcCall::Notification(RpcNotification {
195            jsonrpc: Some(Version::V2),
196            method: "eth_method".to_owned(),
197            params: RequestParams::Array(vec![serde_json::Value::from(999)]),
198        });
199        let obj = serde_json::to_string(&n).unwrap();
200        assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999]}"#);
201    }
202
203    #[test]
204    fn can_serialize_notification() {
205        let n = RpcNotification {
206            jsonrpc: Some(Version::V2),
207            method: "eth_method".to_owned(),
208            params: RequestParams::Array(vec![
209                serde_json::Value::from(999),
210                serde_json::Value::from(1337),
211            ]),
212        };
213        let obj = serde_json::to_string(&n).unwrap();
214        assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999,1337]}"#);
215    }
216
217    #[test]
218    fn can_deserialize_notification() {
219        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999,1337]}"#;
220        let obj: RpcNotification = serde_json::from_str(s).unwrap();
221
222        assert_eq!(
223            obj,
224            RpcNotification {
225                jsonrpc: Some(Version::V2),
226                method: "eth_method".to_owned(),
227                params: RequestParams::Array(vec![
228                    serde_json::Value::from(999),
229                    serde_json::Value::from(1337)
230                ])
231            }
232        );
233        let s = r#"{"jsonrpc": "2.0", "method": "foobar"}"#;
234        let obj: RpcNotification = serde_json::from_str(s).unwrap();
235        assert_eq!(
236            obj,
237            RpcNotification {
238                jsonrpc: Some(Version::V2),
239                method: "foobar".to_owned(),
240                params: RequestParams::None,
241            }
242        );
243        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999,1337], "id": 1}"#;
244        let obj: Result<RpcNotification, _> = serde_json::from_str(s);
245        assert!(obj.is_err());
246    }
247
248    #[test]
249    fn can_deserialize_call() {
250        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999]}"#;
251        let obj: RpcCall = serde_json::from_str(s).unwrap();
252        assert_eq!(
253            obj,
254            RpcCall::Notification(RpcNotification {
255                jsonrpc: Some(Version::V2),
256                method: "eth_method".to_owned(),
257                params: RequestParams::Array(vec![serde_json::Value::from(999)])
258            })
259        );
260
261        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999], "id": 1}"#;
262        let obj: RpcCall = serde_json::from_str(s).unwrap();
263        assert_eq!(
264            obj,
265            RpcCall::MethodCall(RpcMethodCall {
266                jsonrpc: Version::V2,
267                method: "eth_method".to_owned(),
268                params: RequestParams::Array(vec![serde_json::Value::from(999)]),
269                id: Id::Number(1)
270            })
271        );
272
273        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [], "id": 1}"#;
274        let obj: RpcCall = serde_json::from_str(s).unwrap();
275        assert_eq!(
276            obj,
277            RpcCall::MethodCall(RpcMethodCall {
278                jsonrpc: Version::V2,
279                method: "eth_method".to_owned(),
280                params: RequestParams::Array(vec![]),
281                id: Id::Number(1)
282            })
283        );
284
285        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": null, "id": 1}"#;
286        let obj: RpcCall = serde_json::from_str(s).unwrap();
287        assert_eq!(
288            obj,
289            RpcCall::MethodCall(RpcMethodCall {
290                jsonrpc: Version::V2,
291                method: "eth_method".to_owned(),
292                params: RequestParams::None,
293                id: Id::Number(1)
294            })
295        );
296
297        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "id": 1}"#;
298        let obj: RpcCall = serde_json::from_str(s).unwrap();
299        assert_eq!(
300            obj,
301            RpcCall::MethodCall(RpcMethodCall {
302                jsonrpc: Version::V2,
303                method: "eth_method".to_owned(),
304                params: RequestParams::None,
305                id: Id::Number(1)
306            })
307        );
308    }
309}