Skip to main content

quicknode_hyperliquid_sdk/
info.rs

1//! Info API client for Hyperliquid.
2//!
3//! Provides read-only queries for market data, account state, and more.
4
5use serde_json::{json, Value};
6use std::sync::Arc;
7
8use crate::client::HyperliquidSDKInner;
9use crate::error::Result;
10
11/// Info API client
12pub struct Info {
13    inner: Arc<HyperliquidSDKInner>,
14}
15
16impl Info {
17    pub(crate) fn new(inner: Arc<HyperliquidSDKInner>) -> Self {
18        Self { inner }
19    }
20
21    // ──────────────────────────────────────────────────────────────────────────
22    // Market Metadata
23    // ──────────────────────────────────────────────────────────────────────────
24
25    /// Get exchange metadata (perp assets, decimals, etc.)
26    pub async fn meta(&self) -> Result<Value> {
27        self.inner.query_info(&json!({"type": "meta"})).await
28    }
29
30    /// Get spot metadata
31    pub async fn spot_meta(&self) -> Result<Value> {
32        self.inner.query_info(&json!({"type": "spotMeta"})).await
33    }
34
35    /// Get metadata with asset contexts (real-time funding, open interest)
36    pub async fn meta_and_asset_ctxs(&self) -> Result<Value> {
37        self.inner
38            .query_info(&json!({"type": "metaAndAssetCtxs"}))
39            .await
40    }
41
42    /// Get spot metadata with contexts
43    pub async fn spot_meta_and_asset_ctxs(&self) -> Result<Value> {
44        self.inner
45            .query_info(&json!({"type": "spotMetaAndAssetCtxs"}))
46            .await
47    }
48
49    /// Get exchange status
50    pub async fn exchange_status(&self) -> Result<Value> {
51        self.inner
52            .query_info(&json!({"type": "exchangeStatus"}))
53            .await
54    }
55
56    /// Get all perp DEXes (HIP-3)
57    pub async fn perp_dexes(&self) -> Result<Value> {
58        self.inner.query_info(&json!({"type": "perpDexs"})).await
59    }
60
61    /// Get perp categories
62    pub async fn perp_categories(&self) -> Result<Value> {
63        self.inner
64            .query_info(&json!({"type": "perpCategories"}))
65            .await
66    }
67
68    /// Get perp annotation for an asset
69    pub async fn perp_annotation(&self, asset: &str) -> Result<Value> {
70        self.inner
71            .query_info(&json!({"type": "perpAnnotation", "asset": asset}))
72            .await
73    }
74
75    // ──────────────────────────────────────────────────────────────────────────
76    // Pricing
77    // ──────────────────────────────────────────────────────────────────────────
78
79    /// Get all mid prices
80    pub async fn all_mids(&self, dex: Option<&str>) -> Result<Value> {
81        let mut body = json!({"type": "allMids"});
82        if let Some(d) = dex {
83            body["dex"] = json!(d);
84        }
85        self.inner.query_info(&body).await
86    }
87
88    /// Get mid price for a single asset
89    pub async fn get_mid(&self, asset: &str) -> Result<f64> {
90        let mids = self.all_mids(None).await?;
91        mids.get(asset)
92            .and_then(|v| v.as_str())
93            .and_then(|s| s.parse::<f64>().ok())
94            .ok_or_else(|| crate::Error::ValidationError(format!("No price for {}", asset)))
95    }
96
97    /// Get L2 order book
98    pub async fn l2_book(
99        &self,
100        coin: &str,
101        n_sig_figs: Option<u32>,
102        mantissa: Option<u32>,
103    ) -> Result<Value> {
104        let mut body = json!({"type": "l2Book", "coin": coin});
105        if let Some(n) = n_sig_figs {
106            body["nSigFigs"] = json!(n);
107        }
108        if let Some(m) = mantissa {
109            body["mantissa"] = json!(m);
110        }
111        self.inner.query_info(&body).await
112    }
113
114    /// Get recent trades
115    pub async fn recent_trades(&self, coin: &str) -> Result<Value> {
116        self.inner
117            .query_info(&json!({"type": "recentTrades", "coin": coin}))
118            .await
119    }
120
121    /// Get candlestick data
122    pub async fn candles(
123        &self,
124        coin: &str,
125        interval: &str,
126        start_time: u64,
127        end_time: Option<u64>,
128    ) -> Result<Value> {
129        let mut body = json!({
130            "type": "candleSnapshot",
131            "req": {
132                "coin": coin,
133                "interval": interval,
134                "startTime": start_time,
135            }
136        });
137        if let Some(end) = end_time {
138            body["req"]["endTime"] = json!(end);
139        }
140        self.inner.query_info(&body).await
141    }
142
143    /// Get funding history
144    pub async fn funding_history(
145        &self,
146        coin: &str,
147        start_time: u64,
148        end_time: Option<u64>,
149    ) -> Result<Value> {
150        let mut body = json!({
151            "type": "fundingHistory",
152            "coin": coin,
153            "startTime": start_time,
154        });
155        if let Some(end) = end_time {
156            body["endTime"] = json!(end);
157        }
158        self.inner.query_info(&body).await
159    }
160
161    /// Get predicted fundings
162    pub async fn predicted_fundings(&self) -> Result<Value> {
163        self.inner
164            .query_info(&json!({"type": "predictedFundings"}))
165            .await
166    }
167
168    // ──────────────────────────────────────────────────────────────────────────
169    // User Account Data
170    // ──────────────────────────────────────────────────────────────────────────
171
172    /// Get clearinghouse state (positions, margin)
173    pub async fn clearinghouse_state(&self, user: &str, dex: Option<&str>) -> Result<Value> {
174        let mut body = json!({"type": "clearinghouseState", "user": user});
175        if let Some(d) = dex {
176            body["dex"] = json!(d);
177        }
178        self.inner.query_info(&body).await
179    }
180
181    /// Get spot clearinghouse state (token balances)
182    pub async fn spot_clearinghouse_state(&self, user: &str) -> Result<Value> {
183        self.inner
184            .query_info(&json!({"type": "spotClearinghouseState", "user": user}))
185            .await
186    }
187
188    /// Get open orders
189    pub async fn open_orders(&self, user: &str, dex: Option<&str>) -> Result<Value> {
190        let mut body = json!({"type": "openOrders", "user": user});
191        if let Some(d) = dex {
192            body["dex"] = json!(d);
193        }
194        self.inner.query_info(&body).await
195    }
196
197    /// Get frontend open orders (enhanced)
198    pub async fn frontend_open_orders(&self, user: &str, dex: Option<&str>) -> Result<Value> {
199        let mut body = json!({"type": "frontendOpenOrders", "user": user});
200        if let Some(d) = dex {
201            body["dex"] = json!(d);
202        }
203        self.inner.query_info(&body).await
204    }
205
206    /// Get order status
207    pub async fn order_status(&self, user: &str, oid: u64, dex: Option<&str>) -> Result<Value> {
208        let mut body = json!({"type": "orderStatus", "user": user, "oid": oid});
209        if let Some(d) = dex {
210            body["dex"] = json!(d);
211        }
212        self.inner.query_info(&body).await
213    }
214
215    /// Get historical orders
216    pub async fn historical_orders(&self, user: &str) -> Result<Value> {
217        self.inner
218            .query_info(&json!({"type": "historicalOrders", "user": user}))
219            .await
220    }
221
222    /// Get user fills
223    pub async fn user_fills(&self, user: &str, aggregate_by_time: bool) -> Result<Value> {
224        self.inner
225            .query_info(&json!({
226                "type": "userFills",
227                "user": user,
228                "aggregateByTime": aggregate_by_time,
229            }))
230            .await
231    }
232
233    /// Get user fills by time range
234    pub async fn user_fills_by_time(
235        &self,
236        user: &str,
237        start_time: u64,
238        end_time: Option<u64>,
239    ) -> Result<Value> {
240        let mut body = json!({
241            "type": "userFillsByTime",
242            "user": user,
243            "startTime": start_time,
244        });
245        if let Some(end) = end_time {
246            body["endTime"] = json!(end);
247        }
248        self.inner.query_info(&body).await
249    }
250
251    /// Get user funding payments
252    pub async fn user_funding(
253        &self,
254        user: &str,
255        start_time: Option<u64>,
256        end_time: Option<u64>,
257    ) -> Result<Value> {
258        let mut body = json!({"type": "userFunding", "user": user});
259        if let Some(start) = start_time {
260            body["startTime"] = json!(start);
261        }
262        if let Some(end) = end_time {
263            body["endTime"] = json!(end);
264        }
265        self.inner.query_info(&body).await
266    }
267
268    /// Get user fees
269    pub async fn user_fees(&self, user: &str) -> Result<Value> {
270        self.inner
271            .query_info(&json!({"type": "userFees", "user": user}))
272            .await
273    }
274
275    /// Get user rate limit
276    pub async fn user_rate_limit(&self, user: &str) -> Result<Value> {
277        self.inner
278            .query_info(&json!({"type": "userRateLimit", "user": user}))
279            .await
280    }
281
282    /// Get user role
283    pub async fn user_role(&self, user: &str) -> Result<Value> {
284        self.inner
285            .query_info(&json!({"type": "userRole", "user": user}))
286            .await
287    }
288
289    /// Get sub-accounts
290    pub async fn sub_accounts(&self, user: &str) -> Result<Value> {
291        self.inner
292            .query_info(&json!({"type": "subAccounts", "user": user}))
293            .await
294    }
295
296    /// Get extra agents (API keys)
297    pub async fn extra_agents(&self, user: &str) -> Result<Value> {
298        self.inner
299            .query_info(&json!({"type": "extraAgents", "user": user}))
300            .await
301    }
302
303    /// Get portfolio history
304    pub async fn portfolio(&self, user: &str) -> Result<Value> {
305        self.inner
306            .query_info(&json!({"type": "portfolio", "user": user}))
307            .await
308    }
309
310    /// Get comprehensive web data
311    pub async fn web_data2(&self, user: &str) -> Result<Value> {
312        self.inner
313            .query_info(&json!({"type": "webData2", "user": user}))
314            .await
315    }
316
317    // ──────────────────────────────────────────────────────────────────────────
318    // Batch Queries
319    // ──────────────────────────────────────────────────────────────────────────
320
321    /// Batch query clearinghouse states for multiple users
322    pub async fn batch_clearinghouse_states(&self, users: &[&str]) -> Result<Value> {
323        self.inner
324            .query_info(&json!({
325                "type": "batchClearinghouseStates",
326                "users": users,
327            }))
328            .await
329    }
330
331    // ──────────────────────────────────────────────────────────────────────────
332    // Vaults
333    // ──────────────────────────────────────────────────────────────────────────
334
335    /// Get all vault summaries
336    pub async fn vault_summaries(&self) -> Result<Value> {
337        self.inner
338            .query_info(&json!({"type": "vaultSummaries"}))
339            .await
340    }
341
342    /// Get vault details
343    pub async fn vault_details(&self, vault_address: &str, user: Option<&str>) -> Result<Value> {
344        let mut body = json!({"type": "vaultDetails", "vaultAddress": vault_address});
345        if let Some(u) = user {
346            body["user"] = json!(u);
347        }
348        self.inner.query_info(&body).await
349    }
350
351    /// Get user vault equities
352    pub async fn user_vault_equities(&self, user: &str) -> Result<Value> {
353        self.inner
354            .query_info(&json!({"type": "userVaultEquities", "user": user}))
355            .await
356    }
357
358    /// Get leading vaults
359    pub async fn leading_vaults(&self, user: &str) -> Result<Value> {
360        self.inner
361            .query_info(&json!({"type": "leadingVaults", "user": user}))
362            .await
363    }
364
365    // ──────────────────────────────────────────────────────────────────────────
366    // Delegation & Staking
367    // ──────────────────────────────────────────────────────────────────────────
368
369    /// Get delegations
370    pub async fn delegations(&self, user: &str) -> Result<Value> {
371        self.inner
372            .query_info(&json!({"type": "delegations", "user": user}))
373            .await
374    }
375
376    /// Get delegator summary
377    pub async fn delegator_summary(&self, user: &str) -> Result<Value> {
378        self.inner
379            .query_info(&json!({"type": "delegatorSummary", "user": user}))
380            .await
381    }
382
383    /// Get delegator history
384    pub async fn delegator_history(&self, user: &str) -> Result<Value> {
385        self.inner
386            .query_info(&json!({"type": "delegatorHistory", "user": user}))
387            .await
388    }
389
390    /// Get delegator rewards
391    pub async fn delegator_rewards(&self, user: &str) -> Result<Value> {
392        self.inner
393            .query_info(&json!({"type": "delegatorRewards", "user": user}))
394            .await
395    }
396
397    // ──────────────────────────────────────────────────────────────────────────
398    // TWAP
399    // ──────────────────────────────────────────────────────────────────────────
400
401    /// Get user TWAP slice fills
402    pub async fn user_twap_slice_fills(&self, user: &str, limit: Option<u32>) -> Result<Value> {
403        let mut body = json!({"type": "userTwapSliceFills", "user": user});
404        if let Some(l) = limit {
405            body["limit"] = json!(l);
406        }
407        self.inner.query_info(&body).await
408    }
409
410    /// Get user TWAP history
411    pub async fn user_twap_history(&self, user: &str) -> Result<Value> {
412        self.inner
413            .query_info(&json!({"type": "userTwapHistory", "user": user}))
414            .await
415    }
416
417    // ──────────────────────────────────────────────────────────────────────────
418    // Borrow/Lend
419    // ──────────────────────────────────────────────────────────────────────────
420
421    /// Get borrow/lend user state
422    pub async fn borrow_lend_user_state(&self, user: &str) -> Result<Value> {
423        self.inner
424            .query_info(&json!({"type": "borrowLendUserState", "user": user}))
425            .await
426    }
427
428    /// Get borrow/lend reserve state
429    pub async fn borrow_lend_reserve_state(&self, token: &str) -> Result<Value> {
430        self.inner
431            .query_info(&json!({"type": "borrowLendReserveState", "token": token}))
432            .await
433    }
434
435    /// Get all borrow/lend reserve states
436    pub async fn all_borrow_lend_reserve_states(&self) -> Result<Value> {
437        self.inner
438            .query_info(&json!({"type": "allBorrowLendReserveStates"}))
439            .await
440    }
441
442    // ──────────────────────────────────────────────────────────────────────────
443    // Account Abstraction
444    // ──────────────────────────────────────────────────────────────────────────
445
446    /// Get user abstraction mode
447    pub async fn user_abstraction(&self, user: &str) -> Result<Value> {
448        self.inner
449            .query_info(&json!({"type": "userAbstraction", "user": user}))
450            .await
451    }
452
453    /// Get user DEX abstraction mode
454    pub async fn user_dex_abstraction(&self, user: &str) -> Result<Value> {
455        self.inner
456            .query_info(&json!({"type": "userDexAbstraction", "user": user}))
457            .await
458    }
459
460    // ──────────────────────────────────────────────────────────────────────────
461    // Misc
462    // ──────────────────────────────────────────────────────────────────────────
463
464    /// Get liquidatable positions
465    pub async fn liquidatable(&self) -> Result<Value> {
466        self.inner
467            .query_info(&json!({"type": "liquidatable"}))
468            .await
469    }
470
471    /// Get max market order notionals
472    pub async fn max_market_order_ntls(&self) -> Result<Value> {
473        self.inner
474            .query_info(&json!({"type": "maxMarketOrderNtls"}))
475            .await
476    }
477
478    /// Get max builder fee
479    pub async fn max_builder_fee(&self, user: &str, builder: &str) -> Result<Value> {
480        self.inner
481            .query_info(&json!({
482                "type": "maxBuilderFee",
483                "user": user,
484                "builder": builder,
485            }))
486            .await
487    }
488
489    /// Get active asset data
490    pub async fn active_asset_data(&self, user: &str, asset: &str) -> Result<Value> {
491        self.inner
492            .query_info(&json!({
493                "type": "activeAssetData",
494                "user": user,
495                "asset": asset,
496            }))
497            .await
498    }
499
500    // ──────────────────────────────────────────────────────────────────────────
501    // Tokens / Spot
502    // ──────────────────────────────────────────────────────────────────────────
503
504    /// Get token details
505    pub async fn token_details(&self, token_id: &str) -> Result<Value> {
506        self.inner
507            .query_info(&json!({"type": "tokenDetails", "tokenId": token_id}))
508            .await
509    }
510
511    /// Get spot deployment state for user
512    pub async fn spot_deploy_state(&self, user: &str) -> Result<Value> {
513        self.inner
514            .query_info(&json!({"type": "spotDeployState", "user": user}))
515            .await
516    }
517
518    /// Get spot pair deploy auction status
519    pub async fn spot_pair_deploy_auction_status(&self) -> Result<Value> {
520        self.inner
521            .query_info(&json!({"type": "spotPairDeployAuctionStatus"}))
522            .await
523    }
524
525    // ──────────────────────────────────────────────────────────────────────────
526    // Additional Methods
527    // ──────────────────────────────────────────────────────────────────────────
528
529    /// Get user's referral information
530    pub async fn referral(&self, user: &str) -> Result<Value> {
531        self.inner
532            .query_info(&json!({"type": "referral", "user": user}))
533            .await
534    }
535
536    /// Get user's non-funding ledger updates (deposits, withdrawals, transfers)
537    pub async fn user_non_funding_ledger_updates(
538        &self,
539        user: &str,
540        start_time: Option<u64>,
541        end_time: Option<u64>,
542    ) -> Result<Value> {
543        let mut body = json!({"type": "userNonFundingLedgerUpdates", "user": user});
544        if let Some(start) = start_time {
545            body["startTime"] = json!(start);
546        }
547        if let Some(end) = end_time {
548            body["endTime"] = json!(end);
549        }
550        self.inner.query_info(&body).await
551    }
552
553    /// Get multi-sig signers for a user
554    pub async fn user_to_multi_sig_signers(&self, user: &str) -> Result<Value> {
555        self.inner
556            .query_info(&json!({"type": "userToMultiSigSigners", "user": user}))
557            .await
558    }
559
560    /// Get gossip root IPs for the network
561    pub async fn gossip_root_ips(&self) -> Result<Value> {
562        self.inner
563            .query_info(&json!({"type": "gossipRootIps"}))
564            .await
565    }
566
567    /// Get perpetual deploy auction status
568    pub async fn perp_deploy_auction_status(&self) -> Result<Value> {
569        self.inner
570            .query_info(&json!({"type": "perpDeployAuctionStatus"}))
571            .await
572    }
573
574    /// Get perps that are at their open interest cap
575    pub async fn perps_at_open_interest_cap(&self) -> Result<Value> {
576        self.inner
577            .query_info(&json!({"type": "perpsAtOpenInterestCap"}))
578            .await
579    }
580
581    /// Get L1 validator votes
582    pub async fn validator_l1_votes(&self) -> Result<Value> {
583        self.inner
584            .query_info(&json!({"type": "validatorL1Votes"}))
585            .await
586    }
587
588    /// Get list of approved builders for a user
589    pub async fn approved_builders(&self, user: &str) -> Result<Value> {
590        self.inner
591            .query_info(&json!({"type": "approvedBuilders", "user": user}))
592            .await
593    }
594
595    // ──────────────────────────────────────────────────────────────────────────
596    // Extended Perp DEX Info
597    // ──────────────────────────────────────────────────────────────────────────
598
599    /// Get consolidated universe, margin tables, asset contexts across all DEXs
600    pub async fn all_perp_metas(&self) -> Result<Value> {
601        self.inner
602            .query_info(&json!({"type": "allPerpMetas"}))
603            .await
604    }
605
606    /// Get OI caps and transfer limits for builder-deployed markets
607    pub async fn perp_dex_limits(&self, dex: &str) -> Result<Value> {
608        self.inner
609            .query_info(&json!({"type": "perpDexLimits", "dex": dex}))
610            .await
611    }
612
613    /// Get total net deposits for builder-deployed markets
614    pub async fn perp_dex_status(&self, dex: &str) -> Result<Value> {
615        self.inner
616            .query_info(&json!({"type": "perpDexStatus", "dex": dex}))
617            .await
618    }
619
620    // ──────────────────────────────────────────────────────────────────────────
621    // Aligned Quote Token
622    // ──────────────────────────────────────────────────────────────────────────
623
624    /// Get aligned quote token information
625    pub async fn aligned_quote_token_info(&self, token: u32) -> Result<Value> {
626        self.inner
627            .query_info(&json!({"type": "alignedQuoteTokenInfo", "token": token}))
628            .await
629    }
630}