Hyperliquid Rust SDK

repository·master·Indexed 19 days ago

https://github.com/hyperliquid-dex/hyperliquid-rust-sdk

A Rust client for interacting with the Hyperliquid decentralized exchange (version 0.6.0). The SDK provides the ExchangeClient for executing trades, managing orders, updating leverage, claiming rewards, and performing bridge withdrawals, as well as the InfoClient for querying market data, user states, and historical information. It also includes a MarketMaker abstraction for implementing automated liquidity strategies.

Tokens
34.1K
Snippets
101
Records
109
Agent score
66%

What's inside hyperliquid-rust-sdk

  1. Understand Meta and Asset Context structures

    master

    The SDK uses several metadata structures to represent the market universe and asset-specific context. These are typically deserialized from API responses to provide information about available assets, their leverage, decimals, and market statistics like funding or open interest.

    Core Metadata Types

    • Meta: Contains a universe of AssetMeta objects.
    • SpotMeta: Contains a universe of SpotAssetMeta and a list of TokenInfo.
    • AssetMeta: Defines properties for a single asset, including name, sz_decimals, max_leverage, and an optional only_isolated flag.
    • SpotAssetMeta: Defines a spot trading pair, including the tokens involved (as indices), the pair name, its index, and whether it is is_canonical.
    • TokenInfo: Provides details for individual tokens, such as name, sz_decimals, wei_decimals, index, token_id, and is_canonical status.

    Contextual Data Types

    These structures provide real-time or historical market context for assets:

    • AssetContext: Used for perpetual/standard assets. Includes day_ntl_vlm (net volume), funding, impact_pxs, mark_px, mid_px, open_interest, oracle_px, prev_day_px, and optional premium.
    • SpotAssetContext: Used for spot assets. Includes day_ntl_vlm, mark_px, mid_px, prev_day_px, circulating_supply, and coin.

    Note: All context fields use camelCase during deserialization.

  2. Subscribe to Active Asset Data via WebSocket

    master

    You can subscribe to real-time active asset data using the InfoClient::subscribe method. This requires a Subscription::ActiveAssetData variant, which takes a user address and a coin symbol. The method returns a subscription_id used for unsubscribing and requires an mpsc::UnboundedSender to stream incoming Message::ActiveAssetData updates.

    To stop receiving updates, call info_client.unsubscribe(subscription_id) using the ID returned during subscription.

    use alloy::primitives::address;
    use hyperliquid_rust_sdk::{BaseUrl, InfoClient, Message, Subscription};
    use tokio::sync::mpsc::unbounded_channel;
    
    #[tokio::main]
    async fn main() {
        let mut info_client = InfoClient::new(None, Some(BaseUrl::Testnet)).await.unwrap();
        let user = address!("0xc64cc00b46101bd40aa1c3121195e85c0b0918d8");
        let coin = "BTC".to_string();
    
        let (sender, mut receiver) = unbounded_channel();
        
        // Start subscription
        let subscription_id = info_client
            .subscribe(Subscription::ActiveAssetData { user, coin }, sender)
            .await
            .unwrap();
    
        // Handle incoming messages
        while let Some(Message::ActiveAssetData(active_asset_data)) = receiver.recv().await {
            println!("Received active asset data: {:?}", active_asset_data);
        }
    }
  3. Perform a class transfer using ExchangeClient

    master

    You can transfer assets between different classes (e.g., from Spot to Perps) using the class_transfer method on an ExchangeClient instance.

    Parameters:

    • amount: The amount of the asset to transfer (e.g., 1.0).
    • to_perp: A boolean flag. Set to true to transfer to the Perps class, or false to transfer to the Spot class.
    • metadata: An optional parameter (passed as None in the example) for additional transfer context if required by the API.

    This method is asynchronous and returns a result that indicates the success or failure of the transfer.

    use alloy::signers::local::PrivateKeySigner;
    use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient};
    use log::info;
    
    #[tokio::main]
    async fn main() {
        env_logger::init();
        
        // Initialize the signer with a private key
        let wallet: PrivateKeySigner = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e"
            .parse()
            .unwrap();
    
        // Initialize the ExchangeClient
        let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None)
            .await
            .unwrap();
    
        let usdc = 1.0; // Amount to transfer
        let to_perp = false; // false = Spot, true = Perps
    
        let res = exchange_client
            .class_transfer(usdc, to_perp, None)
            .await
            .unwrap();
    
        info!("Class transfer result: {res:?}");
    }
  4. Cancel an existing order with cancel

    master

    To cancel an active order, use the cancel method on an ExchangeClient. You must provide a ClientCancelRequest which specifies the asset and the order ID (oid) obtained from the original order response.

    Important Behavior:

    • If the order has already been filled, the cancellation request will return an error because a filled order cannot be cancelled.
    • The oid (Order ID) can be extracted from the ExchangeDataStatus returned after placing an order (e.g., from Filled or Resting statuses).
    let cancel = ClientCancelRequest {
        asset: "ETH".to_string(),
        oid,
    };
    
    // This response will return an error if order was filled,
    // otherwise it will cancel the order
    let response = exchange_client.cancel(cancel, None).await.unwrap();
  5. Place and cancel spot orders using ExchangeClient

    master

    To interact with the Hyperliquid exchange for spot trading, use the ExchangeClient. You can place limit orders by constructing a ClientOrderRequest and calling .order(). After placing an order, you can cancel it using a ClientCancelRequest which requires the order ID (oid) obtained from the initial order response.

    Note that canceling an order that has already been filled will result in an error.

    use alloy::signers::local::PrivateKeySigner;
    use hyperliquid_rust_sdk::{BaseUrl, ClientCancelRequest, ClientLimit, ClientOrder, ClientOrderRequest, ExchangeClient, ExchangeDataStatus, ExchangeResponseStatus};
    
    #[tokio::main]
    async fn main() {
        let wallet: PrivateKeySigner = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e".parse().unwrap();
    
        // Initialize the client (using Testnet in this example)
        let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None).await.unwrap();
    
        // 1. Define a Limit Order
        let order = ClientOrderRequest {
            asset: "XYZTWO/USDC".to_string(),
            is_buy: true,
            reduce_only: false,
            limit_px: 0.00002378,
            sz: 1000000.0,
            cloid: None,
            order_type: ClientOrder::Limit(ClientLimit {
                tif: "Gtc".to_string(),
            }),
        };
    
        // 2. Place the order
        let response = exchange_client.order(order, None).await.unwrap();
        
        // Handle response to extract the Order ID (oid)
        let response_data = match response {
            ExchangeResponseStatus::Ok(exchange_response) => exchange_response,
            ExchangeResponseStatus::Err(e) => panic!("error with exchange response: {e}"),
        };
    
        let oid = match response_data.data.unwrap().statuses[0].clone() {
            ExchangeDataStatus::Filled(order) => order.oid,
            ExchangeDataStatus::Resting(order) => order.oid,
            _ => panic!("Unexpected status"),
        };
    
        // 3. Cancel the order
        let cancel = ClientCancelRequest {
            asset: "XYZTWO/USDC".to_string(),
            oid,
        };
    
        let cancel_response = exchange_client.cancel(cancel, None).await.unwrap();
        println!("Order potentially cancelled: {cancel_response:?}");
    }
  6. Subscribe to candle data via WebSocket

    master

    You can subscribe to real-time candle (OHLCV) data using the InfoClient::subscribe method. This requires providing a Subscription::Candle variant which specifies the coin (e.g., "ETH") and the interval (e.g., "1m").

    To receive updates, you must pass an unbounded_channel sender to the subscribe method. The method returns a subscription_id which can be used later with unsubscribe(subscription_id) to stop receiving data for that specific subscription.

    use hyperliquid_rust_sdk::{BaseUrl, InfoClient, Message, Subscription};
    use tokio::sync::mpsc::unbounded_channel;
    
    #[tokio::main]
    async fn main() {
        let mut info_client = InfoClient::new(None, Some(BaseUrl::Mainnet)).await.unwrap();
        let (sender, mut receiver) = unbounded_channel();
    
        // Subscribe to 1m ETH candles
        let subscription_id = info_client
            .subscribe(
                Subscription::Candle {
                    coin: "ETH".to_string(),
                    interval: "1m".to_string(),
                },
                sender,
            )
            .await
            .unwrap();
    
        // Handle incoming messages
        while let Some(Message::Candle(candle)) = receiver.recv().await {
            println!("Received candle data: {:?}", candle);
        }
    }
  7. Close a market position

    master

    To close an existing position using a market order, use the market_close method on an ExchangeClient with MarketCloseParams.

    MarketCloseParams

    • asset: The asset symbol.
    • sz: The size to close. If set to None, the entire position for that asset will be closed.
    • px: Price (set to None for market orders).
    • slippage: Optional slippage tolerance.
    • cloid: Client Order ID (optional).
    • wallet: Wallet address (optional).

    Response Handling

    Similar to opening orders, check ExchangeResponseStatus::Ok and then inspect the ExchangeDataStatus within the response to see if the close order was Filled or is Resting.

    let market_close_params = MarketCloseParams {
        asset: "ETH".to_string(),
        sz: None, // Passing None closes the entire position
        px: None,
        slippage: Some(0.01),
        cloid: None,
        wallet: None,
    };
    
    let response = exchange_client
        .market_close(market_close_params)
        .await
        .unwrap();
  8. Subscribe to Active Asset Context via WebSocket

    master

    You can subscribe to real-time updates for a specific asset's context (such as funding rates, open interest, etc.) using the InfoClient::subscribe method.

    1. Initialize an InfoClient with a BaseUrl (e.g., BaseUrl::Testnet or BaseUrl::Mainnet).
    2. Create an asynchronous unbounded channel (tokio::sync::mpsc::unbounded_channel) to receive messages.
    3. Call .subscribe() passing a Subscription::ActiveAssetCtx { coin } variant and the channel's sender. This returns a subscription_id.
    4. Listen for incoming messages on the receiver side. The messages will arrive as the Message::ActiveAssetCtx variant.
    5. To stop receiving updates, call .unsubscribe(subscription_id) using the ID returned during subscription.
    use hyperliquid_rust_sdk::{BaseUrl, InfoClient, Message, Subscription};
    use tokio::sync::mpsc::unbounded_channel;
    
    #[tokio::main]
    async fn main() {
        let mut info_client = InfoClient::new(None, Some(BaseUrl::Testnet)).await.unwrap();
        let coin = "BTC".to_string();
    
        let (sender, mut receiver) = unbounded_channel();
        
        // Subscribe to the active asset context for a specific coin
        let subscription_id = info_client
            .subscribe(Subscription::ActiveAssetCtx { coin }, sender)
            .await
            .unwrap();
    
        // Example: Unsubscribe after 30 seconds
        tokio::spawn(async move {
            tokio::time::sleep(tokio::time::Duration::from_secs(30)).await;
            info_client.unsubscribe(subscription_id).await.unwrap()
        });
    
        // Process incoming messages
        while let Some(Message::ActiveAssetCtx(active_asset_ctx)) = receiver.recv().await {
            println!("Received active asset ctx: {:?}", active_asset_ctx);
        }
    }
  9. Subscribe to WebSocket order updates

    master

    To receive real-time order updates for a specific user, use the InfoClient::subscribe method with the Subscription::OrderUpdates variant. You must provide the user's wallet address and an unbounded_channel sender to receive the messages.

    To stop receiving updates, call info_client.unsubscribe(subscription_id) using the ID returned by the initial subscription call. The receiver loop will terminate once the subscription is closed.

    use alloy::primitives::address;
    use hyperliquid_rust_sdk::{BaseUrl, InfoClient, Message, Subscription};
    use tokio::sync::mpsc::unbounded_channel;
    
    #[tokio::main]
    async fn main() {
        let mut info_client = InfoClient::new(None, Some(BaseUrl::Testnet)).await.unwrap();
        let user = address!("0xc64cc00b46101bd40aa1c3121195e85c0b0918d8");
    
        let (sender, mut receiver) = unbounded_channel();
        
        // Subscribe to order updates
        let subscription_id = info_client
            .subscribe(Subscription::OrderUpdates { user }, sender)
            .await
            .unwrap();
    
        // Handle incoming messages
        while let Some(Message::OrderUpdates(order_updates)) = receiver.recv().await {
            println!("Received order update data: {order_updates:?}");
        }
    }