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