hyperliquid_rust_sdk_abrkn/info/
info_client.rs

1use crate::{
2    info::{
3        CandlesSnapshotResponse, FundingHistoryResponse, L2SnapshotResponse, OpenOrdersResponse,
4        RecentTradesResponse, UserFillsResponse, UserStateResponse,
5    },
6    meta::{Meta, SpotMeta, SpotMetaAndAssetCtxs},
7    prelude::*,
8    req::HttpClient,
9    ws::{Subscription, WsManager},
10    BaseUrl, Error, Message, OrderStatusResponse, ReferralResponse, UserFeesResponse,
11    UserFundingResponse, UserTokenBalanceResponse,
12};
13
14use ethers::types::H160;
15use reqwest::Client;
16use serde::{Deserialize, Serialize};
17use std::collections::HashMap;
18use tokio::sync::mpsc::UnboundedSender;
19
20#[derive(Deserialize, Serialize, Debug, Clone)]
21#[serde(rename_all = "camelCase")]
22pub struct CandleSnapshotRequest {
23    coin: String,
24    interval: String,
25    start_time: u64,
26    end_time: u64,
27}
28
29#[derive(Deserialize, Serialize, Debug, Clone)]
30#[serde(tag = "type")]
31#[serde(rename_all = "camelCase")]
32pub enum InfoRequest {
33    #[serde(rename = "clearinghouseState")]
34    UserState {
35        user: H160,
36    },
37    #[serde(rename = "batchClearinghouseStates")]
38    UserStates {
39        users: Vec<H160>,
40    },
41    #[serde(rename = "spotClearinghouseState")]
42    UserTokenBalances {
43        user: H160,
44    },
45    UserFees {
46        user: H160,
47    },
48    OpenOrders {
49        user: H160,
50    },
51    OrderStatus {
52        user: H160,
53        oid: u64,
54    },
55    #[serde(rename = "orderStatus")]
56    OrderStatusByCloid {
57        user: H160,
58        oid: String,
59    },
60    Meta,
61    SpotMeta,
62    SpotMetaAndAssetCtxs,
63    AllMids,
64    UserFills {
65        user: H160,
66    },
67    #[serde(rename_all = "camelCase")]
68    FundingHistory {
69        coin: String,
70        start_time: u64,
71        end_time: Option<u64>,
72    },
73    #[serde(rename_all = "camelCase")]
74    UserFunding {
75        user: H160,
76        start_time: u64,
77        end_time: Option<u64>,
78    },
79    L2Book {
80        coin: String,
81    },
82    RecentTrades {
83        coin: String,
84    },
85    #[serde(rename_all = "camelCase")]
86    CandleSnapshot {
87        req: CandleSnapshotRequest,
88    },
89    Referral {
90        user: H160,
91    },
92}
93
94pub struct InfoClient {
95    pub http_client: HttpClient,
96    pub(crate) ws_manager: Option<WsManager>,
97}
98
99impl InfoClient {
100    pub async fn new(client: Option<Client>, base_url: Option<BaseUrl>) -> Result<InfoClient> {
101        let client = client.unwrap_or_default();
102        let base_url = base_url.unwrap_or(BaseUrl::Mainnet).get_url();
103
104        Ok(InfoClient {
105            http_client: HttpClient { client, base_url },
106            ws_manager: None,
107        })
108    }
109
110    pub async fn subscribe(
111        &mut self,
112        subscription: Subscription,
113        sender_channel: UnboundedSender<Message>,
114    ) -> Result<u32> {
115        if self.ws_manager.is_none() {
116            let ws_manager =
117                WsManager::new(format!("ws{}/ws", &self.http_client.base_url[4..])).await?;
118            self.ws_manager = Some(ws_manager);
119        }
120
121        let identifier =
122            serde_json::to_string(&subscription).map_err(|e| Error::JsonParse(e.to_string()))?;
123
124        self.ws_manager
125            .as_mut()
126            .ok_or(Error::WsManagerNotFound)?
127            .add_subscription(identifier, sender_channel)
128            .await
129    }
130
131    pub async fn unsubscribe(&mut self, subscription_id: u32) -> Result<()> {
132        if self.ws_manager.is_none() {
133            let ws_manager =
134                WsManager::new(format!("ws{}/ws", &self.http_client.base_url[4..])).await?;
135            self.ws_manager = Some(ws_manager);
136        }
137
138        self.ws_manager
139            .as_mut()
140            .ok_or(Error::WsManagerNotFound)?
141            .remove_subscription(subscription_id)
142            .await
143    }
144
145    async fn send_info_request<T: for<'a> Deserialize<'a>>(
146        &self,
147        info_request: InfoRequest,
148    ) -> Result<T> {
149        let data =
150            serde_json::to_string(&info_request).map_err(|e| Error::JsonParse(e.to_string()))?;
151
152        let return_data = self.http_client.post("/info", data).await?;
153        serde_json::from_str(&return_data).map_err(|e| Error::JsonParse(e.to_string()))
154    }
155
156    pub async fn open_orders(&self, address: H160) -> Result<Vec<OpenOrdersResponse>> {
157        let input = InfoRequest::OpenOrders { user: address };
158        self.send_info_request(input).await
159    }
160
161    pub async fn user_state(&self, address: H160) -> Result<UserStateResponse> {
162        let input = InfoRequest::UserState { user: address };
163        self.send_info_request(input).await
164    }
165
166    pub async fn user_states(&self, addresses: Vec<H160>) -> Result<Vec<UserStateResponse>> {
167        let input = InfoRequest::UserStates { users: addresses };
168        self.send_info_request(input).await
169    }
170
171    pub async fn user_token_balances(&self, address: H160) -> Result<UserTokenBalanceResponse> {
172        let input = InfoRequest::UserTokenBalances { user: address };
173        self.send_info_request(input).await
174    }
175
176    pub async fn user_fees(&self, address: H160) -> Result<UserFeesResponse> {
177        let input = InfoRequest::UserFees { user: address };
178        self.send_info_request(input).await
179    }
180
181    pub async fn meta(&self) -> Result<Meta> {
182        let input = InfoRequest::Meta;
183        self.send_info_request(input).await
184    }
185
186    pub async fn spot_meta(&self) -> Result<SpotMeta> {
187        let input = InfoRequest::SpotMeta;
188        self.send_info_request(input).await
189    }
190
191    pub async fn spot_meta_and_asset_contexts(&self) -> Result<Vec<SpotMetaAndAssetCtxs>> {
192        let input = InfoRequest::SpotMetaAndAssetCtxs;
193        self.send_info_request(input).await
194    }
195
196    pub async fn all_mids(&self) -> Result<HashMap<String, String>> {
197        let input = InfoRequest::AllMids;
198        self.send_info_request(input).await
199    }
200
201    pub async fn user_fills(&self, address: H160) -> Result<Vec<UserFillsResponse>> {
202        let input = InfoRequest::UserFills { user: address };
203        self.send_info_request(input).await
204    }
205
206    pub async fn funding_history(
207        &self,
208        coin: String,
209        start_time: u64,
210        end_time: Option<u64>,
211    ) -> Result<Vec<FundingHistoryResponse>> {
212        let input = InfoRequest::FundingHistory {
213            coin,
214            start_time,
215            end_time,
216        };
217        self.send_info_request(input).await
218    }
219
220    pub async fn user_funding_history(
221        &self,
222        user: H160,
223        start_time: u64,
224        end_time: Option<u64>,
225    ) -> Result<Vec<UserFundingResponse>> {
226        let input = InfoRequest::UserFunding {
227            user,
228            start_time,
229            end_time,
230        };
231        self.send_info_request(input).await
232    }
233
234    pub async fn recent_trades(&self, coin: String) -> Result<Vec<RecentTradesResponse>> {
235        let input = InfoRequest::RecentTrades { coin };
236        self.send_info_request(input).await
237    }
238
239    pub async fn l2_snapshot(&self, coin: String) -> Result<L2SnapshotResponse> {
240        let input = InfoRequest::L2Book { coin };
241        self.send_info_request(input).await
242    }
243
244    pub async fn candles_snapshot(
245        &self,
246        coin: String,
247        interval: String,
248        start_time: u64,
249        end_time: u64,
250    ) -> Result<Vec<CandlesSnapshotResponse>> {
251        let input = InfoRequest::CandleSnapshot {
252            req: CandleSnapshotRequest {
253                coin,
254                interval,
255                start_time,
256                end_time,
257            },
258        };
259        self.send_info_request(input).await
260    }
261
262    pub async fn query_order_by_oid(&self, address: H160, oid: u64) -> Result<OrderStatusResponse> {
263        let input = InfoRequest::OrderStatus { user: address, oid };
264        self.send_info_request(input).await
265    }
266
267    pub async fn query_order_by_cloid(
268        &self,
269        address: H160,
270        cloid: String,
271    ) -> Result<OrderStatusResponse> {
272        let input = InfoRequest::OrderStatusByCloid {
273            user: address,
274            oid: cloid,
275        };
276        self.send_info_request(input).await
277    }
278
279    pub async fn query_referral_state(&self, address: H160) -> Result<ReferralResponse> {
280        let input = InfoRequest::Referral { user: address };
281        self.send_info_request(input).await
282    }
283}