diff --git a/src/exchange/actions.rs b/src/exchange/actions.rs index 71e9f4f1..e1f28fb6 100644 --- a/src/exchange/actions.rs +++ b/src/exchange/actions.rs @@ -275,6 +275,34 @@ pub struct ScheduleCancel { #[serde(rename_all = "camelCase")] pub struct ClaimRewards; +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct UserDexAbstraction { + #[serde(serialize_with = "serialize_hex")] + pub signature_chain_id: u64, + pub hyperliquid_chain: String, + pub user: Address, + pub enabled: bool, + pub nonce: u64, +} + +impl Eip712 for UserDexAbstraction { + fn domain(&self) -> Eip712Domain { + eip_712_domain(self.signature_chain_id) + } + + fn struct_hash(&self) -> B256 { + let items = ( + keccak256("HyperliquidTransaction:UserDexAbstraction(string hyperliquidChain,address user,bool enabled,uint64 nonce)"), + keccak256(&self.hyperliquid_chain), + &self.user, + self.enabled, + &self.nonce, + ); + keccak256(items.abi_encode()) + } +} + impl Eip712 for ApproveBuilderFee { fn domain(&self) -> Eip712Domain { eip_712_domain(self.signature_chain_id) diff --git a/src/exchange/exchange_client.rs b/src/exchange/exchange_client.rs index 70b686b8..48a8e42b 100644 --- a/src/exchange/exchange_client.rs +++ b/src/exchange/exchange_client.rs @@ -13,7 +13,7 @@ use crate::{ actions::{ ApproveAgent, ApproveBuilderFee, BulkCancel, BulkModify, BulkOrder, ClaimRewards, EvmUserModify, ScheduleCancel, SendAsset, SetReferrer, UpdateIsolatedMargin, - UpdateLeverage, UsdSend, + UpdateLeverage, UsdSend, UserDexAbstraction, }, cancel::{CancelRequest, CancelRequestCloid, ClientCancelRequestCloid}, modify::{ClientModifyRequest, ModifyRequest}, @@ -82,6 +82,7 @@ pub enum Actions { EvmUserModify(EvmUserModify), ScheduleCancel(ScheduleCancel), ClaimRewards(ClaimRewards), + UserDexAbstraction(UserDexAbstraction), } impl Actions { @@ -867,6 +868,37 @@ impl ExchangeClient { self.post(action, signature, timestamp).await } + + pub async fn user_dex_abstraction( + &self, + user: Address, + enabled: bool, + wallet: Option<&PrivateKeySigner>, + ) -> Result { + let wallet = wallet.unwrap_or(&self.wallet); + + let hyperliquid_chain = if self.http_client.is_mainnet() { + "Mainnet".to_string() + } else { + "Testnet".to_string() + }; + + let timestamp = next_nonce(); + + let user_dex_abstraction = UserDexAbstraction { + signature_chain_id: 421614, + hyperliquid_chain, + user, + enabled, + nonce: timestamp, + }; + + let signature = sign_typed_data(&user_dex_abstraction, wallet)?; + let action = serde_json::to_value(Actions::UserDexAbstraction(user_dex_abstraction)) + .map_err(|e| Error::JsonParse(e.to_string()))?; + + self.post(action, signature, timestamp).await + } } fn round_to_decimals(value: f64, decimals: u32) -> f64 {