Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/exchange/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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");
}
}
77 changes: 62 additions & 15 deletions src/exchange/exchange_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -485,18 +485,38 @@ impl ExchangeClient {
orders: Vec<ClientOrderRequest>,
wallet: Option<&PrivateKeySigner>,
) -> Result<ExchangeResponseStatus> {
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<ClientOrderRequest>,
wallet: Option<&PrivateKeySigner>,
grouping: OrderGrouping,
) -> Result<ExchangeResponseStatus> {
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<OrderRequest>,
wallet: Option<&PrivateKeySigner>,
grouping: OrderGrouping,
) -> Result<ExchangeResponseStatus> {
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)?;
Expand All @@ -508,10 +528,21 @@ impl ExchangeClient {
}

pub async fn bulk_order_with_builder(
&self,
orders: Vec<ClientOrderRequest>,
wallet: Option<&PrivateKeySigner>,
builder: BuilderInfo,
) -> Result<ExchangeResponseStatus> {
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<ClientOrderRequest>,
wallet: Option<&PrivateKeySigner>,
mut builder: BuilderInfo,
grouping: OrderGrouping,
) -> Result<ExchangeResponseStatus> {
let wallet = wallet.unwrap_or(&self.wallet);
let timestamp = next_nonce();
Expand All @@ -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)?;
Expand All @@ -550,9 +581,6 @@ impl ExchangeClient {
cancels: Vec<ClientCancelRequest>,
wallet: Option<&PrivateKeySigner>,
) -> Result<ExchangeResponseStatus> {
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
Expand All @@ -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<CancelRequest>,
wallet: Option<&PrivateKeySigner>,
) -> Result<ExchangeResponseStatus> {
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()))?;
Expand Down Expand Up @@ -660,11 +697,21 @@ impl ExchangeClient {
is_cross: bool,
wallet: Option<&PrivateKeySigner>,
) -> Result<ExchangeResponseStatus> {
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<ExchangeResponseStatus> {
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,
Expand Down
14 changes: 7 additions & 7 deletions src/exchange/exchange_responses.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
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,
pub avg_px: String,
pub oid: u64,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub enum ExchangeDataStatus {
Success,
Expand All @@ -24,19 +24,19 @@ pub enum ExchangeDataStatus {
Filled(FilledOrder),
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ExchangeDataStatuses {
pub statuses: Vec<ExchangeDataStatus>,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ExchangeResponse {
#[serde(rename = "type")]
pub response_type: String,
pub data: Option<ExchangeDataStatuses>,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "status", content = "response")]
pub enum ExchangeResponseStatus {
Expand Down
6 changes: 3 additions & 3 deletions src/exchange/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};