diff --git a/Readme.md b/Readme.md index 32bb939..e2b2d44 100644 --- a/Readme.md +++ b/Readme.md @@ -1,37 +1,209 @@ # hyperqit -A rust-based hyperliquid integration ot build strategies (interact with core and hypevm both) +A Rust SDK for Hyperliquid. -## Features +## Installation -- **Strategy Engine:** +```bash +cargo add hyperqit +``` - - Periodically evaluates market and funding conditions. - - Dynamically manages positions in spot and perpetual markets. - - Supports configurable leverage, slippage, and liquidation risk thresholds. +## Quick Start -- **Health and Risk Monitoring:** +### Placing Orders - - Monitors funding rates and proximity to liquidation. - - Automatically exits or maintains positions based on real-time risk assessment. +```rust +use hyperqit::*; +use alloy::primitives::Address; -- **Position Management:** +#[tokio::main] +async fn main() -> Result<(), Box> { + // Setup signer + let private_key = "your_private_key_here"; + let signer = LocalWallet::signer(private_key.to_string()); + let user_address = signer.address(); - - Programmatic entry and exit of positions. - - Unified handling of spot and perp assets. - - Automated rebalancing and state tracking. + // Create client + let client = HyperliquidClient::new( + Network::Testnet, + Box::new(signer), + user_address, + ); -- **Hyperliquid Integration:** - - Direct interaction with Hyperliquid APIs for order placement, funding history, leverage updates, and asset transfers. - - Secure signing and wallet management. + // Place an order + let order = BulkOrder { + orders: vec![OrderRequest { + asset: 0, // BTC perpetual + is_buy: true, + limit_px: "50000.0".to_string(), + sz: "0.001".to_string(), + reduce_only: false, + order_type: OrderType::Limit(Limit { tif: "Ioc".into() }), + cloid: None, + }], + grouping: "na".to_string(), + }; -## Strategy Logic + let result = client.create_position_raw(order).await?; + println!("Order placed: {:?}", result); -- **Risk-Aware Execution:** - - Enters positions according to user configuration. - - Exits positions if funding turns negative or liquidation risk exceeds threshold. - - Maintains positions when conditions are favorable, with continuous monitoring. + Ok(()) +} +``` -## Configuration +### Multi Sig Operations -- All operational parameters (keys, asset, thresholds) are set via environment variables. +```rust +use hyperqit::*; +use alloy::primitives::Address; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Setup primary signer + let primary_signer = LocalWallet::signer("primary_key".to_string()); + let primary_address = primary_signer.address(); + + // Setup other signers + let other_signers = vec![ + Box::new(LocalWallet::signer("signer2_key".to_string())), + Box::new(LocalWallet::signer("signer3_key".to_string())), + ]; + + let client = HyperliquidClient::new( + Network::Testnet, + Box::new(primary_signer), + primary_address, + ); + + // Multi-sig USD transfer + let multi_sig_address: Address = "0x1234...".parse()?; + client.multi_sig_usd_class_transfer( + 1000, // amount + true, // to_perp + "0xa4b1".to_string(), // chain ID + other_signers, // other signers + multi_sig_address, // multi-sig address + ).await?; + + println!("Multi-sig transfer completed"); + Ok(()) +} +``` + +## Asset IDs & Market Data + +Get asset IDs and market information: + +```rust +use hyperqit::market_info::*; + +// Get unified market data +let perp_info = client.get_perp_info(None).await?; +let spot_info = client.get_spot_info(None).await?; +let unified = create_unified_market_info(perp_info, spot_info); + +// Find asset by name and get ID +if let Some(market) = find_market_by_name(&unified, "BTC") { + let btc_perp_id = market.perp.as_ref().map(|p| p.asset_id); + let btc_spot_id = market.spot.as_ref().map(|s| s.asset_id); + + println!("BTC Perp ID: {:?}", btc_perp_id); + println!("BTC Spot ID: {:?}", btc_spot_id); +} + +// Get current prices +let btc_perp_price = get_current_price(&unified, "BTC", true, true); // perp, use_mid +let btc_spot_price = get_current_price(&unified, "BTC", false, true); // spot, use_mid +``` + +## Examples + +The binary tools demonstrate SDK usage: + +- `dex` - DEX operations, order management, and market data +- `multisig` - Multi-signature operations and conversions +- `transfer` - USD transfers between spot and perp accounts +- `strat` - **Delta neutral funding rate farming strategy** with web interface +- `deployer` - **HIP-3 builder-deployed perpetuals** deployment and management + +Run examples with: + +```bash +cargo run --bin dex +cargo run --bin multisig +cargo run --bin transfer +cargo run --bin strat +cargo run --bin deployer +``` + +### Delta Neutral Strategy (`strat`) + +Delta neutral funding rate farming strategy that goes long spot + short perp when funding is positive. + +**Configuration:** + +```bash +PRIVATE_KEY=your_private_key +USER_ADDRESS=0x1234... +BOT_URL=https://your-webhook-url.com +CHECK_EVERY=60 +BIND_ADDR=0.0.0.0:3000 +``` + +### HIP-3 Builder-Deployed Perpetuals (`deployer`) + +The `deployer` binary demonstrates HIP-3 perpetual deployment using the SDK: + +**SDK Support:** + +- `PerpDeployAction` enum for all deployment actions +- `RegisterAsset` - Deploy new perpetual markets +- `SetOracle` - Update oracle and mark prices +- `SetFundingMultipliers` - Configure funding rates +- `HaltTrading` - Control market trading + +**Example Usage:** + +```rust +// Deploy new perpetual market +let deploy_action = PerpDeployAction::RegisterAsset(RegisterAsset { + max_gas: Some(1000000), + asset_request: RegisterAssetRequest { + coin: "NEWCOIN".to_string(), + sz_decimals: 6, + oracle_px: "100.0".to_string(), + margin_table_id: 0, + only_isolated: false, + }, + dex: "dex".to_string(), + schema: Some(PerpDexSchemaInput { + full_name: "New Coin Perpetual".to_string(), + collateral_token: 0, + oracle_updater: None, + }), +}); + +client.perp_deploy_action(deploy_action).await?; +``` + +## Signing + +The SDK supports multiple signing methods through the `Signer` trait: + +```rust +#[async_trait] +pub trait Signer { + async fn sign_order(&self, to_sign: FixedBytes<32>) -> Result; +} +``` + +**Built-in Signers:** + +- `LocalWallet` - Local private key signing + +**Extending Signers:** +Implement the `Signer` trait for custom signing backends like hardware wallets, remote signers, or multi-party computation systems. + +## License + +MIT License diff --git a/src/bin/dn_strat/README.md b/src/bin/dn_strat/README.md new file mode 100644 index 0000000..c74563d --- /dev/null +++ b/src/bin/dn_strat/README.md @@ -0,0 +1,56 @@ +# Delta Neutral Strategy + +A delta neutral funding rate farming strategy for Hyperliquid. + +## Strategy Overview + +**Long Spot + Short Perp** when funding rate is positive to capture funding payments while maintaining delta neutrality. + +## Configuration + +Set environment variables: + +```bash +PRIVATE_KEY=your_private_key +USER_ADDRESS=0x1234... +BOT_URL=https://your-webhook-url.com +CHECK_EVERY=60 # seconds +BIND_ADDR=0.0.0.0:3000 +``` + +## Running + +```bash +cargo run --bin strat +``` + +## Strategy Logic + +1. **Entry**: Long spot + Short perp when funding rate > 0 +2. **Monitoring**: Continuous health checks every `CHECK_EVERY` seconds +3. **Exit Conditions**: + - Funding rate turns negative + - Price approaches liquidation threshold (70% of liquidation price) +4. **Notifications**: Webhook alerts for strategy events + +## API Endpoints + +The strategy exposes HTTP endpoints for manual control: + +- `POST /v1/strategy/open` - Enter position +- `POST /v1/strategy/close` - Exit position +- `GET /v1/strategy/position` - Get current position status + +## Risk Management + +- **Liquidation Risk**: Monitors price vs liquidation price +- **Funding Risk**: Exits when funding turns negative +- **Dust Threshold**: Ignores positions below 0.1 USD +- **Slippage**: 0.5% slippage tolerance + +## Strategy Parameters + +- **Leverage**: 1x (configurable) +- **Slippage**: 0.5% +- **Dust Threshold**: 0.1 USD +- **Liquidation Threshold**: 70% of liquidation price diff --git a/src/bin/dn_strat/config.rs b/src/bin/dn_strat/config.rs index e61092e..4e9cf96 100644 --- a/src/bin/dn_strat/config.rs +++ b/src/bin/dn_strat/config.rs @@ -5,9 +5,6 @@ pub struct Config { #[envconfig(from = "PRIVATE_KEY")] pub private_key: String, - #[envconfig(from = "RUST_LOG")] - pub log_level: String, - #[envconfig(from = "USER_ADDRESS")] pub user_address: String, diff --git a/src/bin/dn_strat/main.rs b/src/bin/dn_strat/main.rs index 909b0cd..fd88d02 100644 --- a/src/bin/dn_strat/main.rs +++ b/src/bin/dn_strat/main.rs @@ -31,11 +31,11 @@ async fn main() { ) .init(); let config = Config::init_from_env().unwrap(); - let signer = Signers::Local(hyperqit::LocalWallet::signer(config.private_key)); + let signer = hyperqit::LocalWallet::signer(config.private_key); let user_address: Address = config.user_address.parse().unwrap(); - let executor = crate::HyperliquidClient::new(Network::Testnet, signer, user_address); + let executor = crate::HyperliquidClient::new(Network::Testnet, Box::new(signer), user_address); let asset = Asset::CommonAsset("HYPE".to_owned()); let notifier = NotifierService::new(config.bot_url, user_address.to_string()); let strategy = Arc::new(Strategy::new( diff --git a/src/bin/multi_sig.rs b/src/bin/multi_sig.rs index 29fe066..91d17f6 100644 --- a/src/bin/multi_sig.rs +++ b/src/bin/multi_sig.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use alloy::primitives::Address; use envconfig::Envconfig; use hyperqit::*; +use log::info; use tracing_subscriber::EnvFilter; #[derive(Envconfig)] @@ -21,9 +22,6 @@ pub struct Config { #[envconfig(from = "RUST_LOG")] pub log_level: String, - - #[envconfig(from = "USER_ADDRESS")] - pub user_address: String, } #[tokio::main] @@ -35,15 +33,13 @@ async fn main() { .init(); let config = Config::init_from_env().unwrap(); - let user_a = hyperqit::LocalWallet::signer(config.private_key_a); - let user_b = hyperqit::LocalWallet::signer(config.private_key_b); + let user_a = Box::new(hyperqit::LocalWallet::signer(config.private_key_a)); + let user_b = Box::new(hyperqit::LocalWallet::signer(config.private_key_b)); let user_a_addr = user_a.address(); let user_b_addr = user_b.address(); - let signer: Signers = Signers::Local(hyperqit::LocalWallet::signer(config.private_key_owner)); - - let user_address: Address = config.user_address.parse().unwrap(); + let signer = Box::new(hyperqit::LocalWallet::signer(config.private_key_owner)); let multi_sig_user = Address::from_str(&config.multi_sig).unwrap(); @@ -53,17 +49,30 @@ async fn main() { .await .unwrap(); - let executor = crate::HyperliquidClient::new( - Network::Testnet, - hyperqit::Signers::Local(user_b), - user_b_addr, - ); + let multi_sig_config = core_executor + .get_user_multi_sig_config(multi_sig_user) + .await + .unwrap(); + info!("config {:?}", multi_sig_config); + + let executor = crate::HyperliquidClient::new(Network::Testnet, user_b, user_b_addr); + executor + .multi_sig_convert_to_multisig_user( + None, + 0, + "0x66eee".to_string(), + vec![user_a.clone()], + multi_sig_user, + ) + .await + .unwrap(); + executor .multi_sig_usd_class_transfer( 1, false, "0x66eee".to_string(), - vec![hyperqit::Signers::Local(user_a.clone())], + vec![user_a.clone()], multi_sig_user, ) .await @@ -78,7 +87,7 @@ async fn main() { "2.0".to_string(), None, "0x66eee".to_string(), - vec![hyperqit::Signers::Local(user_a.clone())], + vec![user_a.clone()], multi_sig_user, ) .await @@ -89,7 +98,7 @@ async fn main() { user_a_addr, "2.0".to_string(), "0x66eee".to_string(), - vec![hyperqit::Signers::Local(user_a.clone())], + vec![user_a.clone()], multi_sig_user, ) .await @@ -103,7 +112,7 @@ async fn main() { mark_pxs: vec![], })), "0x66eee".to_string(), - vec![hyperqit::Signers::Local(user_a.clone())], + vec![user_a.clone()], multi_sig_user, ) .await diff --git a/src/bin/perp_deployer.rs b/src/bin/perp_deployer.rs index 7d290d4..cf1a072 100644 --- a/src/bin/perp_deployer.rs +++ b/src/bin/perp_deployer.rs @@ -38,7 +38,7 @@ async fn main() { ) .init(); let config = Config::init_from_env().unwrap(); - let signer = Signers::Local(hyperqit::LocalWallet::signer(config.private_key)); + let signer = Box::new(hyperqit::LocalWallet::signer(config.private_key)); let user_address: Address = config.user_address.parse().unwrap(); diff --git a/src/bin/perp_dex.rs b/src/bin/perp_dex.rs index 3a6f7b2..a002c6e 100644 --- a/src/bin/perp_dex.rs +++ b/src/bin/perp_dex.rs @@ -24,7 +24,7 @@ async fn main() { ) .init(); let config = Config::init_from_env().unwrap(); - let signer = Signers::Local(hyperqit::LocalWallet::signer(config.private_key)); + let signer = Box::new(hyperqit::LocalWallet::signer(config.private_key)); let user_address: Address = config.user_address.parse().unwrap(); diff --git a/src/bin/transfer.rs b/src/bin/transfer.rs index 12939b7..3191c5a 100644 --- a/src/bin/transfer.rs +++ b/src/bin/transfer.rs @@ -22,11 +22,11 @@ async fn main() { .init(); let config = Config::init_from_env().unwrap(); - let signer: Signers = Signers::Local(hyperqit::LocalWallet::signer(config.private_key_sender)); + let signer = Box::new(hyperqit::LocalWallet::signer(config.private_key_sender)); let user_address: Address = config.sender_address.parse().unwrap(); - let executor = crate::HyperliquidClient::new(Network::Testnet, signer, user_address); + let executor = hyperqit::HyperliquidClient::new(Network::Testnet, signer, user_address); executor .transfer_usd(10, false, "0x1".to_owned()) diff --git a/src/hl/client.rs b/src/client.rs similarity index 93% rename from src/hl/client.rs rename to src/client.rs index 7b63833..04b5c29 100644 --- a/src/hl/client.rs +++ b/src/client.rs @@ -1,52 +1,31 @@ use alloy::sol_types::SolStruct; use anyhow::Ok; -use async_trait::async_trait; use std::time::SystemTime; use tracing::{debug, error, info}; use alloy::primitives::{Address, FixedBytes}; -use crate::errors::{Errors, Result}; -use crate::hl::exchange::{ - CONVERT_TO_MULTI_SIG_USER_MULTISIG_TYPE, CONVERT_TO_MULTI_SIG_USER_TYPE, ConvertToMultiSigUser, - ExchangeRequest, ExchangeResponse, MultiSigConvertToMultiSigUser, MultiSigSendAsset, - MultiSigUsdClassTransfer, MultiSigUsdSend, SEND_ASSET_MULTISIG_TYPE, SEND_ASSET_TYPE, - SendAsset, USD_CLASS_TRANSFER_MULTISIG_TYPE, USD_CLASS_TRANSFER_TYPE, USD_SEND_MULTISIG_TYPE, - UsdClassTransfer, generate_action_params, generate_multi_sig_hash, generate_multi_sig_l1_hash, - hyperliquid_signing_hash_with_default_domain, -}; -use crate::hl::info::{GetInfoReq, PerpetualsInfo, SpotResponse}; -use crate::hl::message::SignedMessage; -use crate::hl::nonce::NonceManager; -use crate::hl::user_info::{ - FundingHistory, GetUserFundingHistoryReq, GetUserInfoReq, UserPerpPosition, UserSpotPosition, -}; -use crate::hl::utils::*; -use crate::hl::{Actions, TransferRequest}; -use crate::{ - BulkCancel, BulkOrder, CancelOrder, ConvertToMultiSigUserRequest, ExchangeOrderResponse, - GetHistoricalOrders, GetUserFills, GetUserOpenOrders, HyperLiquidSigningHash, LocalWallet, - MultiSigConfig, MultiSigPayload, MultiSigRequest, Order, OrderRequest, PerpDeployAction, - SendAssetRequest, SignedMessageHex, Signers, UsdSendRequest, UserFillsResponse, - UserOpenOrdersResponse, UserOrderHistoryResponse, -}; - -#[async_trait] -pub trait HlAgentWallet { - async fn sign_order(&self, to_sign: FixedBytes<32>) -> Result; -} +use crate::errors::*; +use crate::internal::*; +use crate::market_info::*; +use crate::order_responses::*; +use crate::requests::*; +use crate::signing::*; +use crate::user_data::*; +use crate::utils::*; +use crate::wallet::*; pub struct HyperliquidClient { client: reqwest::Client, - signer: Signers, + signer: Box, network: Network, user: Address, nonce_manager: NonceManager, } impl HyperliquidClient { - pub fn new(network: Network, signer: Signers, user: Address) -> Self { + pub fn new(network: Network, signer: Box, user: Address) -> Self { debug!("creating hyperliquid client for {} on {:?}", user, network); HyperliquidClient { client: reqwest::Client::new(), @@ -338,6 +317,38 @@ impl HyperliquidClient { Ok(()) } + pub async fn get_user_multi_sig_config( + &self, + user: Address, + ) -> Result> { + debug!("fetching multi sig config for user {}", self.user); + + let req = GetUserMultiSigConfig { + request_type: "userToMultiSigSigners".into(), + user: user.to_string(), + }; + + let resp = self + .client + .post(format!("{}/info", Into::::into(self.network))) + .header("Content-Type", "application/json") + .json(&req) + .send() + .await?; + + let status_code = resp.status().as_u16(); + let body = resp.text().await?; + if status_code != 200 { + error!( + "failed to get user multi sig config: {} - {}", + status_code, body + ); + return Err(Errors::HyperLiquidApiError(status_code, body).into()); + } + + Ok(serde_json::from_str(body.as_str())?) + } + pub async fn create_position_with_size_in_usd( &self, a: u32, @@ -417,7 +428,7 @@ impl HyperliquidClient { limit_px: px.clone(), sz: sz.clone(), reduce_only, - order_type: Order::Limit(crate::Limit { tif: "Ioc".into() }), + order_type: OrderType::Limit(crate::Limit { tif: "Ioc".into() }), cloid: None, }], grouping: "na".to_string(), @@ -708,7 +719,7 @@ impl HyperliquidClient { let sig_chain_id_u64 = parse_chain_id(&sig_chain_id)?; let config_str = serde_json::to_string(&MultiSigConfig { authorized_users: signers.iter().map(|s| s.to_string()).collect(), - threshold, + threshold: threshold, })?; let convert_action: ConvertToMultiSigUserRequest = ConvertToMultiSigUserRequest { @@ -766,7 +777,7 @@ impl HyperliquidClient { multisig_payload: T, sig_type: String, sig_chain_id: String, - other_signers: Vec, + other_signers: Vec>, multi_sig_user: Address, ) -> Result<()> { let sig_chain_id_u64 = parse_chain_id(&sig_chain_id)?; @@ -818,7 +829,7 @@ impl HyperliquidClient { amount: u64, to_perp: bool, sig_chain_id: String, - other_signers: Vec, + other_signers: Vec>, multi_sig_user: Address, ) -> Result<()> { debug!( @@ -869,7 +880,7 @@ impl HyperliquidClient { amount: String, from_sub_account: Option, sig_chain_id: String, - other_signers: Vec, + other_signers: Vec>, multi_sig_user: Address, ) -> Result<()> { debug!( @@ -922,7 +933,7 @@ impl HyperliquidClient { destination: Address, amount: String, sig_chain_id: String, - other_signers: Vec, + other_signers: Vec>, multi_sig_user: Address, ) -> Result<()> { debug!( @@ -964,27 +975,29 @@ impl HyperliquidClient { /// Multi-sig convert to multi-sig user pub async fn multi_sig_convert_to_multisig_user( &self, - signers: Vec
, + signers: Option>, threshold: u64, sig_chain_id: String, - other_signers: Vec, + other_signers: Vec>, multi_sig_user: Address, ) -> Result<()> { debug!( - "multi-sig convert to multisig user: {} signers, threshold {}", - signers.len(), - threshold + "multi-sig convert to multisig user: {:?} signers, threshold {:?}", + signers, threshold ); - let mut sorted_signers = signers; - sorted_signers.sort(); - let nonce = self.nonce_manager.get_next_nonce(); - - let config_str = serde_json::to_string(&MultiSigConfig { - authorized_users: sorted_signers.iter().map(|s| s.to_string()).collect(), - threshold, - })?; + let config_str = match signers { + Some(signers) => { + let mut sorted_signers = signers; + sorted_signers.sort(); + serde_json::to_string(&MultiSigConfig { + authorized_users: sorted_signers.iter().map(|s| s.to_string()).collect(), + threshold, + })? + } + None => "null".to_owned(), + }; let convert_req = ConvertToMultiSigUserRequest { sig_chain_id: sig_chain_id.clone(), @@ -1049,7 +1062,7 @@ impl HyperliquidClient { &self, action: Actions, sig_chain_id: String, - other_signers: Vec, + other_signers: Vec>, multi_sig_user: Address, ) -> Result<()> { debug!("sending multi sig l1 action {:?}", action.clone()); diff --git a/src/hl/mod.rs b/src/hl/mod.rs deleted file mode 100644 index 85ad848..0000000 --- a/src/hl/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -mod actions; -mod client; -mod exchange; -mod info; -mod message; -mod nonce; -mod response; -mod user_info; -mod utils; - -pub use actions::*; -pub use client::{HlAgentWallet, HyperliquidClient}; -pub use info::*; -pub use message::*; -pub use response::*; -pub use user_info::*; -pub use utils::*; diff --git a/src/hl/exchange.rs b/src/internal/exchange.rs similarity index 95% rename from src/hl/exchange.rs rename to src/internal/exchange.rs index f25fd56..06205f3 100644 --- a/src/hl/exchange.rs +++ b/src/internal/exchange.rs @@ -1,23 +1,18 @@ use alloy::{ dyn_abi::Eip712Domain, - hex::hex, - primitives::{FixedBytes, U256, address, keccak256}, + primitives::{FixedBytes, address, keccak256}, sol as alloy_sol, sol_types::{SolStruct, eip712_domain}, }; use hl_sol::sol; - -use reqwest::redirect::Action; use serde::{Deserialize, Serialize}; -use serde_json::Value; -use crate::{ - Actions, HyperLiquidSigningHash, MultiSigRequest, Network, - errors::{Errors, Result}, - hl::SignedMessage, - parse_chain_id, -}; +use crate::errors::*; +use crate::requests::*; +use crate::signing::*; +use crate::utils::*; +use crate::wallet::*; #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct ExchangeResponse { diff --git a/src/internal/mod.rs b/src/internal/mod.rs new file mode 100644 index 0000000..a4f6813 --- /dev/null +++ b/src/internal/mod.rs @@ -0,0 +1,5 @@ +mod exchange; +mod nonce; + +pub use exchange::*; +pub use nonce::*; diff --git a/src/hl/nonce.rs b/src/internal/nonce.rs similarity index 100% rename from src/hl/nonce.rs rename to src/internal/nonce.rs diff --git a/src/lib.rs b/src/lib.rs index c2236d1..c08a1c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,23 @@ +// Module declarations +mod client; mod errors; -mod hl; -mod signer; +mod internal; +mod market_info; +mod order_responses; +mod requests; +mod signing; +mod user_data; +mod utils; +mod wallet; -pub use errors::*; -pub use hl::*; -pub use signer::*; +pub use client::HyperliquidClient; +pub use errors::{CmpError, Errors, Result}; +pub use market_info::{ + PerpMarketInfo, SpotMarketInfo, create_unified_market_info, find_market_by_name, +}; +pub use order_responses::*; +pub use requests::*; +pub use signing::{SignedMessage, SignedMessageHex, Signer}; +pub use user_data::*; +pub use utils::*; +pub use wallet::{HyperLiquidSigningHash, LocalWallet}; diff --git a/src/hl/info.rs b/src/market_info.rs similarity index 100% rename from src/hl/info.rs rename to src/market_info.rs diff --git a/src/hl/response.rs b/src/order_responses.rs similarity index 98% rename from src/hl/response.rs rename to src/order_responses.rs index 266274b..17b1224 100644 --- a/src/hl/response.rs +++ b/src/order_responses.rs @@ -39,7 +39,7 @@ pub enum ExchangeOrderResponse { #[cfg(test)] mod test { - use crate::hl::exchange::ExchangeResponse; + use crate::internal::ExchangeResponse; use super::*; diff --git a/src/hl/actions.rs b/src/requests.rs similarity index 98% rename from src/hl/actions.rs rename to src/requests.rs index ad558d8..2c1f115 100644 --- a/src/hl/actions.rs +++ b/src/requests.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::{SignedMessage, SignedMessageHex}; +use crate::SignedMessageHex; #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] @@ -16,7 +16,7 @@ pub struct OrderRequest { #[serde(rename = "r", alias = "reduceOnly", default)] pub reduce_only: bool, #[serde(rename = "t", alias = "orderType")] - pub order_type: Order, + pub order_type: OrderType, #[serde(rename = "c", alias = "cloid", skip_serializing_if = "Option::is_none")] pub cloid: Option, } @@ -28,7 +28,7 @@ pub struct Limit { #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] -pub enum Order { +pub enum OrderType { Limit(Limit), } @@ -121,7 +121,6 @@ pub struct RegisterAsset { #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] - pub enum PerpDeployAction { RegisterAsset(RegisterAsset), SetFundingMultiplier(SetFundingMultipliers), diff --git a/src/hl/message.rs b/src/signing.rs similarity index 72% rename from src/hl/message.rs rename to src/signing.rs index 9d597fc..0e005c4 100644 --- a/src/hl/message.rs +++ b/src/signing.rs @@ -1,6 +1,13 @@ -use alloy::primitives::U256; +use crate::errors::Result; +use alloy::primitives::{FixedBytes, U256}; +use async_trait::async_trait; use serde::{Deserialize, Serialize}; +#[async_trait] +pub trait Signer { + async fn sign_order(&self, to_sign: FixedBytes<32>) -> Result; +} + #[derive(Debug, Serialize, Deserialize, Clone)] pub struct SignedMessage { pub r: U256, diff --git a/src/hl/user_info.rs b/src/user_data.rs similarity index 93% rename from src/hl/user_info.rs rename to src/user_data.rs index 506c9df..c195049 100644 --- a/src/hl/user_info.rs +++ b/src/user_data.rs @@ -198,14 +198,14 @@ pub type UserOrderHistoryResponse = Vec; #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct OrderWithStatus { - pub order: Order, + pub order: UserOrder, pub status: String, pub status_timestamp: i64, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct Order { +pub struct UserOrder { pub coin: String, pub side: String, pub limit_px: String, @@ -223,3 +223,17 @@ pub struct Order { pub tif: String, pub cloid: Option, } + +#[derive(Serialize, Deserialize)] +pub struct GetUserMultiSigConfig { + #[serde(rename = "type")] + pub request_type: String, + pub user: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct UserMultiSigConfig { + pub authorized_users: Vec, + pub threshold: u64, +} diff --git a/src/hl/utils.rs b/src/utils.rs similarity index 96% rename from src/hl/utils.rs rename to src/utils.rs index eef288a..32f8137 100644 --- a/src/hl/utils.rs +++ b/src/utils.rs @@ -87,8 +87,8 @@ pub fn get_formatted_position_with_amount_raw( } pub fn parse_chain_id(chain_id: &str) -> Result { - if chain_id.starts_with("0x") { - u64::from_str_radix(&chain_id[2..], 16) + if let Some(stripped) = chain_id.strip_prefix("0x") { + u64::from_str_radix(stripped, 16) } else if chain_id.chars().all(|c| c.is_ascii_hexdigit()) { u64::from_str_radix(chain_id, 16) } else { diff --git a/src/signer/mod.rs b/src/wallet.rs similarity index 78% rename from src/signer/mod.rs rename to src/wallet.rs index fd288f5..737f203 100644 --- a/src/signer/mod.rs +++ b/src/wallet.rs @@ -1,4 +1,7 @@ -use crate::{errors::Result, hl::SignedMessage}; +use crate::{ + errors::Result, + signing::{SignedMessage, Signer as WalletSigner}, +}; use alloy::{ primitives::{Address, FixedBytes}, signers::{Signer, local::PrivateKeySigner}, @@ -10,10 +13,6 @@ pub trait HyperLiquidSigningHash { fn hyperliquid_signing_hash(&self, domain: &Eip712Domain) -> FixedBytes<32>; } -pub enum Signers { - Local(LocalWallet), -} - #[derive(Clone)] pub struct LocalWallet { wallet_key: PrivateKeySigner, @@ -35,12 +34,9 @@ impl LocalWallet { } #[async_trait::async_trait] -impl crate::HlAgentWallet for Signers { +impl WalletSigner for LocalWallet { async fn sign_order(&self, to_sign: FixedBytes<32>) -> Result { - let signature = match self { - Signers::Local(wallet) => wallet.sign_hash(to_sign), - } - .await?; + let signature = self.sign_hash(to_sign).await?; Ok(SignedMessage { r: signature.r(), s: signature.s(),