Skip to main content

df_hyperliquid_core/client/evm/rpc/
query.rs

1//! [`Query`], one JSON-RPC method with its params: the request body it writes,
2//! the envelope its answer comes in, and how its `result` is read.
3
4use crate::client::evm::rpc::{BlockTag, NodeError, RpcError};
5use crate::client::evm::u256::U256;
6use crate::utils::{Address, Json, hex};
7use serde::{Deserialize, Deserializer, de::DeserializeOwned};
8use serde_json::value::RawValue;
9
10/// One JSON-RPC method with its params, and how its `result` is read.
11#[derive(Debug, Clone)]
12pub struct Query<T> {
13    pub(super) method: &'static str,
14    pub(super) params: Vec<Json>,
15    pub(super) read: fn(&str) -> Result<T, String>,
16}
17
18impl<T> Query<T> {
19    #[must_use]
20    pub const fn method(&self) -> &'static str {
21        self.method
22    }
23
24    /// `{"jsonrpc":"2.0","id":1,"method":...,"params":[...]}`.
25    #[must_use]
26    pub fn body(&self) -> String {
27        Json::Object(vec![
28            ("jsonrpc".to_owned(), Json::String("2.0".to_owned())),
29            ("id".to_owned(), Json::Number("1".to_owned())),
30            ("method".to_owned(), Json::String(self.method.to_owned())),
31            ("params".to_owned(), Json::Array(self.params.clone())),
32        ])
33        .compact()
34    }
35
36    /// The answer's text, read: its `result` as `T`, or its `error`.
37    pub fn answer(&self, text: &str) -> Result<T, RpcError> {
38        let unreadable = |detail: String| RpcError::Unreadable { method: self.method, detail };
39        let envelope: Envelope<'_> = serde_json::from_str(text)
40            .map_err(|error| unreadable(format!("{error} in {}", text.chars().take(300).collect::<String>())))?;
41        if let Some(error) = envelope.error {
42            let data = error
43                .data
44                .map(|data| serde_json::from_str::<String>(data.get()).unwrap_or_else(|_| data.get().to_owned()));
45            return Err(RpcError::Node {
46                method: self.method,
47                error: NodeError { code: error.code, message: error.message, data },
48            });
49        }
50        let result = envelope.result.ok_or_else(|| unreadable("neither a result nor an error".to_owned()))?;
51        (self.read)(result.get()).map_err(|detail| unreadable(format!("{detail} in {}", result.get())))
52    }
53}
54
55#[derive(Deserialize)]
56struct Envelope<'a> {
57    #[serde(default, borrow, deserialize_with = "raw")]
58    result: Option<&'a RawValue>,
59    #[serde(default, borrow)]
60    error: Option<ErrorBody<'a>>,
61}
62
63#[derive(Deserialize)]
64struct ErrorBody<'a> {
65    code: i64,
66    #[serde(default)]
67    message: String,
68    #[serde(default, borrow, deserialize_with = "raw")]
69    data: Option<&'a RawValue>,
70}
71
72/// A value as the text it was written in, `null` included.
73fn raw<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<&'de RawValue>, D::Error> {
74    <&RawValue>::deserialize(deserializer).map(Some)
75}
76
77pub(super) fn json<T: DeserializeOwned>(text: &str) -> Result<T, String> {
78    serde_json::from_str(text).map_err(|error| error.to_string())
79}
80
81pub(super) fn small(text: &str) -> Result<u64, String> {
82    let value: U256 = json(text)?;
83    value.to_u64().ok_or_else(|| format!("{value} does not fit 64 bits"))
84}
85
86pub(super) fn data(text: &str) -> Result<Vec<u8>, String> {
87    let said: String = json(text)?;
88    let digits = said.strip_prefix("0x").ok_or_else(|| format!("`{said}` is not 0x and hex"))?;
89    hex::decode(digits).map_err(|error| error.to_string())
90}
91
92pub(super) fn quantity(value: U256) -> Json {
93    Json::String(value.to_quantity())
94}
95
96pub(super) fn tag(block: BlockTag) -> Json {
97    Json::String(block.to_string())
98}
99
100pub(super) fn address(address: Address) -> Json {
101    Json::String(address.to_hex())
102}
103
104#[cfg(test)]
105#[path = "tests/query.rs"]
106mod tests;