1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4use starknet_types::messaging::MessageToL2;
5
6use crate::error::Error;
7
8#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(deny_unknown_fields)]
11pub struct RpcMethodCall {
12 pub jsonrpc: Version,
14 pub method: String,
16 #[serde(default = "no_params")]
18 pub params: RequestParams,
19 pub id: Id,
23}
24
25impl RpcMethodCall {
26 pub fn id(&self) -> Id {
27 self.id.clone()
28 }
29}
30
31impl TryFrom<MessageToL2> for RpcMethodCall {
32 type Error = Error;
33
34 fn try_from(message: MessageToL2) -> Result<Self, Self::Error> {
35 (&message).try_into()
36 }
37}
38
39impl TryFrom<&MessageToL2> for RpcMethodCall {
40 type Error = Error;
41
42 fn try_from(message: &MessageToL2) -> Result<Self, Self::Error> {
43 let message_serialized =
44 serde_json::to_value(message).map_err(|e| Error::ConversionError(e.to_string()))?;
45
46 let params = message_serialized
47 .as_object()
48 .ok_or(Error::ConversionError("Message not convertible to JSON object".into()))?;
49
50 Ok(Self {
51 jsonrpc: Version::V2,
52 method: "devnet_postmanSendMessageToL2".into(),
53 params: RequestParams::Object(params.clone()),
54 id: Id::Number(0),
55 })
56 }
57}
58
59#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
62#[serde(deny_unknown_fields)]
63pub struct RpcNotification {
64 pub jsonrpc: Option<Version>,
65 pub method: String,
66 #[serde(default = "no_params")]
67 pub params: RequestParams,
68}
69
70#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(untagged)]
73pub enum RpcCall {
74 MethodCall(RpcMethodCall),
76 Notification(RpcNotification),
78 Invalid {
80 #[serde(default = "null_id")]
82 id: Id,
83 },
84}
85
86#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
88#[cfg_attr(test, derive(Serialize))]
89#[serde(deny_unknown_fields)]
90#[serde(untagged)]
91pub enum Request {
92 Single(RpcCall),
94 Batch(Vec<RpcCall>),
96}
97
98#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
100#[serde(untagged, deny_unknown_fields)]
101pub enum RequestParams {
102 None,
104 Array(Vec<serde_json::Value>),
106 Object(serde_json::Map<String, serde_json::Value>),
108}
109
110impl From<RequestParams> for serde_json::Value {
111 fn from(params: RequestParams) -> Self {
112 match params {
113 RequestParams::None => serde_json::Value::Null,
114 RequestParams::Array(arr) => arr.into(),
115 RequestParams::Object(obj) => obj.into(),
116 }
117 }
118}
119
120fn no_params() -> RequestParams {
121 RequestParams::None
122}
123
124#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
126pub enum Version {
127 #[serde(rename = "2.0")]
128 V2,
129 #[serde(rename = "1.0")]
130 V1,
131}
132
133#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
134#[serde(untagged)]
135pub enum Id {
136 String(String),
137 Number(i64),
138 Null,
139}
140
141impl fmt::Display for Id {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 match self {
144 Id::String(s) => s.fmt(f),
145 Id::Number(n) => n.fmt(f),
146 Id::Null => f.write_str("null"),
147 }
148 }
149}
150
151fn null_id() -> Id {
152 Id::Null
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn can_serialize_batch() {
161 let batch = Request::Batch(vec![
162 RpcCall::MethodCall(RpcMethodCall {
163 jsonrpc: Version::V2,
164 method: "eth_method".to_owned(),
165 params: RequestParams::Array(vec![
166 serde_json::Value::from(999),
167 serde_json::Value::from(1337),
168 ]),
169 id: Id::Number(1),
170 }),
171 RpcCall::Notification(RpcNotification {
172 jsonrpc: Some(Version::V2),
173 method: "eth_method".to_owned(),
174 params: RequestParams::Array(vec![serde_json::Value::from(999)]),
175 }),
176 ]);
177
178 let obj = serde_json::to_string(&batch).unwrap();
179 assert_eq!(
180 obj,
181 r#"[{"jsonrpc":"2.0","method":"eth_method","params":[999,1337],"id":1},{"jsonrpc":"2.0","method":"eth_method","params":[999]}]"#
182 );
183 }
184
185 #[test]
186 fn can_deserialize_batch() {
187 let s = r#"[{}, {"jsonrpc": "2.0", "method": "eth_call", "params": [1337,420], "id": 1},{"jsonrpc": "2.0", "method": "notify", "params": [999]}]"#;
188 let obj: Request = serde_json::from_str(s).unwrap();
189 assert_eq!(
190 obj,
191 Request::Batch(vec![
192 RpcCall::Invalid { id: Id::Null },
193 RpcCall::MethodCall(RpcMethodCall {
194 jsonrpc: Version::V2,
195 method: "eth_call".to_owned(),
196 params: RequestParams::Array(vec![
197 serde_json::Value::from(1337),
198 serde_json::Value::from(420)
199 ]),
200 id: Id::Number(1)
201 }),
202 RpcCall::Notification(RpcNotification {
203 jsonrpc: Some(Version::V2),
204 method: "notify".to_owned(),
205 params: RequestParams::Array(vec![serde_json::Value::from(999)])
206 })
207 ])
208 )
209 }
210
211 #[test]
212 fn can_serialize_method() {
213 let m = RpcMethodCall {
214 jsonrpc: Version::V2,
215 method: "eth_method".to_owned(),
216 params: RequestParams::Array(vec![
217 serde_json::Value::from(999),
218 serde_json::Value::from(1337),
219 ]),
220 id: Id::Number(1),
221 };
222
223 let obj = serde_json::to_string(&m).unwrap();
224 assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999,1337],"id":1}"#);
225 }
226
227 #[test]
228 fn can_serialize_call_notification() {
229 let n = RpcCall::Notification(RpcNotification {
230 jsonrpc: Some(Version::V2),
231 method: "eth_method".to_owned(),
232 params: RequestParams::Array(vec![serde_json::Value::from(999)]),
233 });
234 let obj = serde_json::to_string(&n).unwrap();
235 assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999]}"#);
236 }
237
238 #[test]
239 fn can_serialize_notification() {
240 let n = RpcNotification {
241 jsonrpc: Some(Version::V2),
242 method: "eth_method".to_owned(),
243 params: RequestParams::Array(vec![
244 serde_json::Value::from(999),
245 serde_json::Value::from(1337),
246 ]),
247 };
248 let obj = serde_json::to_string(&n).unwrap();
249 assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999,1337]}"#);
250 }
251
252 #[test]
253 fn can_deserialize_notification() {
254 let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999,1337]}"#;
255 let obj: RpcNotification = serde_json::from_str(s).unwrap();
256
257 assert_eq!(
258 obj,
259 RpcNotification {
260 jsonrpc: Some(Version::V2),
261 method: "eth_method".to_owned(),
262 params: RequestParams::Array(vec![
263 serde_json::Value::from(999),
264 serde_json::Value::from(1337)
265 ])
266 }
267 );
268 let s = r#"{"jsonrpc": "2.0", "method": "foobar"}"#;
269 let obj: RpcNotification = serde_json::from_str(s).unwrap();
270 assert_eq!(
271 obj,
272 RpcNotification {
273 jsonrpc: Some(Version::V2),
274 method: "foobar".to_owned(),
275 params: RequestParams::None,
276 }
277 );
278 let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999,1337], "id": 1}"#;
279 let obj: Result<RpcNotification, _> = serde_json::from_str(s);
280 assert!(obj.is_err());
281 }
282
283 #[test]
284 fn can_deserialize_call() {
285 let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999]}"#;
286 let obj: RpcCall = serde_json::from_str(s).unwrap();
287 assert_eq!(
288 obj,
289 RpcCall::Notification(RpcNotification {
290 jsonrpc: Some(Version::V2),
291 method: "eth_method".to_owned(),
292 params: RequestParams::Array(vec![serde_json::Value::from(999)])
293 })
294 );
295
296 let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999], "id": 1}"#;
297 let obj: RpcCall = serde_json::from_str(s).unwrap();
298 assert_eq!(
299 obj,
300 RpcCall::MethodCall(RpcMethodCall {
301 jsonrpc: Version::V2,
302 method: "eth_method".to_owned(),
303 params: RequestParams::Array(vec![serde_json::Value::from(999)]),
304 id: Id::Number(1)
305 })
306 );
307
308 let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [], "id": 1}"#;
309 let obj: RpcCall = serde_json::from_str(s).unwrap();
310 assert_eq!(
311 obj,
312 RpcCall::MethodCall(RpcMethodCall {
313 jsonrpc: Version::V2,
314 method: "eth_method".to_owned(),
315 params: RequestParams::Array(vec![]),
316 id: Id::Number(1)
317 })
318 );
319
320 let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": null, "id": 1}"#;
321 let obj: RpcCall = serde_json::from_str(s).unwrap();
322 assert_eq!(
323 obj,
324 RpcCall::MethodCall(RpcMethodCall {
325 jsonrpc: Version::V2,
326 method: "eth_method".to_owned(),
327 params: RequestParams::None,
328 id: Id::Number(1)
329 })
330 );
331
332 let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "id": 1}"#;
333 let obj: RpcCall = serde_json::from_str(s).unwrap();
334 assert_eq!(
335 obj,
336 RpcCall::MethodCall(RpcMethodCall {
337 jsonrpc: Version::V2,
338 method: "eth_method".to_owned(),
339 params: RequestParams::None,
340 id: Id::Number(1)
341 })
342 );
343 }
344}