diff --git a/src/exchange/actions.rs b/src/exchange/actions.rs index 71e9f4f1..e0d22194 100644 --- a/src/exchange/actions.rs +++ b/src/exchange/actions.rs @@ -11,6 +11,24 @@ use crate::{ exchange::{cancel::CancelRequest, modify::ModifyRequest, order::OrderRequest}, }; +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub enum OrderGrouping { + Na, + NormalTpsl, + PositionTpsl, +} + +impl OrderGrouping { + pub const fn as_str(self) -> &'static str { + match self { + Self::Na => "na", + Self::NormalTpsl => "normalTpsl", + Self::PositionTpsl => "positionTpsl", + } + } +} + fn eip_712_domain(chain_id: u64) -> Eip712Domain { eip712_domain! { name: "HyperliquidSignTransaction", @@ -291,3 +309,15 @@ impl Eip712 for ApproveBuilderFee { keccak256(items.abi_encode()) } } + +#[cfg(test)] +mod tests { + use super::OrderGrouping; + + #[test] + fn order_grouping_uses_hyperliquid_wire_values() { + assert_eq!(OrderGrouping::Na.as_str(), "na"); + assert_eq!(OrderGrouping::NormalTpsl.as_str(), "normalTpsl"); + assert_eq!(OrderGrouping::PositionTpsl.as_str(), "positionTpsl"); + } +} diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 70b686b8..27e50571 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -26,8 +26,8 @@ use crate::{ prelude::*, req::HttpClient, signature::{sign_l1_action, sign_typed_data}, - BaseUrl, BulkCancelCloid, ClassTransfer, Error, ExchangeResponseStatus, SpotSend, SpotUser, - VaultTransfer, Withdraw3, + BaseUrl, BulkCancelCloid, ClassTransfer, Error, ExchangeResponseStatus, OrderGrouping, + OrderRequest, SpotSend, SpotUser, VaultTransfer, Withdraw3, }; #[derive(Debug)] @@ -485,18 +485,38 @@ impl ExchangeClient { orders: Vec, wallet: Option<&PrivateKeySigner>, ) -> Result { - let wallet = wallet.unwrap_or(&self.wallet); - let timestamp = next_nonce(); + self.bulk_order_with_grouping(orders, wallet, OrderGrouping::Na) + .await + } + pub async fn bulk_order_with_grouping( + &self, + orders: Vec, + wallet: Option<&PrivateKeySigner>, + grouping: OrderGrouping, + ) -> Result { let mut transformed_orders = Vec::new(); for order in orders { transformed_orders.push(order.convert(&self.coin_to_asset)?); } + self.bulk_order_raw_with_grouping(transformed_orders, wallet, grouping) + .await + } + + pub async fn bulk_order_raw_with_grouping( + &self, + orders: Vec, + wallet: Option<&PrivateKeySigner>, + grouping: OrderGrouping, + ) -> Result { + let wallet = wallet.unwrap_or(&self.wallet); + let timestamp = next_nonce(); + let action = Actions::Order(BulkOrder { - orders: transformed_orders, - grouping: "na".to_string(), + orders, + grouping: grouping.as_str().to_string(), builder: None, }); let connection_id = action.hash(timestamp, self.vault_address)?; @@ -508,10 +528,21 @@ impl ExchangeClient { } pub async fn bulk_order_with_builder( + &self, + orders: Vec, + wallet: Option<&PrivateKeySigner>, + builder: BuilderInfo, + ) -> Result { + self.bulk_order_with_builder_and_grouping(orders, wallet, builder, OrderGrouping::Na) + .await + } + + pub async fn bulk_order_with_builder_and_grouping( &self, orders: Vec, wallet: Option<&PrivateKeySigner>, mut builder: BuilderInfo, + grouping: OrderGrouping, ) -> Result { let wallet = wallet.unwrap_or(&self.wallet); let timestamp = next_nonce(); @@ -526,7 +557,7 @@ impl ExchangeClient { let action = Actions::Order(BulkOrder { orders: transformed_orders, - grouping: "na".to_string(), + grouping: grouping.as_str().to_string(), builder: Some(builder), }); let connection_id = action.hash(timestamp, self.vault_address)?; @@ -550,9 +581,6 @@ impl ExchangeClient { cancels: Vec, wallet: Option<&PrivateKeySigner>, ) -> Result { - let wallet = wallet.unwrap_or(&self.wallet); - let timestamp = next_nonce(); - let mut transformed_cancels = Vec::new(); for cancel in cancels.into_iter() { let &asset = self @@ -565,9 +593,18 @@ impl ExchangeClient { }); } - let action = Actions::Cancel(BulkCancel { - cancels: transformed_cancels, - }); + self.bulk_cancel_raw(transformed_cancels, wallet).await + } + + pub async fn bulk_cancel_raw( + &self, + cancels: Vec, + wallet: Option<&PrivateKeySigner>, + ) -> Result { + let wallet = wallet.unwrap_or(&self.wallet); + let timestamp = next_nonce(); + + let action = Actions::Cancel(BulkCancel { cancels }); let connection_id = action.hash(timestamp, self.vault_address)?; let action = serde_json::to_value(&action).map_err(|e| Error::JsonParse(e.to_string()))?; @@ -660,11 +697,21 @@ impl ExchangeClient { is_cross: bool, wallet: Option<&PrivateKeySigner>, ) -> Result { - let wallet = wallet.unwrap_or(&self.wallet); + let &asset_index = self.coin_to_asset.get(coin).ok_or(Error::AssetNotFound)?; + self.update_leverage_by_asset_index(leverage, asset_index, is_cross, wallet) + .await + } + pub async fn update_leverage_by_asset_index( + &self, + leverage: u32, + asset_index: u32, + is_cross: bool, + wallet: Option<&PrivateKeySigner>, + ) -> Result { + let wallet = wallet.unwrap_or(&self.wallet); let timestamp = next_nonce(); - let &asset_index = self.coin_to_asset.get(coin).ok_or(Error::AssetNotFound)?; let action = Actions::UpdateLeverage(UpdateLeverage { asset: asset_index, is_cross, diff --git a/src/exchange/exchange_responses.rs b/src/exchange/exchange_responses.rs index 63d3de5c..b264b5eb 100644 --- a/src/exchange/exchange_responses.rs +++ b/src/exchange/exchange_responses.rs @@ -1,11 +1,11 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub struct RestingOrder { pub oid: u64, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct FilledOrder { pub total_sz: String, @@ -13,7 +13,7 @@ pub struct FilledOrder { pub oid: u64, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub enum ExchangeDataStatus { Success, @@ -24,19 +24,19 @@ pub enum ExchangeDataStatus { Filled(FilledOrder), } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub struct ExchangeDataStatuses { pub statuses: Vec, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub struct ExchangeResponse { #[serde(rename = "type")] pub response_type: String, pub data: Option, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] #[serde(tag = "status", content = "response")] pub enum ExchangeResponseStatus { diff --git a/src/exchange/mod.rs b/src/exchange/mod.rs index 7b9e9042..d19d87ee 100644 --- a/src/exchange/mod.rs +++ b/src/exchange/mod.rs @@ -8,11 +8,11 @@ mod order; pub use actions::*; pub use builder::*; -pub use cancel::{ClientCancelRequest, ClientCancelRequestCloid}; +pub use cancel::{CancelRequest, ClientCancelRequest, ClientCancelRequestCloid}; pub use exchange_client::*; pub use exchange_responses::*; pub use modify::{ClientModifyRequest, ModifyRequest}; pub use order::{ - ClientLimit, ClientOrder, ClientOrderRequest, ClientTrigger, MarketCloseParams, - MarketOrderParams, Order, + ClientLimit, ClientOrder, ClientOrderRequest, ClientTrigger, Limit, MarketCloseParams, + MarketOrderParams, Order, OrderRequest, Trigger, };