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