1use std::collections::HashMap;
4
5use alloy::primitives::Address;
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(tag = "type", rename_all = "camelCase")]
11pub enum Subscription {
12 AllMids,
13 Notification { user: Address },
14 WebData2 { user: Address },
15 Candle { coin: String, interval: String },
16 L2Book { coin: String },
17 Trades { coin: String },
18 OrderUpdates { user: Address },
19 UserEvents { user: Address },
20 UserFills { user: Address },
21 UserFundings { user: Address },
22 UserNonFundingLedgerUpdates { user: Address },
23 Bbo { coin: String },
25 OpenOrders { user: Address },
26 ClearinghouseState { user: Address },
27 WebData3 { user: Address },
29 TwapStates { user: Address },
30 ActiveAssetCtx { coin: String },
31 ActiveAssetData { user: Address, coin: String },
32 UserTwapSliceFills { user: Address },
33 UserTwapHistory { user: Address },
34}
35
36#[derive(Debug, Clone, Deserialize)]
38#[serde(tag = "channel", rename_all = "camelCase")]
39pub enum Message {
40 AllMids(AllMids),
41 Trades(Trades),
42 L2Book(L2Book),
43 Candle(Candle),
44 OrderUpdates(OrderUpdates),
45 UserFills(UserFills),
46 UserFundings(UserFundings),
47 UserNonFundingLedgerUpdates(UserNonFundingLedgerUpdates),
48 Notification(Notification),
49 WebData2(WebData2),
50 User(User),
51 SubscriptionResponse,
52 Pong,
53 Bbo(Bbo),
55 OpenOrders(OpenOrdersWs),
56 ClearinghouseState(ClearinghouseStateWs),
57 WebData3(WebData3Ws),
59 TwapStates(TwapStatesWs),
60 ActiveAssetCtx(ActiveAssetCtxWs),
61 ActiveAssetData(ActiveAssetDataWs),
62 UserTwapSliceFills(UserTwapSliceFillsWs),
63 UserTwapHistory(UserTwapHistoryWs),
64}
65
66#[derive(Debug, Clone, Deserialize)]
68pub struct AllMids {
69 pub data: AllMidsData,
70}
71
72#[derive(Debug, Clone, Deserialize)]
73pub struct AllMidsData {
74 pub mids: HashMap<String, String>,
75}
76
77#[derive(Debug, Clone, Deserialize)]
78pub struct Trades {
79 pub data: Vec<Trade>,
80}
81
82#[derive(Debug, Clone, Deserialize)]
83pub struct Trade {
84 pub coin: String,
85 pub side: String,
86 pub px: String,
87 pub sz: String,
88 pub time: u64,
89 pub hash: String,
90 pub tid: u64,
91}
92
93#[derive(Debug, Clone, Deserialize)]
94pub struct L2Book {
95 pub data: L2BookData,
96}
97
98#[derive(Debug, Clone, Deserialize)]
99pub struct L2BookData {
100 pub coin: String,
101 pub time: u64,
102 pub levels: Vec<Vec<BookLevel>>,
103}
104
105#[derive(Debug, Clone, Deserialize)]
106pub struct BookLevel {
107 pub px: String,
108 pub sz: String,
109 pub n: u64,
110}
111
112#[derive(Debug, Clone, Deserialize)]
113pub struct Candle {
114 pub data: CandleData,
115}
116
117#[derive(Debug, Clone, Deserialize)]
118pub struct CandleData {
119 #[serde(rename = "T")]
120 pub time_close: u64,
121 #[serde(rename = "c")]
122 pub close: String,
123 #[serde(rename = "h")]
124 pub high: String,
125 #[serde(rename = "i")]
126 pub interval: String,
127 #[serde(rename = "l")]
128 pub low: String,
129 #[serde(rename = "n")]
130 pub num_trades: u64,
131 #[serde(rename = "o")]
132 pub open: String,
133 #[serde(rename = "s")]
134 pub coin: String,
135 #[serde(rename = "t")]
136 pub time_open: u64,
137 #[serde(rename = "v")]
138 pub volume: String,
139}
140
141#[derive(Debug, Clone, Deserialize)]
143pub struct OrderUpdates {
144 pub data: Vec<OrderUpdate>,
145}
146
147#[derive(Debug, Clone, Deserialize)]
148#[serde(rename_all = "camelCase")]
149pub struct OrderUpdate {
150 pub order: BasicOrder,
151 pub status: String,
152 pub status_timestamp: u64,
153}
154
155#[derive(Debug, Clone, Deserialize)]
156#[serde(rename_all = "camelCase")]
157pub struct BasicOrder {
158 pub coin: String,
159 pub side: String,
160 pub limit_px: String,
161 pub sz: String,
162 pub oid: u64,
163 pub timestamp: u64,
164 pub orig_sz: String,
165 pub cloid: Option<String>,
166}
167
168#[derive(Debug, Clone, Deserialize)]
169pub struct UserFills {
170 pub data: UserFillsData,
171}
172
173#[derive(Debug, Clone, Deserialize)]
174#[serde(rename_all = "camelCase")]
175pub struct UserFillsData {
176 pub is_snapshot: Option<bool>,
177 pub user: Address,
178 pub fills: Vec<TradeInfo>,
179}
180
181#[derive(Debug, Clone, Deserialize)]
182#[serde(rename_all = "camelCase")]
183pub struct TradeInfo {
184 pub coin: String,
185 pub side: String,
186 pub px: String,
187 pub sz: String,
188 pub time: u64,
189 pub hash: String,
190 pub start_position: String,
191 pub dir: String,
192 pub closed_pnl: String,
193 pub oid: u64,
194 pub cloid: Option<String>,
195 pub crossed: bool,
196 pub fee: String,
197 pub fee_token: String,
198 pub tid: u64,
199}
200
201#[derive(Debug, Clone, Deserialize)]
202pub struct UserFundings {
203 pub data: UserFundingsData,
204}
205
206#[derive(Debug, Clone, Deserialize)]
207#[serde(rename_all = "camelCase")]
208pub struct UserFundingsData {
209 pub is_snapshot: Option<bool>,
210 pub user: Address,
211 pub fundings: Vec<UserFunding>,
212}
213
214#[derive(Debug, Clone, Deserialize)]
215#[serde(rename_all = "camelCase")]
216pub struct UserFunding {
217 pub time: u64,
218 pub coin: String,
219 pub usdc: String,
220 pub szi: String,
221 pub funding_rate: String,
222}
223
224#[derive(Debug, Clone, Deserialize)]
225pub struct UserNonFundingLedgerUpdates {
226 pub data: UserNonFundingLedgerUpdatesData,
227}
228
229#[derive(Debug, Clone, Deserialize)]
230#[serde(rename_all = "camelCase")]
231pub struct UserNonFundingLedgerUpdatesData {
232 pub is_snapshot: Option<bool>,
233 pub user: Address,
234 pub non_funding_ledger_updates: Vec<LedgerUpdateData>,
235}
236
237#[derive(Debug, Clone, Deserialize)]
238pub struct LedgerUpdateData {
239 pub time: u64,
240 pub hash: String,
241 pub delta: LedgerUpdate,
242}
243
244#[derive(Debug, Clone, Deserialize)]
245#[serde(rename_all = "camelCase")]
246#[serde(tag = "type")]
247pub enum LedgerUpdate {
248 Deposit {
249 usdc: String,
250 },
251 Withdraw {
252 usdc: String,
253 nonce: u64,
254 fee: String,
255 },
256 InternalTransfer {
257 usdc: String,
258 user: Address,
259 destination: Address,
260 fee: String,
261 },
262 SubAccountTransfer {
263 usdc: String,
264 user: Address,
265 destination: Address,
266 },
267 SpotTransfer {
268 token: String,
269 amount: String,
270 user: Address,
271 destination: Address,
272 fee: String,
273 },
274}
275
276#[derive(Debug, Clone, Deserialize)]
277pub struct Notification {
278 pub data: NotificationData,
279}
280
281#[derive(Debug, Clone, Deserialize)]
282pub struct NotificationData {
283 pub notification: String,
284}
285
286#[derive(Debug, Clone, Deserialize)]
287pub struct WebData2 {
288 pub data: WebData2Data,
289}
290
291#[derive(Debug, Clone, Deserialize)]
292#[serde(rename_all = "camelCase")]
293pub struct WebData2Data {
294 pub user: Address,
295}
296
297#[derive(Debug, Clone, Deserialize)]
298pub struct User {
299 pub data: UserData,
300}
301
302#[derive(Debug, Clone, Deserialize)]
303#[serde(rename_all = "camelCase")]
304#[serde(untagged)]
305pub enum UserData {
306 Fills(Vec<TradeInfo>),
307 Funding(UserFunding),
308}
309
310#[derive(Debug, Serialize)]
312pub struct WsRequest {
313 pub method: &'static str,
314 #[serde(skip_serializing_if = "Option::is_none")]
315 pub subscription: Option<Subscription>,
316}
317
318impl WsRequest {
319 pub fn subscribe(subscription: Subscription) -> Self {
320 Self {
321 method: "subscribe",
322 subscription: Some(subscription),
323 }
324 }
325
326 pub fn unsubscribe(subscription: Subscription) -> Self {
327 Self {
328 method: "unsubscribe",
329 subscription: Some(subscription),
330 }
331 }
332
333 pub fn ping() -> Self {
334 Self {
335 method: "ping",
336 subscription: None,
337 }
338 }
339}
340
341#[derive(Debug, Clone, Deserialize)]
345pub struct Bbo {
346 pub data: BboData,
347}
348
349#[derive(Debug, Clone, Deserialize)]
350#[serde(rename_all = "camelCase")]
351pub struct BboData {
352 pub coin: String,
353 pub time: u64,
354 pub bbo: BboLevel,
355}
356
357#[derive(Debug, Clone, Deserialize)]
358pub struct BboLevel {
359 pub bid: PriceLevel,
360 pub ask: PriceLevel,
361}
362
363#[derive(Debug, Clone, Deserialize)]
364pub struct PriceLevel {
365 pub px: String,
366 pub sz: String,
367}
368
369#[derive(Debug, Clone, Deserialize)]
371pub struct OpenOrdersWs {
372 pub data: OpenOrdersWsData,
373}
374
375#[derive(Debug, Clone, Deserialize)]
376#[serde(rename_all = "camelCase")]
377pub struct OpenOrdersWsData {
378 pub user: Address,
379 pub is_snapshot: Option<bool>,
380 pub orders: Vec<BasicOrder>,
381}
382
383#[derive(Debug, Clone, Deserialize)]
385pub struct ClearinghouseStateWs {
386 pub data: ClearinghouseStateWsData,
387}
388
389#[derive(Debug, Clone, Deserialize)]
390#[serde(rename_all = "camelCase")]
391pub struct ClearinghouseStateWsData {
392 pub user: Address,
393 pub margin_summary: MarginSummaryWs,
394 pub cross_margin_summary: MarginSummaryWs,
395 pub withdrawable: String,
396 pub asset_positions: Vec<AssetPositionWs>,
397}
398
399#[derive(Debug, Clone, Deserialize)]
400#[serde(rename_all = "camelCase")]
401pub struct MarginSummaryWs {
402 pub account_value: String,
403 pub total_margin_used: String,
404 pub total_ntl_pos: String,
405 pub total_raw_usd: String,
406}
407
408#[derive(Debug, Clone, Deserialize)]
409#[serde(rename_all = "camelCase")]
410pub struct AssetPositionWs {
411 pub position: PositionWs,
412 #[serde(rename = "type")]
413 pub type_string: String,
414}
415
416#[derive(Debug, Clone, Deserialize)]
417#[serde(rename_all = "camelCase")]
418pub struct PositionWs {
419 pub coin: String,
420 pub entry_px: Option<String>,
421 pub liquidation_px: Option<String>,
422 pub margin_used: String,
423 pub position_value: String,
424 pub return_on_equity: String,
425 pub szi: String,
426 pub unrealized_pnl: String,
427}
428
429#[derive(Debug, Clone, Deserialize)]
433pub struct WebData3Ws {
434 pub data: WebData3Data,
435}
436
437#[derive(Debug, Clone, Deserialize)]
438#[serde(rename_all = "camelCase")]
439pub struct WebData3Data {
440 pub user: Address,
441 #[serde(default)]
442 pub is_snapshot: Option<bool>,
443 #[serde(default)]
444 pub clearinghouse_state: Option<ClearinghouseStateWsData>,
445 #[serde(default)]
446 pub open_orders: Option<Vec<BasicOrder>>,
447 #[serde(default)]
448 pub fills: Option<Vec<TradeInfo>>,
449 #[serde(default)]
450 pub fundings: Option<Vec<UserFunding>>,
451}
452
453#[derive(Debug, Clone, Deserialize)]
455pub struct TwapStatesWs {
456 pub data: TwapStatesData,
457}
458
459#[derive(Debug, Clone, Deserialize)]
460#[serde(rename_all = "camelCase")]
461pub struct TwapStatesData {
462 pub user: Address,
463 #[serde(default)]
464 pub is_snapshot: Option<bool>,
465 pub twap_states: Vec<TwapState>,
466}
467
468#[derive(Debug, Clone, Deserialize)]
469#[serde(rename_all = "camelCase")]
470pub struct TwapState {
471 pub twap_id: u64,
472 pub coin: String,
473 pub side: String,
474 pub sz: String,
475 pub sz_filled: String,
476 pub duration_minutes: u32,
477 pub start_time: u64,
478 pub end_time: u64,
479 pub status: String,
480 #[serde(default)]
481 pub randomize: Option<bool>,
482}
483
484#[derive(Debug, Clone, Deserialize)]
486pub struct ActiveAssetCtxWs {
487 pub data: ActiveAssetCtxData,
488}
489
490#[derive(Debug, Clone, Deserialize)]
491#[serde(rename_all = "camelCase")]
492pub struct ActiveAssetCtxData {
493 pub coin: String,
494 pub ctx: AssetCtx,
495}
496
497#[derive(Debug, Clone, Deserialize)]
498#[serde(rename_all = "camelCase")]
499pub struct AssetCtx {
500 pub funding: String,
501 pub open_interest: String,
502 pub prev_day_px: String,
503 pub day_ntl_vlm: String,
504 pub premium: Option<String>,
505 pub oracle_px: String,
506 pub mark_px: String,
507 pub mid_px: Option<String>,
508 pub impact_pxs: Option<Vec<String>>,
509}
510
511#[derive(Debug, Clone, Deserialize)]
513pub struct ActiveAssetDataWs {
514 pub data: ActiveAssetDataData,
515}
516
517#[derive(Debug, Clone, Deserialize)]
518#[serde(rename_all = "camelCase")]
519pub struct ActiveAssetDataData {
520 pub user: Address,
521 pub coin: String,
522 pub leverage: LeverageWs,
523 #[serde(default)]
524 pub max_trade_szs: Option<Vec<String>>,
525}
526
527#[derive(Debug, Clone, Deserialize)]
528#[serde(rename_all = "camelCase")]
529pub struct LeverageWs {
530 #[serde(rename = "type")]
531 pub leverage_type: String,
532 pub value: u32,
533 #[serde(default)]
534 pub raw_usd: Option<String>,
535}
536
537#[derive(Debug, Clone, Deserialize)]
539pub struct UserTwapSliceFillsWs {
540 pub data: UserTwapSliceFillsData,
541}
542
543#[derive(Debug, Clone, Deserialize)]
544#[serde(rename_all = "camelCase")]
545pub struct UserTwapSliceFillsData {
546 pub user: Address,
547 #[serde(default)]
548 pub is_snapshot: Option<bool>,
549 pub twap_slice_fills: Vec<TwapSliceFill>,
550}
551
552#[derive(Debug, Clone, Deserialize)]
553#[serde(rename_all = "camelCase")]
554pub struct TwapSliceFill {
555 pub twap_id: u64,
556 pub coin: String,
557 pub side: String,
558 pub px: String,
559 pub sz: String,
560 pub time: u64,
561 pub fee: String,
562 pub oid: u64,
563 pub hash: String,
564}
565
566#[derive(Debug, Clone, Deserialize)]
568pub struct UserTwapHistoryWs {
569 pub data: UserTwapHistoryData,
570}
571
572#[derive(Debug, Clone, Deserialize)]
573#[serde(rename_all = "camelCase")]
574pub struct UserTwapHistoryData {
575 pub user: Address,
576 #[serde(default)]
577 pub is_snapshot: Option<bool>,
578 pub twap_history: Vec<TwapHistoryEntry>,
579}
580
581#[derive(Debug, Clone, Deserialize)]
582#[serde(rename_all = "camelCase")]
583pub struct TwapHistoryEntry {
584 pub twap_id: u64,
585 pub coin: String,
586 pub side: String,
587 pub sz: String,
588 pub sz_filled: String,
589 pub avg_px: Option<String>,
590 pub duration_minutes: u32,
591 pub start_time: u64,
592 pub end_time: u64,
593 pub status: String,
594 #[serde(default)]
595 pub randomize: Option<bool>,
596}