df_elysium_core/client/rpc/
query.rs1use serde::{Deserialize, Deserializer, de::DeserializeOwned};
6use serde_json::value::RawValue;
7
8use df_hyperliquid_core::client::{Http, Request, TransportError};
9
10use crate::client::rpc::{BlockTag, NodeError, Rpc, RpcError};
11use crate::utils::{Json, hex};
12use crate::{B256, U256};
13
14#[derive(Debug, Clone)]
16pub struct Query<T> {
17 method: &'static str,
18 params: Vec<Json>,
19 read: fn(&str) -> Result<T, String>,
20}
21
22impl<T> Query<T> {
23 #[must_use]
25 pub const fn new(method: &'static str, params: Vec<Json>, read: fn(&str) -> Result<T, String>) -> Query<T> {
26 Query { method, params, read }
27 }
28
29 #[must_use]
30 pub const fn method(&self) -> &'static str {
31 self.method
32 }
33
34 #[must_use]
36 pub fn body(&self) -> String {
37 Json::Object(vec![
38 ("jsonrpc".to_owned(), Json::String("2.0".to_owned())),
39 ("id".to_owned(), Json::Number("1".to_owned())),
40 ("method".to_owned(), Json::String(self.method.to_owned())),
41 ("params".to_owned(), Json::Array(self.params.clone())),
42 ])
43 .compact()
44 }
45
46 pub fn answer(&self, text: &str) -> Result<T, RpcError> {
48 let unreadable = |detail: String| RpcError::Unreadable { method: self.method, detail };
49 let envelope: Envelope<'_> = serde_json::from_str(text)
50 .map_err(|error| unreadable(format!("{error} in {}", text.chars().take(300).collect::<String>())))?;
51 if let Some(error) = envelope.error {
52 let data = error
53 .data
54 .map(|data| serde_json::from_str::<String>(data.get()).unwrap_or_else(|_| data.get().to_owned()));
55 return Err(RpcError::Node {
56 method: self.method,
57 error: NodeError { code: error.code, message: error.message, data },
58 });
59 }
60 let result = envelope.result.ok_or_else(|| unreadable("neither a result nor an error".to_owned()))?;
61 (self.read)(result.get()).map_err(|detail| unreadable(format!("{detail} in {}", result.get())))
62 }
63}
64
65pub async fn run<H: Http, T>(rpc: &Rpc<H>, query: Query<T>) -> Result<T, RpcError> {
69 let response = rpc.http().send(Request::post_json(rpc.url().to_owned(), query.body())).await?;
70 if response.status == 429 {
71 return Err(TransportError::RateLimited { retry_after_ms: response.retry_after_ms() }.into());
72 }
73 let text = response.text();
74 match (response.is_success(), query.answer(&text)) {
75 (_, Err(refused @ RpcError::Node { .. })) => Err(refused),
76 (true, answer) => answer,
77 (false, _) => Err(TransportError::Status { status: response.status, body: text }.into()),
78 }
79}
80
81#[derive(Deserialize)]
82struct Envelope<'a> {
83 #[serde(default, borrow, deserialize_with = "raw")]
84 result: Option<&'a RawValue>,
85 #[serde(default, borrow)]
86 error: Option<ErrorBody<'a>>,
87}
88
89#[derive(Deserialize)]
90struct ErrorBody<'a> {
91 code: i64,
92 #[serde(default)]
93 message: String,
94 #[serde(default, borrow, deserialize_with = "raw")]
95 data: Option<&'a RawValue>,
96}
97
98fn raw<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<&'de RawValue>, D::Error> {
100 <&RawValue>::deserialize(deserializer).map(Some)
101}
102
103pub fn json<T: DeserializeOwned>(text: &str) -> Result<T, String> {
105 serde_json::from_str(text).map_err(|error| error.to_string())
106}
107
108pub fn small(text: &str) -> Result<u64, String> {
110 let value: U256 = json(text)?;
111 value.to_u64().ok_or_else(|| format!("{value} does not fit 64 bits"))
112}
113
114pub fn data(text: &str) -> Result<Vec<u8>, String> {
116 let said: String = json(text)?;
117 let digits = said.strip_prefix("0x").ok_or_else(|| format!("`{said}` is not 0x and hex"))?;
118 hex::decode(digits).map_err(|error| error.to_string())
119}
120
121pub(crate) fn tag(block: BlockTag) -> Json {
122 Json::String(block.to_string())
123}
124
125pub(crate) fn word(word: B256) -> Json {
126 Json::String(word.to_hex())
127}
128
129#[cfg(test)]
130#[path = "tests/query.rs"]
131mod tests;