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}