df_hyperliquid_core/live/subscription/
mod.rs1mod parse;
6mod wire;
7
8use crate::markets::Interval;
9use crate::utils::Address;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub enum Subscription {
15 AllMids {
17 dex: String,
18 },
19 Notification {
21 user: Address,
22 },
23 WebData3 {
25 user: Address,
26 },
27 TwapStates {
28 user: Address,
29 dex: String,
30 },
31 ClearinghouseState {
32 user: Address,
33 dex: String,
34 },
35 OpenOrders {
36 user: Address,
37 dex: String,
38 },
39 Candle {
40 coin: String,
41 interval: Interval,
42 },
43 L2Book {
46 coin: String,
47 n_sig_figs: Option<u8>,
48 mantissa: Option<u8>,
49 fast: bool,
50 },
51 Trades {
52 coin: String,
53 },
54 OrderUpdates {
55 user: Address,
56 },
57 UserEvents {
59 user: Address,
60 },
61 UserFills {
63 user: Address,
64 aggregate_by_time: bool,
65 },
66 UserFundings {
67 user: Address,
68 },
69 UserNonFundingLedgerUpdates {
70 user: Address,
71 },
72 ActiveAssetCtx {
74 coin: String,
75 },
76 ActiveAssetData {
78 user: Address,
79 coin: String,
80 },
81 UserTwapSliceFills {
82 user: Address,
83 },
84 UserTwapHistory {
85 user: Address,
86 },
87 Bbo {
89 coin: String,
90 },
91 SpotState {
94 user: Address,
95 ignore_portfolio_margin: bool,
96 },
97 AllDexsClearinghouseState {
98 user: Address,
99 },
100 AllDexsAssetCtxs,
102 OutcomeMetaUpdates,
104 FastAssetCtxs,
106 AssetCtxs {
108 dex: String,
109 },
110 SpotAssetCtxs,
112 UserHistoricalOrders {
114 user: Address,
115 },
116}
117
118#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
120pub enum BadSubscription {
121 #[error("a subscription is an object")]
122 NotAnObject,
123 #[error("`{0}` is not a subscription the venue serves")]
124 UnknownType(String),
125 #[error("`{kind}` needs `{field}`")]
126 Missing { kind: &'static str, field: &'static str },
127 #[error("`{field}` of `{kind}` must be {expected}")]
128 Wrong { kind: &'static str, field: &'static str, expected: &'static str },
129 #[error("a coin is never an empty string")]
130 EmptyCoin,
131 #[error("nSigFigs is 2, 3, 4 or 5, not {0}")]
132 SigFigs(u8),
133 #[error("mantissa is 1, 2 or 5, and only with nSigFigs 5; {0} is refused here")]
134 Mantissa(u8),
135}
136
137impl Subscription {
138 #[must_use]
140 pub const fn kind(&self) -> &'static str {
141 match self {
142 Subscription::AllMids { .. } => "allMids",
143 Subscription::Notification { .. } => "notification",
144 Subscription::WebData3 { .. } => "webData3",
145 Subscription::TwapStates { .. } => "twapStates",
146 Subscription::ClearinghouseState { .. } => "clearinghouseState",
147 Subscription::OpenOrders { .. } => "openOrders",
148 Subscription::Candle { .. } => "candle",
149 Subscription::L2Book { .. } => "l2Book",
150 Subscription::Trades { .. } => "trades",
151 Subscription::OrderUpdates { .. } => "orderUpdates",
152 Subscription::UserEvents { .. } => "userEvents",
153 Subscription::UserFills { .. } => "userFills",
154 Subscription::UserFundings { .. } => "userFundings",
155 Subscription::UserNonFundingLedgerUpdates { .. } => "userNonFundingLedgerUpdates",
156 Subscription::ActiveAssetCtx { .. } => "activeAssetCtx",
157 Subscription::ActiveAssetData { .. } => "activeAssetData",
158 Subscription::UserTwapSliceFills { .. } => "userTwapSliceFills",
159 Subscription::UserTwapHistory { .. } => "userTwapHistory",
160 Subscription::Bbo { .. } => "bbo",
161 Subscription::SpotState { .. } => "spotState",
162 Subscription::AllDexsClearinghouseState { .. } => "allDexsClearinghouseState",
163 Subscription::AllDexsAssetCtxs => "allDexsAssetCtxs",
164 Subscription::OutcomeMetaUpdates => "outcomeMetaUpdates",
165 Subscription::FastAssetCtxs => "fastAssetCtxs",
166 Subscription::AssetCtxs { .. } => "assetCtxs",
167 Subscription::SpotAssetCtxs => "spotAssetCtxs",
168 Subscription::UserHistoricalOrders { .. } => "userHistoricalOrders",
169 }
170 }
171
172 #[must_use]
176 pub fn channel(&self) -> &'static str {
177 match self {
178 Subscription::UserEvents { .. } => "user",
179 Subscription::ActiveAssetCtx { coin } if is_spot(coin) => "activeSpotAssetCtx",
180 other => other.kind(),
181 }
182 }
183
184 #[must_use]
187 pub const fn user(&self) -> Option<Address> {
188 match self {
189 Subscription::Notification { user }
190 | Subscription::WebData3 { user }
191 | Subscription::TwapStates { user, .. }
192 | Subscription::ClearinghouseState { user, .. }
193 | Subscription::OpenOrders { user, .. }
194 | Subscription::OrderUpdates { user }
195 | Subscription::UserEvents { user }
196 | Subscription::UserFills { user, .. }
197 | Subscription::UserFundings { user }
198 | Subscription::UserNonFundingLedgerUpdates { user }
199 | Subscription::ActiveAssetData { user, .. }
200 | Subscription::UserTwapSliceFills { user }
201 | Subscription::UserTwapHistory { user }
202 | Subscription::SpotState { user, .. }
203 | Subscription::AllDexsClearinghouseState { user }
204 | Subscription::UserHistoricalOrders { user } => Some(*user),
205 _ => None,
206 }
207 }
208
209 #[must_use]
211 pub fn coin(&self) -> Option<&str> {
212 match self {
213 Subscription::Candle { coin, .. }
214 | Subscription::L2Book { coin, .. }
215 | Subscription::Trades { coin }
216 | Subscription::ActiveAssetCtx { coin }
217 | Subscription::ActiveAssetData { coin, .. }
218 | Subscription::Bbo { coin } => Some(coin),
219 _ => None,
220 }
221 }
222
223 pub fn check(&self) -> Result<(), BadSubscription> {
227 if self.coin().is_some_and(str::is_empty) {
228 return Err(BadSubscription::EmptyCoin);
229 }
230 if let Subscription::L2Book { n_sig_figs, mantissa, .. } = self {
231 if let Some(figures) = n_sig_figs.filter(|figures| !(2..=5).contains(figures)) {
232 return Err(BadSubscription::SigFigs(figures));
233 }
234 if let Some(mantissa) = mantissa.filter(|mantissa| *n_sig_figs != Some(5) || ![1, 2, 5].contains(mantissa))
235 {
236 return Err(BadSubscription::Mantissa(mantissa));
237 }
238 }
239 Ok(())
240 }
241}
242
243fn is_spot(coin: &str) -> bool {
245 coin.starts_with(['@', '#', '+']) || coin.contains('/')
246}
247
248#[cfg(test)]
249#[path = "tests/fixtures.rs"]
250mod fixtures;
251
252#[cfg(test)]
253#[path = "tests/subscription.rs"]
254mod tests;