From 1d894613f6f4dd4d9e70f9315a5fbfe6f978cdfd Mon Sep 17 00:00:00 2001 From: Suryansh Date: Thu, 28 Nov 2024 16:04:26 +0900 Subject: [PATCH 01/16] Add initial code --- src/bin/approve_builder_fee.rs | 26 +++++++++++++++++ src/exchange/actions.rs | 7 +++++ src/exchange/exchange_client.rs | 52 ++++++++++++++++++++++++--------- 3 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 src/bin/approve_builder_fee.rs diff --git a/src/bin/approve_builder_fee.rs b/src/bin/approve_builder_fee.rs new file mode 100644 index 0000000..7c52a7f --- /dev/null +++ b/src/bin/approve_builder_fee.rs @@ -0,0 +1,26 @@ +use ethers::signers::LocalWallet; +use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient}; +use log::info; + +#[tokio::main] +async fn main() { + env_logger::init(); + // Key was randomly generated for testing and shouldn't be used with any real funds + let wallet: LocalWallet = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e" + .parse() + .unwrap(); + + let exchange_client = + ExchangeClient::new(None, wallet.clone(), Some(BaseUrl::Testnet), None, None) + .await + .unwrap(); + + let max_fee_rate = "10"; + let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79"; + + info!("something"); + let res = exchange_client + .approve_builder_fee(builder.to_string(), max_fee_rate.to_string(), Some(&wallet)) + .await; + info!("Response from result: {res:#?}"); +} diff --git a/src/exchange/actions.rs b/src/exchange/actions.rs index d389b6d..127ab22 100644 --- a/src/exchange/actions.rs +++ b/src/exchange/actions.rs @@ -293,3 +293,10 @@ pub struct VaultTransfer { pub struct SetReferrer { pub code: String, } + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct ApproveBuilderFee { + pub max_fee_rate: String, + pub builder: String, +} diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index c456235..b5d1839 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -2,8 +2,8 @@ use crate::signature::sign_typed_data; use crate::{ exchange::{ actions::{ - ApproveAgent, BulkCancel, BulkModify, BulkOrder, SetReferrer, UpdateIsolatedMargin, - UpdateLeverage, UsdSend, + ApproveAgent, ApproveBuilderFee, BulkCancel, BulkModify, BulkOrder, SetReferrer, + UpdateIsolatedMargin, UpdateLeverage, UsdSend, }, cancel::{CancelRequest, CancelRequestCloid}, modify::{ClientModifyRequest, ModifyRequest}, @@ -23,7 +23,7 @@ use ethers::{ signers::{LocalWallet, Signer}, types::{Signature, H160, H256}, }; -use log::debug; +use log::{debug, info}; use reqwest::Client; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -66,6 +66,7 @@ pub enum Actions { VaultTransfer(VaultTransfer), SpotSend(SpotSend), SetReferrer(SetReferrer), + ApproveBuilderFee(ApproveBuilderFee), } impl Actions { @@ -137,16 +138,18 @@ impl ExchangeClient { }; let res = serde_json::to_string(&exchange_payload) .map_err(|e| Error::JsonParse(e.to_string()))?; - debug!("Sending request {res:?}"); - - serde_json::from_str( - &self - .http_client - .post("/exchange", res) - .await - .map_err(|e| Error::JsonParse(e.to_string()))?, - ) - .map_err(|e| Error::JsonParse(e.to_string())) + info!("Sending request {res:?}"); + + let output = &self + .http_client + .post("/exchange", res) + .await + .map_err(|e| { + info!("{e:#?}"); + Error::JsonParse(e.to_string()) + })?; + info!("{output:?}"); + serde_json::from_str(output).map_err(|e| Error::JsonParse(e.to_string())) } pub async fn usdc_transfer( @@ -662,6 +665,29 @@ impl ExchangeClient { let signature = sign_l1_action(wallet, connection_id, is_mainnet)?; self.post(action, signature, timestamp).await } + + pub async fn approve_builder_fee( + &self, + builder: String, + max_fee_rate: String, + wallet: Option<&LocalWallet>, + ) -> Result { + let wallet = wallet.unwrap_or(&self.wallet); + let timestamp = next_nonce(); + + let action = Actions::ApproveBuilderFee(ApproveBuilderFee { + builder, + max_fee_rate, + }); + + info!("{timestamp:?}"); + let connection_id = action.hash(timestamp, self.vault_address)?; + let action = serde_json::to_value(&action).map_err(|e| Error::JsonParse(e.to_string()))?; + + let is_mainnet = self.http_client.is_mainnet(); + let signature = sign_l1_action(wallet, connection_id, is_mainnet)?; + self.post(action, signature, timestamp).await + } } fn round_to_decimals(value: f64, decimals: u32) -> f64 { From 3f7278240be894cba78e4953065c200c9c0ecad8 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Thu, 28 Nov 2024 16:41:53 +0900 Subject: [PATCH 02/16] Update for clippy? --- src/exchange/exchange_client.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index b5d1839..3c86981 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -140,14 +140,10 @@ impl ExchangeClient { .map_err(|e| Error::JsonParse(e.to_string()))?; info!("Sending request {res:?}"); - let output = &self - .http_client - .post("/exchange", res) - .await - .map_err(|e| { - info!("{e:#?}"); - Error::JsonParse(e.to_string()) - })?; + let output = &self.http_client.post("/exchange", res).await.map_err(|e| { + info!("{e:#?}"); + Error::JsonParse(e.to_string()) + })?; info!("{output:?}"); serde_json::from_str(output).map_err(|e| Error::JsonParse(e.to_string())) } From 4b6879f068bf7f4d077b33f632974c8e7ea55892 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Sat, 30 Nov 2024 23:07:40 +0900 Subject: [PATCH 03/16] Update style stuff --- src/bin/approve_builder_fee.rs | 5 ++--- src/exchange/exchange_client.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/bin/approve_builder_fee.rs b/src/bin/approve_builder_fee.rs index 7c52a7f..1ab3719 100644 --- a/src/bin/approve_builder_fee.rs +++ b/src/bin/approve_builder_fee.rs @@ -18,9 +18,8 @@ async fn main() { let max_fee_rate = "10"; let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79"; - info!("something"); - let res = exchange_client + let resp = exchange_client .approve_builder_fee(builder.to_string(), max_fee_rate.to_string(), Some(&wallet)) .await; - info!("Response from result: {res:#?}"); + info!("resp: {resp:#?}"); } diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 3c86981..96ecdb4 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -138,7 +138,7 @@ impl ExchangeClient { }; let res = serde_json::to_string(&exchange_payload) .map_err(|e| Error::JsonParse(e.to_string()))?; - info!("Sending request {res:?}"); + debug!("Sending request {res:?}"); let output = &self.http_client.post("/exchange", res).await.map_err(|e| { info!("{e:#?}"); From 87d93d15abd748f9c9d96ac33d5c001409050603 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Mon, 9 Dec 2024 13:30:59 +0900 Subject: [PATCH 04/16] Add builder order functions --- src/exchange/actions.rs | 10 ++++- src/exchange/builder.rs | 10 +++++ src/exchange/exchange_client.rs | 67 ++++++++++++++++++++++++++++++++- src/exchange/mod.rs | 2 + 4 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/exchange/builder.rs diff --git a/src/exchange/actions.rs b/src/exchange/actions.rs index 127ab22..9727265 100644 --- a/src/exchange/actions.rs +++ b/src/exchange/actions.rs @@ -12,7 +12,7 @@ pub(crate) use ethers::{ }; use serde::{Deserialize, Serialize}; -use super::cancel::CancelRequestCloid; +use super::{cancel::CancelRequestCloid, BuilderInfo}; pub(crate) const HYPERLIQUID_EIP_PREFIX: &str = "HyperliquidTransaction:"; @@ -101,6 +101,14 @@ pub struct BulkOrder { pub grouping: String, } +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct BulkOrderWithBuilder { + pub orders: Vec, + pub grouping: String, + pub builder: BuilderInfo, +} + #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct BulkCancel { diff --git a/src/exchange/builder.rs b/src/exchange/builder.rs new file mode 100644 index 0000000..e0ff416 --- /dev/null +++ b/src/exchange/builder.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct BuilderInfo { + #[serde(rename = "b")] + pub builder: String, + #[serde(rename = "f")] + pub fee: f64, +} diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 96ecdb4..13a5bb4 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -30,7 +30,7 @@ use std::collections::HashMap; use super::cancel::ClientCancelRequestCloid; use super::order::{MarketCloseParams, MarketOrderParams}; -use super::{ClientLimit, ClientOrder}; +use super::{BuilderInfo, BulkOrderWithBuilder, ClientLimit, ClientOrder}; pub struct ExchangeClient { pub http_client: HttpClient, @@ -57,6 +57,8 @@ pub enum Actions { UpdateLeverage(UpdateLeverage), UpdateIsolatedMargin(UpdateIsolatedMargin), Order(BulkOrder), + #[serde(rename = "order")] + OrderWithBuilder(BulkOrderWithBuilder), Cancel(BulkCancel), CancelByCloid(BulkCancelCloid), BatchModify(BulkModify), @@ -251,6 +253,31 @@ impl ExchangeClient { self.order(order, params.wallet).await } + pub async fn market_open_with_builder( + &self, + params: MarketOrderParams<'_>, + builder: BuilderInfo, + ) -> Result { + let slippage = params.slippage.unwrap_or(0.05); // Default 5% slippage + let (px, sz_decimals) = self + .calculate_slippage_price(params.asset, params.is_buy, slippage, params.px) + .await?; + + let order = ClientOrderRequest { + asset: params.asset.to_string(), + is_buy: params.is_buy, + reduce_only: false, + limit_px: px, + sz: round_to_decimals(params.sz, sz_decimals), + cloid: params.cloid, + order_type: ClientOrder::Limit(ClientLimit { + tif: "Ioc".to_string(), + }), + }; + + self.order_with_builder(order, params.wallet, builder).await + } + pub async fn market_close( &self, params: MarketCloseParams<'_>, @@ -362,6 +389,16 @@ impl ExchangeClient { self.bulk_order(vec![order], wallet).await } + pub async fn order_with_builder( + &self, + order: ClientOrderRequest, + wallet: Option<&LocalWallet>, + builder: BuilderInfo, + ) -> Result { + self.bulk_order_with_builder(vec![order], wallet, builder) + .await + } + pub async fn bulk_order( &self, orders: Vec, @@ -388,6 +425,34 @@ impl ExchangeClient { self.post(action, signature, timestamp).await } + pub async fn bulk_order_with_builder( + &self, + orders: Vec, + wallet: Option<&LocalWallet>, + builder: BuilderInfo, + ) -> Result { + let wallet = wallet.unwrap_or(&self.wallet); + let timestamp = next_nonce(); + + let mut transformed_orders = Vec::new(); + + for order in orders { + transformed_orders.push(order.convert(&self.coin_to_asset)?); + } + + let action = Actions::OrderWithBuilder(BulkOrderWithBuilder { + orders: transformed_orders, + grouping: "na".to_string(), + builder, + }); + let connection_id = action.hash(timestamp, self.vault_address)?; + let action = serde_json::to_value(&action).map_err(|e| Error::JsonParse(e.to_string()))?; + + let is_mainnet = self.http_client.is_mainnet(); + let signature = sign_l1_action(wallet, connection_id, is_mainnet)?; + self.post(action, signature, timestamp).await + } + pub async fn cancel( &self, cancel: ClientCancelRequest, diff --git a/src/exchange/mod.rs b/src/exchange/mod.rs index 97a192a..0add0dc 100644 --- a/src/exchange/mod.rs +++ b/src/exchange/mod.rs @@ -4,6 +4,7 @@ mod exchange_client; mod exchange_responses; mod modify; mod order; +mod builder; pub use actions::*; pub use cancel::{ClientCancelRequest, ClientCancelRequestCloid}; @@ -14,3 +15,4 @@ pub use order::{ ClientLimit, ClientOrder, ClientOrderRequest, ClientTrigger, MarketCloseParams, MarketOrderParams, Order, }; +pub use builder::*; From 530d80ebb39a2346f442d1efb767ca982afd954b Mon Sep 17 00:00:00 2001 From: Suryansh Date: Mon, 9 Dec 2024 15:39:03 +0900 Subject: [PATCH 05/16] Add examples --- .../market_order_with_builder_and_cancel.rs | 88 +++++++++++++++++++ src/bin/order_with_builder_and_cancel.rs | 72 +++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 src/bin/market_order_with_builder_and_cancel.rs create mode 100644 src/bin/order_with_builder_and_cancel.rs diff --git a/src/bin/market_order_with_builder_and_cancel.rs b/src/bin/market_order_with_builder_and_cancel.rs new file mode 100644 index 0000000..d8eccaa --- /dev/null +++ b/src/bin/market_order_with_builder_and_cancel.rs @@ -0,0 +1,88 @@ +use ethers::signers::LocalWallet; +use log::info; + +use hyperliquid_rust_sdk::{ + BaseUrl, BuilderInfo, ExchangeClient, ExchangeDataStatus, ExchangeResponseStatus, + MarketCloseParams, MarketOrderParams, +}; +use std::{thread::sleep, time::Duration}; + +#[tokio::main] +async fn main() { + env_logger::init(); + // Key was randomly generated for testing and shouldn't be used with any real funds + let wallet: LocalWallet = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e" + .parse() + .unwrap(); + + let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None) + .await + .unwrap(); + + // Market open order + let market_open_params = MarketOrderParams { + asset: "ETH", + is_buy: true, + sz: 0.01, + px: None, + slippage: Some(0.01), // 1% slippage + cloid: None, + wallet: None, + }; + + let fee = 10.0f64; + let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79"; + + let response = exchange_client + .market_open_with_builder( + market_open_params, + BuilderInfo { + builder: builder.to_string(), + fee, + }, + ) + .await + .unwrap(); + info!("Market open order placed: {response:?}"); + + let response = match response { + ExchangeResponseStatus::Ok(exchange_response) => exchange_response, + ExchangeResponseStatus::Err(e) => panic!("Error with exchange response: {e}"), + }; + let status = response.data.unwrap().statuses[0].clone(); + match status { + ExchangeDataStatus::Filled(order) => info!("Order filled: {order:?}"), + ExchangeDataStatus::Resting(order) => info!("Order resting: {order:?}"), + _ => panic!("Unexpected status: {status:?}"), + }; + + // Wait for a while before closing the position + sleep(Duration::from_secs(10)); + + // Market close order + let market_close_params = MarketCloseParams { + asset: "ETH", + sz: None, // Close entire position + px: None, + slippage: Some(0.01), // 1% slippage + cloid: None, + wallet: None, + }; + + let response = exchange_client + .market_close(market_close_params) + .await + .unwrap(); + info!("Market close order placed: {response:?}"); + + let response = match response { + ExchangeResponseStatus::Ok(exchange_response) => exchange_response, + ExchangeResponseStatus::Err(e) => panic!("Error with exchange response: {e}"), + }; + let status = response.data.unwrap().statuses[0].clone(); + match status { + ExchangeDataStatus::Filled(order) => info!("Close order filled: {order:?}"), + ExchangeDataStatus::Resting(order) => info!("Close order resting: {order:?}"), + _ => panic!("Unexpected status: {status:?}"), + }; +} diff --git a/src/bin/order_with_builder_and_cancel.rs b/src/bin/order_with_builder_and_cancel.rs new file mode 100644 index 0000000..3b41f24 --- /dev/null +++ b/src/bin/order_with_builder_and_cancel.rs @@ -0,0 +1,72 @@ +use ethers::signers::LocalWallet; +use log::info; + +use hyperliquid_rust_sdk::{ + BaseUrl, BuilderInfo, ClientCancelRequest, ClientLimit, ClientOrder, ClientOrderRequest, + ExchangeClient, ExchangeDataStatus, ExchangeResponseStatus, +}; +use std::{thread::sleep, time::Duration}; + +#[tokio::main] +async fn main() { + env_logger::init(); + // Key was randomly generated for testing and shouldn't be used with any real funds + let wallet: LocalWallet = "e908f86dbb4d55ac876378565aafeabc187f6690f046459397b17d9b9a19688e" + .parse() + .unwrap(); + + let exchange_client = ExchangeClient::new(None, wallet, Some(BaseUrl::Testnet), None, None) + .await + .unwrap(); + + let order = ClientOrderRequest { + asset: "ETH".to_string(), + is_buy: true, + reduce_only: false, + limit_px: 1800.0, + sz: 0.01, + cloid: None, + order_type: ClientOrder::Limit(ClientLimit { + tif: "Gtc".to_string(), + }), + }; + + let fee = 10.0f64; + let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79"; + + let response = exchange_client + .order_with_builder( + order, + None, + BuilderInfo { + builder: builder.to_string(), + fee, + }, + ) + .await + .unwrap(); + info!("Order placed: {response:?}"); + + let response = match response { + ExchangeResponseStatus::Ok(exchange_response) => exchange_response, + ExchangeResponseStatus::Err(e) => panic!("error with exchange response: {e}"), + }; + let status = response.data.unwrap().statuses[0].clone(); + let oid = match status { + ExchangeDataStatus::Filled(order) => order.oid, + ExchangeDataStatus::Resting(order) => order.oid, + _ => panic!("Error: {status:?}"), + }; + + // So you can see the order before it's cancelled + sleep(Duration::from_secs(10)); + + let cancel = ClientCancelRequest { + asset: "ETH".to_string(), + oid, + }; + + // This response will return an error if order was filled (since you can't cancel a filled order), otherwise it will cancel the order + let response = exchange_client.cancel(cancel, None).await.unwrap(); + info!("Order potentially cancelled: {response:?}"); +} From 7cf34b7824204dc636906ab662587612b405f977 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Tue, 10 Dec 2024 00:38:30 +0900 Subject: [PATCH 06/16] Minor updates --- src/bin/approve_builder_fee.rs | 1 - src/exchange/exchange_client.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/bin/approve_builder_fee.rs b/src/bin/approve_builder_fee.rs index 1ab3719..cdda17c 100644 --- a/src/bin/approve_builder_fee.rs +++ b/src/bin/approve_builder_fee.rs @@ -1,6 +1,5 @@ use ethers::signers::LocalWallet; use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient}; -use log::info; #[tokio::main] async fn main() { diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 13a5bb4..4181f3c 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -741,7 +741,6 @@ impl ExchangeClient { max_fee_rate, }); - info!("{timestamp:?}"); let connection_id = action.hash(timestamp, self.vault_address)?; let action = serde_json::to_value(&action).map_err(|e| Error::JsonParse(e.to_string()))?; From 15369f3b47220dd376f067c26e2f1676dc2ee1e3 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Tue, 10 Dec 2024 01:49:56 +0900 Subject: [PATCH 07/16] Update fee in example --- src/bin/approve_builder_fee.rs | 3 ++- src/exchange/exchange_client.rs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/approve_builder_fee.rs b/src/bin/approve_builder_fee.rs index cdda17c..e260c15 100644 --- a/src/bin/approve_builder_fee.rs +++ b/src/bin/approve_builder_fee.rs @@ -1,5 +1,6 @@ use ethers::signers::LocalWallet; use hyperliquid_rust_sdk::{BaseUrl, ExchangeClient}; +use log::info; #[tokio::main] async fn main() { @@ -14,7 +15,7 @@ async fn main() { .await .unwrap(); - let max_fee_rate = "10"; + let max_fee_rate = "0.001%"; let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79"; let resp = exchange_client diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 4181f3c..52b7490 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -146,7 +146,6 @@ impl ExchangeClient { info!("{e:#?}"); Error::JsonParse(e.to_string()) })?; - info!("{output:?}"); serde_json::from_str(output).map_err(|e| Error::JsonParse(e.to_string())) } From 054c6140728992350b9b4faa8d2cabe30205eda9 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Tue, 10 Dec 2024 13:27:11 +0900 Subject: [PATCH 08/16] Update ApproveBuilderFee --- src/exchange/actions.rs | 3 +++ src/exchange/exchange_client.rs | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/exchange/actions.rs b/src/exchange/actions.rs index 9727265..54f4a83 100644 --- a/src/exchange/actions.rs +++ b/src/exchange/actions.rs @@ -307,4 +307,7 @@ pub struct SetReferrer { pub struct ApproveBuilderFee { pub max_fee_rate: String, pub builder: String, + pub nonce: u64, + pub signature_chain_id: U256, + pub hyperliquid_chain: String, } diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 52b7490..a006d58 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -143,7 +143,6 @@ impl ExchangeClient { debug!("Sending request {res:?}"); let output = &self.http_client.post("/exchange", res).await.map_err(|e| { - info!("{e:#?}"); Error::JsonParse(e.to_string()) })?; serde_json::from_str(output).map_err(|e| Error::JsonParse(e.to_string())) @@ -735,9 +734,19 @@ impl ExchangeClient { let wallet = wallet.unwrap_or(&self.wallet); let timestamp = next_nonce(); + let hyperliquid_chain = if self.http_client.is_mainnet() { + "Mainnet".to_string() + } else { + "Testnet".to_string() + }; + + let nonce = next_nonce(); let action = Actions::ApproveBuilderFee(ApproveBuilderFee { + signature_chain_id: 421614.into(), + hyperliquid_chain, builder, max_fee_rate, + nonce, }); let connection_id = action.hash(timestamp, self.vault_address)?; From 0a0d934b2d909d136c58cdf5e96e594dd1984527 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Tue, 10 Dec 2024 13:31:28 +0900 Subject: [PATCH 09/16] Style changes --- src/exchange/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/exchange/mod.rs b/src/exchange/mod.rs index 0add0dc..7b9e904 100644 --- a/src/exchange/mod.rs +++ b/src/exchange/mod.rs @@ -1,12 +1,13 @@ mod actions; +mod builder; mod cancel; mod exchange_client; mod exchange_responses; mod modify; mod order; -mod builder; pub use actions::*; +pub use builder::*; pub use cancel::{ClientCancelRequest, ClientCancelRequestCloid}; pub use exchange_client::*; pub use exchange_responses::*; @@ -15,4 +16,3 @@ pub use order::{ ClientLimit, ClientOrder, ClientOrderRequest, ClientTrigger, MarketCloseParams, MarketOrderParams, Order, }; -pub use builder::*; From 04124fad9eeae86ecfc27d4401448bf78add58f5 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Tue, 10 Dec 2024 13:39:32 +0900 Subject: [PATCH 10/16] Another style change --- src/exchange/exchange_client.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index a006d58..81b4280 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -23,7 +23,7 @@ use ethers::{ signers::{LocalWallet, Signer}, types::{Signature, H160, H256}, }; -use log::{debug, info}; +use log::debug; use reqwest::Client; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -142,9 +142,11 @@ impl ExchangeClient { .map_err(|e| Error::JsonParse(e.to_string()))?; debug!("Sending request {res:?}"); - let output = &self.http_client.post("/exchange", res).await.map_err(|e| { - Error::JsonParse(e.to_string()) - })?; + let output = &self + .http_client + .post("/exchange", res) + .await + .map_err(|e| Error::JsonParse(e.to_string()))?; serde_json::from_str(output).map_err(|e| Error::JsonParse(e.to_string())) } From f192aa2d626b99676cae93e62156d15ff6292bc3 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Tue, 10 Dec 2024 13:52:29 +0900 Subject: [PATCH 11/16] Changing serde stuff --- src/exchange/exchange_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 81b4280..1ee97da 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -57,7 +57,7 @@ pub enum Actions { UpdateLeverage(UpdateLeverage), UpdateIsolatedMargin(UpdateIsolatedMargin), Order(BulkOrder), - #[serde(rename = "order")] + #[serde(rename(serialize = "order"))] OrderWithBuilder(BulkOrderWithBuilder), Cancel(BulkCancel), CancelByCloid(BulkCancelCloid), From 0324ac7a0023fbaa2e0983f3e276bcf86cee31f1 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Tue, 10 Dec 2024 23:04:53 +0900 Subject: [PATCH 12/16] Minor change --- src/exchange/exchange_client.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 1ee97da..541eda7 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -742,13 +742,12 @@ impl ExchangeClient { "Testnet".to_string() }; - let nonce = next_nonce(); let action = Actions::ApproveBuilderFee(ApproveBuilderFee { signature_chain_id: 421614.into(), hyperliquid_chain, builder, max_fee_rate, - nonce, + nonce: timestamp, }); let connection_id = action.hash(timestamp, self.vault_address)?; From 64fbebedd97abac651530dadb40ba3d2f60c598d Mon Sep 17 00:00:00 2001 From: Suryansh Date: Thu, 12 Dec 2024 17:41:26 +0900 Subject: [PATCH 13/16] Update BuilderInfo struct --- src/bin/approve_builder_fee.rs | 4 ++-- src/bin/market_order_with_builder_and_cancel.rs | 2 +- src/bin/order_with_builder_and_cancel.rs | 2 +- src/exchange/builder.rs | 2 +- src/exchange/exchange_client.rs | 4 +++- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/bin/approve_builder_fee.rs b/src/bin/approve_builder_fee.rs index e260c15..a5078c5 100644 --- a/src/bin/approve_builder_fee.rs +++ b/src/bin/approve_builder_fee.rs @@ -15,8 +15,8 @@ async fn main() { .await .unwrap(); - let max_fee_rate = "0.001%"; - let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79"; + let max_fee_rate = "0.1%"; + let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79".to_lowercase(); let resp = exchange_client .approve_builder_fee(builder.to_string(), max_fee_rate.to_string(), Some(&wallet)) diff --git a/src/bin/market_order_with_builder_and_cancel.rs b/src/bin/market_order_with_builder_and_cancel.rs index d8eccaa..f8fa561 100644 --- a/src/bin/market_order_with_builder_and_cancel.rs +++ b/src/bin/market_order_with_builder_and_cancel.rs @@ -30,7 +30,7 @@ async fn main() { wallet: None, }; - let fee = 10.0f64; + let fee = 1; let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79"; let response = exchange_client diff --git a/src/bin/order_with_builder_and_cancel.rs b/src/bin/order_with_builder_and_cancel.rs index 3b41f24..7660d61 100644 --- a/src/bin/order_with_builder_and_cancel.rs +++ b/src/bin/order_with_builder_and_cancel.rs @@ -31,7 +31,7 @@ async fn main() { }), }; - let fee = 10.0f64; + let fee = 1.0f64; let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79"; let response = exchange_client diff --git a/src/exchange/builder.rs b/src/exchange/builder.rs index e0ff416..aeeb978 100644 --- a/src/exchange/builder.rs +++ b/src/exchange/builder.rs @@ -6,5 +6,5 @@ pub struct BuilderInfo { #[serde(rename = "b")] pub builder: String, #[serde(rename = "f")] - pub fee: f64, + pub fee: u64, } diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 541eda7..310bac3 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -429,11 +429,13 @@ impl ExchangeClient { &self, orders: Vec, wallet: Option<&LocalWallet>, - builder: BuilderInfo, + mut builder: BuilderInfo, ) -> Result { let wallet = wallet.unwrap_or(&self.wallet); let timestamp = next_nonce(); + builder.builder = builder.builder.to_lowercase(); + let mut transformed_orders = Vec::new(); for order in orders { From bfe502845262d55f3fad5745867ea891f26e2aef Mon Sep 17 00:00:00 2001 From: Suryansh Date: Thu, 12 Dec 2024 17:52:35 +0900 Subject: [PATCH 14/16] Update order_with_builder_and_cancel --- src/bin/order_with_builder_and_cancel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/order_with_builder_and_cancel.rs b/src/bin/order_with_builder_and_cancel.rs index 7660d61..33622ca 100644 --- a/src/bin/order_with_builder_and_cancel.rs +++ b/src/bin/order_with_builder_and_cancel.rs @@ -31,7 +31,7 @@ async fn main() { }), }; - let fee = 1.0f64; + let fee = 1u64; let builder = "0x1ab189B7801140900C711E458212F9c76F8dAC79"; let response = exchange_client From 6716a18314073d38cf0791ff635dd1ce35aab8e3 Mon Sep 17 00:00:00 2001 From: Suryansh Date: Tue, 24 Dec 2024 01:46:26 +0900 Subject: [PATCH 15/16] Make a genius refactor --- src/exchange/actions.rs | 10 ++-------- src/exchange/builder.rs | 2 +- src/exchange/exchange_client.rs | 9 ++++----- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/exchange/actions.rs b/src/exchange/actions.rs index 54f4a83..e4f030e 100644 --- a/src/exchange/actions.rs +++ b/src/exchange/actions.rs @@ -99,14 +99,8 @@ pub struct UpdateIsolatedMargin { pub struct BulkOrder { pub orders: Vec, pub grouping: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(rename_all = "camelCase")] -pub struct BulkOrderWithBuilder { - pub orders: Vec, - pub grouping: String, - pub builder: BuilderInfo, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub builder: Option, } #[derive(Serialize, Deserialize, Debug, Clone)] diff --git a/src/exchange/builder.rs b/src/exchange/builder.rs index aeeb978..227d83a 100644 --- a/src/exchange/builder.rs +++ b/src/exchange/builder.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Default, Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct BuilderInfo { #[serde(rename = "b")] diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 310bac3..39312b3 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -30,7 +30,7 @@ use std::collections::HashMap; use super::cancel::ClientCancelRequestCloid; use super::order::{MarketCloseParams, MarketOrderParams}; -use super::{BuilderInfo, BulkOrderWithBuilder, ClientLimit, ClientOrder}; +use super::{BuilderInfo, ClientLimit, ClientOrder}; pub struct ExchangeClient { pub http_client: HttpClient, @@ -57,8 +57,6 @@ pub enum Actions { UpdateLeverage(UpdateLeverage), UpdateIsolatedMargin(UpdateIsolatedMargin), Order(BulkOrder), - #[serde(rename(serialize = "order"))] - OrderWithBuilder(BulkOrderWithBuilder), Cancel(BulkCancel), CancelByCloid(BulkCancelCloid), BatchModify(BulkModify), @@ -416,6 +414,7 @@ impl ExchangeClient { let action = Actions::Order(BulkOrder { orders: transformed_orders, grouping: "na".to_string(), + builder: None, }); let connection_id = action.hash(timestamp, self.vault_address)?; let action = serde_json::to_value(&action).map_err(|e| Error::JsonParse(e.to_string()))?; @@ -442,10 +441,10 @@ impl ExchangeClient { transformed_orders.push(order.convert(&self.coin_to_asset)?); } - let action = Actions::OrderWithBuilder(BulkOrderWithBuilder { + let action = Actions::Order(BulkOrder { orders: transformed_orders, grouping: "na".to_string(), - builder, + builder: Some(builder), }); let connection_id = action.hash(timestamp, self.vault_address)?; let action = serde_json::to_value(&action).map_err(|e| Error::JsonParse(e.to_string()))?; From 02633a4a858ce5dd517258cd04ad634c1b3d89ff Mon Sep 17 00:00:00 2001 From: Suryansh Date: Tue, 24 Dec 2024 02:05:13 +0900 Subject: [PATCH 16/16] Attack on Clippy: Episode 1 --- src/exchange/exchange_client.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 39312b3..2ed3a76 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -806,6 +806,7 @@ mod tests { cloid: None, }], grouping: "na".to_string(), + builder: None, }); let connection_id = action.hash(1583838, None)?; @@ -836,6 +837,7 @@ mod tests { cloid: Some(uuid_to_hex_string(cloid.unwrap())), }], grouping: "na".to_string(), + builder: None, }); let connection_id = action.hash(1583838, None)?; @@ -880,6 +882,7 @@ mod tests { } ], grouping: "na".to_string(), + builder: None, }); let connection_id = action.hash(1583838, None)?;