use ccxt_core::types::default_type::DefaultType;
use ccxt_core::{BaseExchange, ExchangeConfig, Result};
pub mod auth;
pub mod builder;
pub mod endpoint_router;
pub mod error;
mod exchange_impl;
pub mod parser;
pub mod rest;
pub mod signed_request;
pub mod ws;
mod ws_exchange_impl;
pub use auth::HyperLiquidAuth;
pub use builder::{HyperLiquidBuilder, validate_default_type};
pub use endpoint_router::HyperLiquidEndpointRouter;
pub use error::{HyperLiquidErrorCode, is_error_response, parse_error};
#[derive(Debug)]
pub struct HyperLiquid {
base: BaseExchange,
options: HyperLiquidOptions,
auth: Option<HyperLiquidAuth>,
pub(crate) ws_connection: std::sync::Arc<tokio::sync::RwLock<Option<ws::HyperLiquidWs>>>,
}
#[derive(Debug, Clone)]
pub struct HyperLiquidOptions {
pub testnet: bool,
pub vault_address: Option<String>,
pub default_leverage: u32,
pub default_type: DefaultType,
}
impl Default for HyperLiquidOptions {
fn default() -> Self {
Self {
testnet: false,
vault_address: None,
default_leverage: 1,
default_type: DefaultType::Swap,
}
}
}
impl HyperLiquid {
pub fn builder() -> HyperLiquidBuilder {
HyperLiquidBuilder::new()
}
pub fn new_with_options(
config: ExchangeConfig,
options: HyperLiquidOptions,
auth: Option<HyperLiquidAuth>,
) -> Result<Self> {
let base = BaseExchange::new(config)?;
Ok(Self {
base,
options,
auth,
ws_connection: std::sync::Arc::new(tokio::sync::RwLock::new(None)),
})
}
pub fn base(&self) -> &BaseExchange {
&self.base
}
pub fn base_mut(&mut self) -> &mut BaseExchange {
&mut self.base
}
pub fn options(&self) -> &HyperLiquidOptions {
&self.options
}
pub fn auth(&self) -> Option<&HyperLiquidAuth> {
self.auth.as_ref()
}
pub fn signed_action(
&self,
action: serde_json::Value,
) -> signed_request::HyperliquidSignedRequestBuilder<'_> {
signed_request::HyperliquidSignedRequestBuilder::new(self, action)
}
pub fn id(&self) -> &'static str {
"hyperliquid"
}
pub fn name(&self) -> &'static str {
"HyperLiquid"
}
pub fn version(&self) -> &'static str {
"1"
}
pub fn certified(&self) -> bool {
false
}
pub fn pro(&self) -> bool {
true
}
pub fn rate_limit(&self) -> u32 {
100
}
pub fn is_sandbox(&self) -> bool {
self.base().config.sandbox || self.options.testnet
}
pub fn urls(&self) -> HyperLiquidUrls {
if self.is_sandbox() {
HyperLiquidUrls::testnet()
} else {
HyperLiquidUrls::mainnet()
}
}
pub fn wallet_address(&self) -> Option<&str> {
self.auth
.as_ref()
.map(auth::HyperLiquidAuth::wallet_address)
}
pub fn create_ws(&self) -> ws::HyperLiquidWs {
let urls = self.urls();
ws::HyperLiquidWs::new(urls.ws)
}
}
#[derive(Debug, Clone)]
pub struct HyperLiquidUrls {
pub rest: String,
pub ws: String,
}
impl HyperLiquidUrls {
pub fn mainnet() -> Self {
Self {
rest: "https://api.hyperliquid.xyz".to_string(),
ws: "wss://api.hyperliquid.xyz/ws".to_string(),
}
}
pub fn testnet() -> Self {
Self {
rest: "https://api.hyperliquid-testnet.xyz".to_string(),
ws: "wss://api.hyperliquid-testnet.xyz/ws".to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_options() {
let options = HyperLiquidOptions::default();
assert!(!options.testnet);
assert!(options.vault_address.is_none());
assert_eq!(options.default_leverage, 1);
assert_eq!(options.default_type, DefaultType::Swap);
}
#[test]
fn test_mainnet_urls() {
let urls = HyperLiquidUrls::mainnet();
assert_eq!(urls.rest, "https://api.hyperliquid.xyz");
assert_eq!(urls.ws, "wss://api.hyperliquid.xyz/ws");
}
#[test]
fn test_testnet_urls() {
let urls = HyperLiquidUrls::testnet();
assert_eq!(urls.rest, "https://api.hyperliquid-testnet.xyz");
assert_eq!(urls.ws, "wss://api.hyperliquid-testnet.xyz/ws");
}
#[test]
fn test_is_sandbox_with_options_testnet() {
let config = ExchangeConfig::default();
let options = HyperLiquidOptions {
testnet: true,
..Default::default()
};
let exchange = HyperLiquid::new_with_options(config, options, None).unwrap();
assert!(exchange.is_sandbox());
}
#[test]
fn test_is_sandbox_with_config_sandbox() {
let config = ExchangeConfig {
sandbox: true,
..Default::default()
};
let options = HyperLiquidOptions::default();
let exchange = HyperLiquid::new_with_options(config, options, None).unwrap();
assert!(exchange.is_sandbox());
}
#[test]
fn test_is_sandbox_false_by_default() {
let config = ExchangeConfig::default();
let options = HyperLiquidOptions::default();
let exchange = HyperLiquid::new_with_options(config, options, None).unwrap();
assert!(!exchange.is_sandbox());
}
#[test]
fn test_urls_returns_mainnet_by_default() {
let config = ExchangeConfig::default();
let options = HyperLiquidOptions::default();
let exchange = HyperLiquid::new_with_options(config, options, None).unwrap();
let urls = exchange.urls();
assert_eq!(urls.rest, "https://api.hyperliquid.xyz");
assert_eq!(urls.ws, "wss://api.hyperliquid.xyz/ws");
}
#[test]
fn test_urls_returns_testnet_with_options_testnet() {
let config = ExchangeConfig::default();
let options = HyperLiquidOptions {
testnet: true,
..Default::default()
};
let exchange = HyperLiquid::new_with_options(config, options, None).unwrap();
let urls = exchange.urls();
assert_eq!(urls.rest, "https://api.hyperliquid-testnet.xyz");
assert_eq!(urls.ws, "wss://api.hyperliquid-testnet.xyz/ws");
}
#[test]
fn test_urls_returns_testnet_with_config_sandbox() {
let config = ExchangeConfig {
sandbox: true,
..Default::default()
};
let options = HyperLiquidOptions::default();
let exchange = HyperLiquid::new_with_options(config, options, None).unwrap();
let urls = exchange.urls();
assert_eq!(urls.rest, "https://api.hyperliquid-testnet.xyz");
assert_eq!(urls.ws, "wss://api.hyperliquid-testnet.xyz/ws");
}
}