Skip to main content

nautilus_hyperliquid/common/
types.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::fmt::Display;
17
18use serde::{Deserialize, Serialize};
19
20/// Represents an asset ID for Hyperliquid.
21///
22/// Asset IDs follow Hyperliquid's convention:
23/// - Perps: raw index into meta.universe
24/// - Spot: 10000 + index in spotMeta.universe
25/// - Builder perps: 100000 + dex_index * 10000 + meta_index
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
27pub struct HyperliquidAssetId(pub u32);
28
29impl HyperliquidAssetId {
30    /// Creates a perpetual asset ID from raw index.
31    pub fn perp(index: u32) -> Self {
32        Self(index)
33    }
34
35    /// Creates a spot asset ID (10000 + index).
36    pub fn spot(index: u32) -> Self {
37        Self(10_000 + index)
38    }
39
40    /// Creates a builder perpetual asset ID.
41    pub fn builder_perp(dex_index: u32, meta_index: u32) -> Self {
42        Self(100_000 + dex_index * 10_000 + meta_index)
43    }
44
45    /// Checks if this is a spot asset (>= 10000).
46    pub fn is_spot(self) -> bool {
47        self.0 >= 10_000 && self.0 < 100_000
48    }
49
50    /// Checks if this is a builder perp (>= 100000).
51    pub fn is_builder_perp(self) -> bool {
52        self.0 >= 100_000
53    }
54
55    /// Gets the base index for spot assets (asset_id - 10000).
56    /// For perps, returns the raw index.
57    pub fn base_index(self) -> u32 {
58        if self.is_spot() {
59            self.0 - 10_000
60        } else if self.is_builder_perp() {
61            // For builder perps, return the meta_index
62            (self.0 - 100_000) % 10_000
63        } else {
64            self.0
65        }
66    }
67
68    /// Gets the raw asset ID value.
69    pub fn to_raw(self) -> u32 {
70        self.0
71    }
72}
73
74impl Display for HyperliquidAssetId {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        write!(f, "{}", self.0)
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use rstest::rstest;
83
84    use super::*;
85
86    #[rstest]
87    fn test_asset_id_perp() {
88        let asset_id = HyperliquidAssetId::perp(7);
89        assert_eq!(asset_id.to_raw(), 7);
90        assert!(!asset_id.is_spot());
91        assert!(!asset_id.is_builder_perp());
92        assert_eq!(asset_id.base_index(), 7);
93    }
94
95    #[rstest]
96    fn test_asset_id_spot() {
97        let asset_id = HyperliquidAssetId::spot(7);
98        assert_eq!(asset_id.to_raw(), 10_007);
99        assert!(asset_id.is_spot());
100        assert!(!asset_id.is_builder_perp());
101        assert_eq!(asset_id.base_index(), 7);
102    }
103
104    #[rstest]
105    fn test_asset_id_builder_perp() {
106        let asset_id = HyperliquidAssetId::builder_perp(1, 7);
107        assert_eq!(asset_id.to_raw(), 110_007);
108        assert!(!asset_id.is_spot());
109        assert!(asset_id.is_builder_perp());
110        assert_eq!(asset_id.base_index(), 7);
111    }
112}