A high-performance Rust SDK for the Hyperliquid Protocol, built with a "thin wrapper, maximum control" philosophy.
- 🚀 High Performance: Uses
simd-jsonfor 10x faster JSON parsing thanserde_json - 🔒 Type-Safe: Strongly-typed Rust bindings with compile-time guarantees
- 🎯 Direct Control: No hidden retry logic or complex abstractions - you control the flow
- ⚡ Fast WebSockets: Built on
fastwebsocketsfor 3-4x performance overtungstenite - 🛠️ Builder Support: Native support for MEV builders with configurable fees
- 📊 Complete API Coverage: Info, Exchange, and WebSocket providers for all endpoints
Add this to your Cargo.toml:
[dependencies]
ferrofluid = "0.1.0"use ferrofluid::{InfoProvider,Network};#[tokio::main]asyncfnmain() -> Result<(),Box<dyn std::error::Error>>{let info = InfoProvider::new(Network::Mainnet);// Get all mid priceslet mids = info.all_mids().await?;println!("BTC mid price: {}", mids["BTC"]);// Get L2 order booklet book = info.l2_book("ETH").await?;println!("ETH best bid: {:?}", book.levels[0][0]);Ok(())}use ferrofluid::{ExchangeProvider, signers::AlloySigner};use alloy::signers::local::PrivateKeySigner;#[tokio::main]asyncfnmain() -> Result<(),Box<dyn std::error::Error>>{// Setup signerlet signer = PrivateKeySigner::random();let hyperliquid_signer = AlloySigner{inner: signer };// Create exchange providerlet exchange = ExchangeProvider::mainnet(hyperliquid_signer);// Place an order using the builder patternlet result = exchange.order(0)// BTC perpetual.limit_buy("50000","0.001").reduce_only(false).send().await?;println!("Order placed: {:?}", result);Ok(())}use ferrofluid::{WsProvider,Network, types::ws::Message};#[tokio::main]asyncfnmain() -> Result<(),Box<dyn std::error::Error>>{letmut ws = WsProvider::connect(Network::Mainnet).await?;// Subscribe to BTC order booklet(_id,mut rx) = ws.subscribe_l2_book("BTC").await?;
ws.start_reading().await?;// Handle updateswhileletSome(msg) = rx.recv().await{match msg {Message::L2Book(book) => {println!("BTC book update: {:?}", book.data.coin);}
_ => {}}}Ok(())}For production use, consider the ManagedWsProvider which adds automatic reconnection and keep-alive:
use ferrofluid::{ManagedWsProvider,WsConfig,Network};use std::time::Duration;#[tokio::main]asyncfnmain() -> Result<(),Box<dyn std::error::Error>>{// Configure with custom settingslet config = WsConfig{ping_interval:Duration::from_secs(30),auto_reconnect:true,exponential_backoff:true,
..Default::default()};let ws = ManagedWsProvider::connect(Network::Mainnet, config).await?;// Subscriptions automatically restore on reconnectlet(_id,mut rx) = ws.subscribe_l2_book("BTC").await?;
ws.start_reading().await?;// Your subscriptions survive disconnections!whileletSome(msg) = rx.recv().await{// Handle messages...}Ok(())}The examples/ directory contains comprehensive examples:
00_symbols.rs- Working with pre-defined symbols01_info_types.rs- Using the Info provider for market data02_info_provider.rs- Advanced Info provider usage03_exchange_provider.rs- Placing and managing orders04_websocket.rs- Real-time WebSocket subscriptions05_builder_orders.rs- Using MEV builders for orders06_basis_trade.rs- Example basis trading strategy07_managed_websocket.rs- WebSocket with auto-reconnect and keep-alive
Run examples with:
cargo run --example 01_info_typesFerrofluid follows a modular architecture:
ferrofluid/
├── providers/
│ ├── info.rs // Read-only market data (HTTP)
│ ├── exchange.rs // Trading operations (HTTP, requires signer)
│ └── websocket.rs // Real-time subscriptions
├── types/
│ ├── actions.rs // EIP-712 signable actions
│ ├── requests.rs // Order, Cancel, Modify structs
│ ├── responses.rs // API response types
│ └── ws.rs // WebSocket message types
└── signers/
└── signer.rs // HyperliquidSigner trait
Ferrofluid is designed for maximum performance:
- JSON Parsing: Uses
simd-jsonfor vectorized parsing - HTTP Client: Built on
hyper+towerfor connection pooling - WebSocket: Uses
fastwebsocketsfor minimal overhead - Zero-Copy: Minimizes allocations where possible
Native support for MEV builders with configurable fees:
let exchange = ExchangeProvider::mainnet_builder(signer, builder_address);// All orders automatically include builder infolet order = exchange.order(0).limit_buy("50000","0.001").send().await?;// Or specify custom builder feelet result = exchange.place_order_with_builder_fee(&order_request,10).await?;Built-in rate limiter respects Hyperliquid's limits:
// Rate limiting is automaticlet result = info.l2_book("BTC").await?;// Uses 1 weightlet fills = info.user_fills(address).await?;// Uses 2 weightComprehensive error types with thiserror:
match exchange.place_order(&order).await{Ok(status) => println!("Success: {:?}", status),Err(HyperliquidError::RateLimited{ available, required }) => {println!("Rate limited: need {} but only {} available", required, available);}Err(e) => println!("Error: {}", e),}Run the test suite:
cargo testIntegration tests against testnet:
cargo test --features testnetContributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
Built with high-performance crates from the Rust ecosystem:
- alloy-rs for Ethereum primitives
- hyperliquid-rust-sdk
- hyper for HTTP
- fastwebsockets for WebSocket
- simd-json for JSON parsing
