@@ -15,6 +15,7 @@ use crate::{
1515 prelude:: * ,
1616 req:: HttpClient ,
1717 signature:: sign_l1_action,
18+ ws:: WsPostClient ,
1819 BaseUrl , BulkCancelCloid , Error , ExchangeResponseStatus ,
1920} ;
2021use crate :: { ClassTransfer , SpotSend , SpotUser , VaultTransfer , Withdraw3 } ;
@@ -39,6 +40,7 @@ pub struct ExchangeClient {
3940 pub meta : Meta ,
4041 pub vault_address : Option < H160 > ,
4142 pub coin_to_asset : HashMap < String , u32 > ,
43+ pub ws_post_client : Option < WsPostClient > ,
4244}
4345
4446#[ derive( Serialize , Deserialize ) ]
@@ -122,6 +124,7 @@ impl ExchangeClient {
122124 base_url : base_url. get_url ( ) ,
123125 } ,
124126 coin_to_asset,
127+ ws_post_client : None ,
125128 } )
126129 }
127130
@@ -759,6 +762,75 @@ impl ExchangeClient {
759762 let signature = sign_l1_action ( wallet, connection_id, is_mainnet) ?;
760763 self . post ( action, signature, timestamp) . await
761764 }
765+
766+ /// Initialize WebSocket post client for faster order execution
767+ pub async fn init_ws_post_client ( & mut self ) -> Result < ( ) > {
768+ let base_url = match self . http_client . base_url . as_str ( ) {
769+ "https://api.hyperliquid.xyz" => BaseUrl :: Mainnet ,
770+ "https://api.hyperliquid-testnet.xyz" => BaseUrl :: Testnet ,
771+ _ => return Err ( Error :: GenericRequest ( "Invalid base URL" . to_string ( ) ) ) ,
772+ } ;
773+
774+ self . ws_post_client = Some ( WsPostClient :: new ( base_url) . await ?) ;
775+ Ok ( ( ) )
776+ }
777+
778+ /// Execute bulk order via WebSocket for lower latency
779+ pub async fn bulk_order_ws (
780+ & self ,
781+ orders : Vec < ClientOrderRequest > ,
782+ wallet : Option < & LocalWallet > ,
783+ ) -> Result < ExchangeResponseStatus > {
784+ let ws_client = self . ws_post_client . as_ref ( )
785+ . ok_or_else ( || Error :: GenericRequest ( "WebSocket client not initialized. Call init_ws_post_client() first." . to_string ( ) ) ) ?;
786+
787+ let wallet = wallet. unwrap_or ( & self . wallet ) ;
788+ let mut transformed_orders = Vec :: new ( ) ;
789+
790+ for order in orders {
791+ transformed_orders. push ( order. convert ( & self . coin_to_asset ) ?) ;
792+ }
793+
794+ let action = BulkOrder {
795+ orders : transformed_orders,
796+ grouping : "na" . to_string ( ) ,
797+ builder : None ,
798+ } ;
799+
800+ let is_mainnet = self . http_client . is_mainnet ( ) ;
801+ ws_client. bulk_order ( action, wallet, is_mainnet, self . vault_address ) . await
802+ }
803+
804+ /// Execute bulk cancel by cloid via WebSocket for lower latency
805+ pub async fn bulk_cancel_by_cloid_ws (
806+ & self ,
807+ cancels : Vec < ClientCancelRequestCloid > ,
808+ wallet : Option < & LocalWallet > ,
809+ ) -> Result < ExchangeResponseStatus > {
810+ let ws_client = self . ws_post_client . as_ref ( )
811+ . ok_or_else ( || Error :: GenericRequest ( "WebSocket client not initialized. Call init_ws_post_client() first." . to_string ( ) ) ) ?;
812+
813+ let wallet = wallet. unwrap_or ( & self . wallet ) ;
814+ let mut transformed_cancels: Vec < CancelRequestCloid > = Vec :: new ( ) ;
815+
816+ for cancel in cancels. into_iter ( ) {
817+ let & asset = self
818+ . coin_to_asset
819+ . get ( & cancel. asset )
820+ . ok_or ( Error :: AssetNotFound ) ?;
821+ transformed_cancels. push ( CancelRequestCloid {
822+ asset,
823+ cloid : uuid_to_hex_string ( cancel. cloid ) ,
824+ } ) ;
825+ }
826+
827+ let action = BulkCancelCloid {
828+ cancels : transformed_cancels,
829+ } ;
830+
831+ let is_mainnet = self . http_client . is_mainnet ( ) ;
832+ ws_client. bulk_cancel_by_cloid ( action, wallet, is_mainnet, self . vault_address ) . await
833+ }
762834}
763835
764836fn round_to_decimals ( value : f64 , decimals : u32 ) -> f64 {
0 commit comments