drasi_source_hyperliquid/
rest.rs1use crate::types::{AssetCtx, InfoRequest, L2Book, MetaResponse, SpotMetaResponse};
18use anyhow::{anyhow, Result};
19use reqwest::Client;
20
21#[derive(Clone)]
22pub struct HyperliquidRestClient {
23 base_url: String,
24 client: Client,
25}
26
27impl HyperliquidRestClient {
28 pub fn new(base_url: impl Into<String>) -> Self {
29 Self {
30 base_url: base_url.into(),
31 client: Client::new(),
32 }
33 }
34
35 async fn post_info<T: serde::de::DeserializeOwned>(
36 &self,
37 request: serde_json::Value,
38 ) -> Result<T> {
39 let response = self
40 .client
41 .post(&self.base_url)
42 .json(&request)
43 .send()
44 .await?
45 .error_for_status()?;
46
47 Ok(response.json::<T>().await?)
48 }
49
50 pub async fn fetch_meta(&self) -> Result<MetaResponse> {
51 self.post_info(serde_json::json!({ "type": "meta" })).await
52 }
53
54 pub async fn fetch_spot_meta(&self) -> Result<SpotMetaResponse> {
55 self.post_info(serde_json::json!({ "type": "spotMeta" }))
56 .await
57 }
58
59 pub async fn fetch_all_mids(&self) -> Result<std::collections::HashMap<String, String>> {
60 self.post_info(serde_json::json!({ "type": "allMids" }))
61 .await
62 }
63
64 pub async fn fetch_meta_and_asset_ctxs(&self) -> Result<(MetaResponse, Vec<AssetCtx>)> {
65 let value: serde_json::Value = self
66 .post_info(serde_json::json!({ "type": "metaAndAssetCtxs" }))
67 .await?;
68
69 let array = value
70 .as_array()
71 .ok_or_else(|| anyhow!("metaAndAssetCtxs response is not an array"))?;
72 let meta_value = array
73 .first()
74 .ok_or_else(|| anyhow!("metaAndAssetCtxs missing meta response"))?
75 .clone();
76 let ctxs_value = array
77 .get(1)
78 .filter(|v| !v.is_null())
79 .cloned()
80 .unwrap_or(serde_json::Value::Array(vec![]));
81
82 let meta: MetaResponse = serde_json::from_value(meta_value)
83 .map_err(|e| anyhow!("Failed to parse meta response: {e}"))?;
84 let ctxs: Vec<AssetCtx> = serde_json::from_value(ctxs_value).map_err(|e| {
85 anyhow!("Failed to parse asset contexts from metaAndAssetCtxs response: {e}")
86 })?;
87
88 Ok((meta, ctxs))
89 }
90
91 pub async fn fetch_l2_book(&self, coin: &str) -> Result<L2Book> {
92 self.post_info(serde_json::json!({ "type": "l2Book", "coin": coin }))
93 .await
94 }
95
96 pub async fn resolve_all_coins(&self) -> Result<Vec<String>> {
97 let meta = self.fetch_meta().await?;
98 Ok(meta.universe.into_iter().map(|asset| asset.name).collect())
99 }
100
101 pub async fn post_custom(&self, request: InfoRequest) -> Result<serde_json::Value> {
102 let response = self
103 .client
104 .post(&self.base_url)
105 .json(&request)
106 .send()
107 .await?
108 .error_for_status()?;
109
110 Ok(response.json::<serde_json::Value>().await?)
111 }
112}