1use crate::exchange::{cancel::CancelRequest, modify::ModifyRequest, order::OrderRequest};
2pub(crate) use ethers::{
3 abi::{encode, ParamType, Tokenizable},
4 types::{
5 transaction::{
6 eip712,
7 eip712::{encode_eip712_type, EIP712Domain, Eip712, Eip712Error},
8 },
9 H160, U256,
10 },
11 utils::keccak256,
12};
13use serde::{Deserialize, Serialize};
14
15use super::{cancel::CancelRequestCloid, BuilderInfo};
16
17pub(crate) const HYPERLIQUID_EIP_PREFIX: &str = "HyperliquidTransaction:";
18
19fn eip_712_domain(chain_id: U256) -> EIP712Domain {
20 EIP712Domain {
21 name: Some("HyperliquidSignTransaction".to_string()),
22 version: Some("1".to_string()),
23 chain_id: Some(chain_id),
24 verifying_contract: Some(
25 "0x0000000000000000000000000000000000000000"
26 .parse()
27 .unwrap(),
28 ),
29 salt: None,
30 }
31}
32
33#[derive(Serialize, Deserialize, Debug, Clone)]
34#[serde(rename_all = "camelCase")]
35pub struct UsdSend {
36 pub signature_chain_id: U256,
37 pub hyperliquid_chain: String,
38 pub destination: String,
39 pub amount: String,
40 pub time: u64,
41}
42
43impl Eip712 for UsdSend {
44 type Error = Eip712Error;
45
46 fn domain(&self) -> Result<EIP712Domain, Self::Error> {
47 Ok(eip_712_domain(self.signature_chain_id))
48 }
49
50 fn type_hash() -> Result<[u8; 32], Self::Error> {
51 Ok(eip712::make_type_hash(
52 format!("{HYPERLIQUID_EIP_PREFIX}UsdSend"),
53 &[
54 ("hyperliquidChain".to_string(), ParamType::String),
55 ("destination".to_string(), ParamType::String),
56 ("amount".to_string(), ParamType::String),
57 ("time".to_string(), ParamType::Uint(64)),
58 ],
59 ))
60 }
61
62 fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
63 let Self {
64 signature_chain_id: _,
65 hyperliquid_chain,
66 destination,
67 amount,
68 time,
69 } = self;
70 let items = vec![
71 ethers::abi::Token::Uint(Self::type_hash()?.into()),
72 encode_eip712_type(hyperliquid_chain.clone().into_token()),
73 encode_eip712_type(destination.clone().into_token()),
74 encode_eip712_type(amount.clone().into_token()),
75 encode_eip712_type(time.into_token()),
76 ];
77 Ok(keccak256(encode(&items)))
78 }
79}
80
81#[derive(Serialize, Deserialize, Debug, Clone)]
82#[serde(rename_all = "camelCase")]
83pub struct UpdateLeverage {
84 pub asset: u32,
85 pub is_cross: bool,
86 pub leverage: u32,
87}
88
89#[derive(Serialize, Deserialize, Debug, Clone)]
90#[serde(rename_all = "camelCase")]
91pub struct UpdateIsolatedMargin {
92 pub asset: u32,
93 pub is_buy: bool,
94 pub ntli: i64,
95}
96
97#[derive(Serialize, Deserialize, Debug, Clone)]
98#[serde(rename_all = "camelCase")]
99pub struct BulkOrder {
100 pub orders: Vec<OrderRequest>,
101 pub grouping: String,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
103 pub builder: Option<BuilderInfo>,
104}
105
106#[derive(Serialize, Deserialize, Debug, Clone)]
107#[serde(rename_all = "camelCase")]
108pub struct BulkCancel {
109 pub cancels: Vec<CancelRequest>,
110}
111
112#[derive(Serialize, Deserialize, Debug, Clone)]
113#[serde(rename_all = "camelCase")]
114pub struct BulkModify {
115 pub modifies: Vec<ModifyRequest>,
116}
117
118#[derive(Serialize, Deserialize, Debug, Clone)]
119#[serde(rename_all = "camelCase")]
120pub struct BulkCancelCloid {
121 pub cancels: Vec<CancelRequestCloid>,
122}
123
124#[derive(Serialize, Deserialize, Debug, Clone)]
125#[serde(rename_all = "camelCase")]
126pub struct ApproveAgent {
127 pub signature_chain_id: U256,
128 pub hyperliquid_chain: String,
129 pub agent_address: H160,
130 pub agent_name: Option<String>,
131 pub nonce: u64,
132}
133
134impl Eip712 for ApproveAgent {
135 type Error = Eip712Error;
136
137 fn domain(&self) -> Result<EIP712Domain, Self::Error> {
138 Ok(eip_712_domain(self.signature_chain_id))
139 }
140
141 fn type_hash() -> Result<[u8; 32], Self::Error> {
142 Ok(eip712::make_type_hash(
143 format!("{HYPERLIQUID_EIP_PREFIX}ApproveAgent"),
144 &[
145 ("hyperliquidChain".to_string(), ParamType::String),
146 ("agentAddress".to_string(), ParamType::Address),
147 ("agentName".to_string(), ParamType::String),
148 ("nonce".to_string(), ParamType::Uint(64)),
149 ],
150 ))
151 }
152
153 fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
154 let Self {
155 signature_chain_id: _,
156 hyperliquid_chain,
157 agent_address,
158 agent_name,
159 nonce,
160 } = self;
161 let items = vec![
162 ethers::abi::Token::Uint(Self::type_hash()?.into()),
163 encode_eip712_type(hyperliquid_chain.clone().into_token()),
164 encode_eip712_type(agent_address.into_token()),
165 encode_eip712_type(agent_name.clone().unwrap_or_default().into_token()),
166 encode_eip712_type(nonce.into_token()),
167 ];
168 Ok(keccak256(encode(&items)))
169 }
170}
171
172#[derive(Serialize, Deserialize, Debug, Clone)]
173#[serde(rename_all = "camelCase")]
174pub struct Withdraw3 {
175 pub hyperliquid_chain: String,
176 pub signature_chain_id: U256,
177 pub amount: String,
178 pub time: u64,
179 pub destination: String,
180}
181
182impl Eip712 for Withdraw3 {
183 type Error = Eip712Error;
184
185 fn domain(&self) -> Result<EIP712Domain, Self::Error> {
186 Ok(eip_712_domain(self.signature_chain_id))
187 }
188
189 fn type_hash() -> Result<[u8; 32], Self::Error> {
190 Ok(eip712::make_type_hash(
191 format!("{HYPERLIQUID_EIP_PREFIX}Withdraw"),
192 &[
193 ("hyperliquidChain".to_string(), ParamType::String),
194 ("destination".to_string(), ParamType::String),
195 ("amount".to_string(), ParamType::String),
196 ("time".to_string(), ParamType::Uint(64)),
197 ],
198 ))
199 }
200
201 fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
202 let Self {
203 signature_chain_id: _,
204 hyperliquid_chain,
205 amount,
206 time,
207 destination,
208 } = self;
209 let items = vec![
210 ethers::abi::Token::Uint(Self::type_hash()?.into()),
211 encode_eip712_type(hyperliquid_chain.clone().into_token()),
212 encode_eip712_type(destination.clone().into_token()),
213 encode_eip712_type(amount.clone().into_token()),
214 encode_eip712_type(time.into_token()),
215 ];
216 Ok(keccak256(encode(&items)))
217 }
218}
219
220#[derive(Serialize, Deserialize, Debug, Clone)]
221#[serde(rename_all = "camelCase")]
222pub struct SpotSend {
223 pub hyperliquid_chain: String,
224 pub signature_chain_id: U256,
225 pub destination: String,
226 pub token: String,
227 pub amount: String,
228 pub time: u64,
229}
230
231impl Eip712 for SpotSend {
232 type Error = Eip712Error;
233
234 fn domain(&self) -> Result<EIP712Domain, Self::Error> {
235 Ok(eip_712_domain(self.signature_chain_id))
236 }
237
238 fn type_hash() -> Result<[u8; 32], Self::Error> {
239 Ok(eip712::make_type_hash(
240 format!("{HYPERLIQUID_EIP_PREFIX}SpotSend"),
241 &[
242 ("hyperliquidChain".to_string(), ParamType::String),
243 ("destination".to_string(), ParamType::String),
244 ("token".to_string(), ParamType::String),
245 ("amount".to_string(), ParamType::String),
246 ("time".to_string(), ParamType::Uint(64)),
247 ],
248 ))
249 }
250
251 fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
252 let Self {
253 signature_chain_id: _,
254 hyperliquid_chain,
255 destination,
256 token,
257 amount,
258 time,
259 } = self;
260 let items = vec![
261 ethers::abi::Token::Uint(Self::type_hash()?.into()),
262 encode_eip712_type(hyperliquid_chain.clone().into_token()),
263 encode_eip712_type(destination.clone().into_token()),
264 encode_eip712_type(token.clone().into_token()),
265 encode_eip712_type(amount.clone().into_token()),
266 encode_eip712_type(time.into_token()),
267 ];
268 Ok(keccak256(encode(&items)))
269 }
270}
271
272#[derive(Serialize, Deserialize, Debug, Clone)]
273#[serde(rename_all = "camelCase")]
274pub struct SpotUser {
275 pub class_transfer: ClassTransfer,
276}
277
278#[derive(Serialize, Deserialize, Debug, Clone)]
279#[serde(rename_all = "camelCase")]
280pub struct ClassTransfer {
281 pub usdc: u64,
282 pub to_perp: bool,
283}
284
285#[derive(Serialize, Deserialize, Debug, Clone)]
286#[serde(rename_all = "camelCase")]
287pub struct VaultTransfer {
288 pub vault_address: H160,
289 pub is_deposit: bool,
290 pub usd: u64,
291}
292
293#[derive(Serialize, Deserialize, Debug, Clone)]
294#[serde(rename_all = "camelCase")]
295pub struct SetReferrer {
296 pub code: String,
297}
298
299#[derive(Serialize, Deserialize, Debug, Clone)]
300#[serde(rename_all = "camelCase")]
301pub struct ApproveBuilderFee {
302 pub max_fee_rate: String,
303 pub builder: String,
304 pub nonce: u64,
305 pub signature_chain_id: U256,
306 pub hyperliquid_chain: String,
307}