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, MetaAndAssetCtxs, 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    MetaAndAssetCtxs,
63    SpotMetaAndAssetCtxs,
64    AllMids,
65    UserFills {
66        user: H160,
67    },
68    #[serde(rename_all = "camelCase")]
69    UserFillsByTime {
70        user: H160,
71        /// Start time in milliseconds, inclusive
72        start_time: u64,
73        /// End time in milliseconds, inclusive. Defaults to current time.
74        #[serde(skip_serializing_if = "Option::is_none")]
75        end_time: Option<u64>,
76        /// When true, partial fills are combined when a crossing order gets filled by multiple different resting orders. Resting orders filled by multiple crossing orders will not be aggregated.
77        #[serde(skip_serializing_if = "Option::is_none")]
78        aggregate_by_time: Option<bool>,
79    },
80    #[serde(rename_all = "camelCase")]
81    FundingHistory {
82        coin: String,
83        start_time: u64,
84        end_time: Option<u64>,
85    },
86    #[serde(rename_all = "camelCase")]
87    UserFunding {
88        user: H160,
89        start_time: u64,
90        end_time: Option<u64>,
91    },
92    L2Book {
93        coin: String,
94    },
95    RecentTrades {
96        coin: String,
97    },
98    #[serde(rename_all = "camelCase")]
99    CandleSnapshot {
100        req: CandleSnapshotRequest,
101    },
102    Referral {
103        user: H160,
104    },
105    PerpsAtOpenInterestCap,
106}
107
108pub struct InfoClient {
109    pub http_client: HttpClient,
110    pub(crate) ws_manager: Option<WsManager>,
111}
112
113impl InfoClient {
114    pub async fn new(client: Option<Client>, base_url: Option<BaseUrl>) -> Result<InfoClient> {
115        let client = client.unwrap_or_default();
116        let base_url = base_url.unwrap_or(BaseUrl::Mainnet).get_url();
117
118        Ok(InfoClient {
119            http_client: HttpClient { client, base_url },
120            ws_manager: None,
121        })
122    }
123
124    pub async fn subscribe(
125        &mut self,
126        subscription: Subscription,
127        sender_channel: UnboundedSender<Message>,
128    ) -> Result<u32> {
129        if self.ws_manager.is_none() {
130            let ws_manager =
131                WsManager::new(format!("ws{}/ws", &self.http_client.base_url[4..])).await?;
132            self.ws_manager = Some(ws_manager);
133        }
134
135        let identifier =
136            serde_json::to_string(&subscription).map_err(|e| Error::JsonParse(e.to_string()))?;
137
138        self.ws_manager
139            .as_mut()
140            .ok_or(Error::WsManagerNotFound)?
141            .add_subscription(identifier, sender_channel)
142            .await
143    }
144
145    pub async fn unsubscribe(&mut self, subscription_id: u32) -> Result<()> {
146        if self.ws_manager.is_none() {
147            let ws_manager =
148                WsManager::new(format!("ws{}/ws", &self.http_client.base_url[4..])).await?;
149            self.ws_manager = Some(ws_manager);
150        }
151
152        self.ws_manager
153            .as_mut()
154            .ok_or(Error::WsManagerNotFound)?
155            .remove_subscription(subscription_id)
156            .await
157    }
158
159    async fn send_info_request<T: for<'a> Deserialize<'a>>(
160        &self,
161        info_request: InfoRequest,
162    ) -> Result<T> {
163        let data =
164            serde_json::to_string(&info_request).map_err(|e| Error::JsonParse(e.to_string()))?;
165
166        let return_data = self.http_client.post("/info", data).await?;
167        serde_json::from_str(&return_data).map_err(|e| Error::JsonParse(e.to_string()))
168    }
169
170    pub async fn open_orders(&self, address: H160) -> Result<Vec<OpenOrdersResponse>> {
171        let input = InfoRequest::OpenOrders { user: address };
172        self.send_info_request(input).await
173    }
174
175    pub async fn user_state(&self, address: H160) -> Result<UserStateResponse> {
176        let input = InfoRequest::UserState { user: address };
177        self.send_info_request(input).await
178    }
179
180    pub async fn user_states(&self, addresses: Vec<H160>) -> Result<Vec<UserStateResponse>> {
181        let input = InfoRequest::UserStates { users: addresses };
182        self.send_info_request(input).await
183    }
184
185    pub async fn user_token_balances(&self, address: H160) -> Result<UserTokenBalanceResponse> {
186        let input = InfoRequest::UserTokenBalances { user: address };
187        self.send_info_request(input).await
188    }
189
190    pub async fn user_fees(&self, address: H160) -> Result<UserFeesResponse> {
191        let input = InfoRequest::UserFees { user: address };
192        self.send_info_request(input).await
193    }
194
195    pub async fn meta(&self) -> Result<Meta> {
196        let input = InfoRequest::Meta;
197        self.send_info_request(input).await
198    }
199
200    pub async fn spot_meta(&self) -> Result<SpotMeta> {
201        let input = InfoRequest::SpotMeta;
202        self.send_info_request(input).await
203    }
204
205    pub async fn meta_and_asset_contexts(&self) -> Result<MetaAndAssetCtxs> {
206        let input = InfoRequest::MetaAndAssetCtxs;
207        self.send_info_request(input).await
208    }
209
210    pub async fn spot_meta_and_asset_contexts(&self) -> Result<Vec<SpotMetaAndAssetCtxs>> {
211        let input = InfoRequest::SpotMetaAndAssetCtxs;
212        self.send_info_request(input).await
213    }
214
215    pub async fn all_mids(&self) -> Result<HashMap<String, String>> {
216        let input = InfoRequest::AllMids;
217        self.send_info_request(input).await
218    }
219
220    pub async fn user_fills(&self, address: H160) -> Result<Vec<UserFillsResponse>> {
221        let input = InfoRequest::UserFills { user: address };
222        self.send_info_request(input).await
223    }
224
225    pub async fn user_fills_by_time(
226        &self,
227        address: H160,
228        start_time: u64,
229        end_time: Option<u64>,
230        aggregate_by_time: Option<bool>,
231    ) -> Result<Vec<UserFillsResponse>> {
232        let input = InfoRequest::UserFillsByTime {
233            user: address,
234            start_time,
235            end_time,
236            aggregate_by_time,
237        };
238
239        self.send_info_request(input).await
240    }
241
242    pub async fn funding_history(
243        &self,
244        coin: String,
245        start_time: u64,
246        end_time: Option<u64>,
247    ) -> Result<Vec<FundingHistoryResponse>> {
248        let input = InfoRequest::FundingHistory {
249            coin,
250            start_time,
251            end_time,
252        };
253        self.send_info_request(input).await
254    }
255
256    pub async fn user_funding_history(
257        &self,
258        user: H160,
259        start_time: u64,
260        end_time: Option<u64>,
261    ) -> Result<Vec<UserFundingResponse>> {
262        let input = InfoRequest::UserFunding {
263            user,
264            start_time,
265            end_time,
266        };
267        self.send_info_request(input).await
268    }
269
270    pub async fn recent_trades(&self, coin: String) -> Result<Vec<RecentTradesResponse>> {
271        let input = InfoRequest::RecentTrades { coin };
272        self.send_info_request(input).await
273    }
274
275    pub async fn l2_snapshot(&self, coin: String) -> Result<L2SnapshotResponse> {
276        let input = InfoRequest::L2Book { coin };
277        self.send_info_request(input).await
278    }
279
280    pub async fn candles_snapshot(
281        &self,
282        coin: String,
283        interval: String,
284        start_time: u64,
285        end_time: u64,
286    ) -> Result<Vec<CandlesSnapshotResponse>> {
287        let input = InfoRequest::CandleSnapshot {
288            req: CandleSnapshotRequest {
289                coin,
290                interval,
291                start_time,
292                end_time,
293            },
294        };
295        self.send_info_request(input).await
296    }
297
298    pub async fn query_order_by_oid(&self, address: H160, oid: u64) -> Result<OrderStatusResponse> {
299        let input = InfoRequest::OrderStatus { user: address, oid };
300        self.send_info_request(input).await
301    }
302
303    pub async fn query_order_by_cloid(
304        &self,
305        address: H160,
306        cloid: String,
307    ) -> Result<OrderStatusResponse> {
308        let input = InfoRequest::OrderStatusByCloid {
309            user: address,
310            oid: cloid,
311        };
312        self.send_info_request(input).await
313    }
314
315    pub async fn query_referral_state(&self, address: H160) -> Result<ReferralResponse> {
316        let input = InfoRequest::Referral { user: address };
317        self.send_info_request(input).await
318    }
319
320    pub async fn perps_at_open_interest_cap(&self) -> Result<Vec<String>> {
321        let input = InfoRequest::PerpsAtOpenInterestCap;
322        self.send_info_request(input).await
323    }
324}