ccxt_exchanges/hyperliquid/
mod.rs

1//! HyperLiquid exchange implementation.
2//!
3//! HyperLiquid is a decentralized perpetual futures exchange built on its own L1 blockchain.
4//! Unlike centralized exchanges (CEX) like Binance or Bitget, HyperLiquid uses:
5//! - Ethereum wallet private keys for authentication (EIP-712 typed data signatures)
6//! - Wallet addresses as account identifiers (no registration required)
7//! - USDC as the sole settlement currency
8//!
9//! # Features
10//!
11//! - Perpetual futures trading with up to 50x leverage
12//! - Cross-margin and isolated margin modes
13//! - Real-time WebSocket data streaming
14//! - EIP-712 compliant transaction signing
15//!
16//! # Example
17//!
18//! ```no_run
19//! use ccxt_exchanges::hyperliquid::HyperLiquid;
20//!
21//! # async fn example() -> ccxt_core::Result<()> {
22//! // Create a public-only instance (no authentication)
23//! let exchange = HyperLiquid::builder()
24//!     .testnet(true)
25//!     .build()?;
26//!
27//! // Fetch markets
28//! let markets = exchange.fetch_markets().await?;
29//! println!("Found {} markets", markets.len());
30//!
31//! // Create an authenticated instance
32//! let exchange = HyperLiquid::builder()
33//!     .private_key("0x...")
34//!     .testnet(true)
35//!     .build()?;
36//!
37//! // Fetch balance
38//! let balance = exchange.fetch_balance().await?;
39//! # Ok(())
40//! # }
41//! ```
42
43use ccxt_core::{BaseExchange, ExchangeConfig, Result};
44
45pub mod auth;
46pub mod builder;
47pub mod error;
48mod exchange_impl;
49pub mod parser;
50pub mod rest;
51pub mod ws;
52mod ws_exchange_impl;
53
54pub use auth::HyperLiquidAuth;
55pub use builder::HyperLiquidBuilder;
56pub use error::{HyperLiquidErrorCode, is_error_response, parse_error};
57
58/// HyperLiquid exchange structure.
59#[derive(Debug)]
60pub struct HyperLiquid {
61    /// Base exchange instance.
62    base: BaseExchange,
63    /// HyperLiquid-specific options.
64    options: HyperLiquidOptions,
65    /// Authentication instance (optional, for private API).
66    auth: Option<HyperLiquidAuth>,
67}
68
69/// HyperLiquid-specific options.
70#[derive(Debug, Clone)]
71pub struct HyperLiquidOptions {
72    /// Whether to use testnet.
73    pub testnet: bool,
74    /// Vault address for vault trading (optional).
75    pub vault_address: Option<String>,
76    /// Default leverage multiplier.
77    pub default_leverage: u32,
78}
79
80impl Default for HyperLiquidOptions {
81    fn default() -> Self {
82        Self {
83            testnet: false,
84            vault_address: None,
85            default_leverage: 1,
86        }
87    }
88}
89
90impl HyperLiquid {
91    /// Creates a new HyperLiquid instance using the builder pattern.
92    ///
93    /// This is the recommended way to create a HyperLiquid instance.
94    ///
95    /// # Example
96    ///
97    /// ```no_run
98    /// use ccxt_exchanges::hyperliquid::HyperLiquid;
99    ///
100    /// let exchange = HyperLiquid::builder()
101    ///     .private_key("0x...")
102    ///     .testnet(true)
103    ///     .build()
104    ///     .unwrap();
105    /// ```
106    pub fn builder() -> HyperLiquidBuilder {
107        HyperLiquidBuilder::new()
108    }
109
110    /// Creates a new HyperLiquid instance with custom options.
111    ///
112    /// This is used internally by the builder pattern.
113    ///
114    /// # Arguments
115    ///
116    /// * `config` - Exchange configuration.
117    /// * `options` - HyperLiquid-specific options.
118    /// * `auth` - Optional authentication instance.
119    pub fn new_with_options(
120        config: ExchangeConfig,
121        options: HyperLiquidOptions,
122        auth: Option<HyperLiquidAuth>,
123    ) -> Result<Self> {
124        let base = BaseExchange::new(config)?;
125        Ok(Self {
126            base,
127            options,
128            auth,
129        })
130    }
131
132    /// Returns a reference to the base exchange.
133    pub fn base(&self) -> &BaseExchange {
134        &self.base
135    }
136
137    /// Returns a mutable reference to the base exchange.
138    pub fn base_mut(&mut self) -> &mut BaseExchange {
139        &mut self.base
140    }
141
142    /// Returns the HyperLiquid options.
143    pub fn options(&self) -> &HyperLiquidOptions {
144        &self.options
145    }
146
147    /// Returns a reference to the authentication instance.
148    pub fn auth(&self) -> Option<&HyperLiquidAuth> {
149        self.auth.as_ref()
150    }
151
152    /// Returns the exchange ID.
153    pub fn id(&self) -> &str {
154        "hyperliquid"
155    }
156
157    /// Returns the exchange name.
158    pub fn name(&self) -> &str {
159        "HyperLiquid"
160    }
161
162    /// Returns the API version.
163    pub fn version(&self) -> &str {
164        "1"
165    }
166
167    /// Returns `true` if the exchange is CCXT-certified.
168    pub fn certified(&self) -> bool {
169        false
170    }
171
172    /// Returns `true` if Pro version (WebSocket) is supported.
173    pub fn pro(&self) -> bool {
174        true
175    }
176
177    /// Returns the rate limit in requests per second.
178    pub fn rate_limit(&self) -> f64 {
179        // HyperLiquid has generous rate limits
180        100.0
181    }
182
183    /// Returns the API URLs.
184    pub fn urls(&self) -> HyperLiquidUrls {
185        if self.options.testnet {
186            HyperLiquidUrls::testnet()
187        } else {
188            HyperLiquidUrls::mainnet()
189        }
190    }
191
192    /// Returns the wallet address if authenticated.
193    pub fn wallet_address(&self) -> Option<&str> {
194        self.auth.as_ref().map(|a| a.wallet_address())
195    }
196
197    // TODO: Implement in task 11 (WebSocket Implementation)
198    // /// Creates a public WebSocket client.
199    // pub fn create_ws(&self) -> ws::HyperLiquidWs {
200    //     let urls = self.urls();
201    //     ws::HyperLiquidWs::new(urls.ws)
202    // }
203}
204
205/// HyperLiquid API URLs.
206#[derive(Debug, Clone)]
207pub struct HyperLiquidUrls {
208    /// REST API base URL.
209    pub rest: String,
210    /// WebSocket URL.
211    pub ws: String,
212}
213
214impl HyperLiquidUrls {
215    /// Returns mainnet environment URLs.
216    pub fn mainnet() -> Self {
217        Self {
218            rest: "https://api.hyperliquid.xyz".to_string(),
219            ws: "wss://api.hyperliquid.xyz/ws".to_string(),
220        }
221    }
222
223    /// Returns testnet environment URLs.
224    pub fn testnet() -> Self {
225        Self {
226            rest: "https://api.hyperliquid-testnet.xyz".to_string(),
227            ws: "wss://api.hyperliquid-testnet.xyz/ws".to_string(),
228        }
229    }
230}
231
232#[cfg(test)]
233mod tests {
234    use super::*;
235
236    #[test]
237    fn test_default_options() {
238        let options = HyperLiquidOptions::default();
239        assert!(!options.testnet);
240        assert!(options.vault_address.is_none());
241        assert_eq!(options.default_leverage, 1);
242    }
243
244    #[test]
245    fn test_mainnet_urls() {
246        let urls = HyperLiquidUrls::mainnet();
247        assert_eq!(urls.rest, "https://api.hyperliquid.xyz");
248        assert_eq!(urls.ws, "wss://api.hyperliquid.xyz/ws");
249    }
250
251    #[test]
252    fn test_testnet_urls() {
253        let urls = HyperLiquidUrls::testnet();
254        assert_eq!(urls.rest, "https://api.hyperliquid-testnet.xyz");
255        assert_eq!(urls.ws, "wss://api.hyperliquid-testnet.xyz/ws");
256    }
257}