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;
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}
103
104#[derive(Serialize, Deserialize, Debug, Clone)]
105#[serde(rename_all = "camelCase")]
106pub struct BulkCancel {
107 pub cancels: Vec<CancelRequest>,
108}
109
110#[derive(Serialize, Deserialize, Debug, Clone)]
111#[serde(rename_all = "camelCase")]
112pub struct BulkModify {
113 pub modifies: Vec<ModifyRequest>,
114}
115
116#[derive(Serialize, Deserialize, Debug, Clone)]
117#[serde(rename_all = "camelCase")]
118pub struct BulkCancelCloid {
119 pub cancels: Vec<CancelRequestCloid>,
120}
121
122#[derive(Serialize, Deserialize, Debug, Clone)]
123#[serde(rename_all = "camelCase")]
124pub struct ApproveAgent {
125 pub signature_chain_id: U256,
126 pub hyperliquid_chain: String,
127 pub agent_address: H160,
128 pub agent_name: Option<String>,
129 pub nonce: u64,
130}
131
132impl Eip712 for ApproveAgent {
133 type Error = Eip712Error;
134
135 fn domain(&self) -> Result<EIP712Domain, Self::Error> {
136 Ok(eip_712_domain(self.signature_chain_id))
137 }
138
139 fn type_hash() -> Result<[u8; 32], Self::Error> {
140 Ok(eip712::make_type_hash(
141 format!("{HYPERLIQUID_EIP_PREFIX}ApproveAgent"),
142 &[
143 ("hyperliquidChain".to_string(), ParamType::String),
144 ("agentAddress".to_string(), ParamType::Address),
145 ("agentName".to_string(), ParamType::String),
146 ("nonce".to_string(), ParamType::Uint(64)),
147 ],
148 ))
149 }
150
151 fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
152 let Self {
153 signature_chain_id: _,
154 hyperliquid_chain,
155 agent_address,
156 agent_name,
157 nonce,
158 } = self;
159 let items = vec![
160 ethers::abi::Token::Uint(Self::type_hash()?.into()),
161 encode_eip712_type(hyperliquid_chain.clone().into_token()),
162 encode_eip712_type(agent_address.into_token()),
163 encode_eip712_type(agent_name.clone().unwrap_or_default().into_token()),
164 encode_eip712_type(nonce.into_token()),
165 ];
166 Ok(keccak256(encode(&items)))
167 }
168}
169
170#[derive(Serialize, Deserialize, Debug, Clone)]
171#[serde(rename_all = "camelCase")]
172pub struct Withdraw3 {
173 pub hyperliquid_chain: String,
174 pub signature_chain_id: U256,
175 pub amount: String,
176 pub time: u64,
177 pub destination: String,
178}
179
180impl Eip712 for Withdraw3 {
181 type Error = Eip712Error;
182
183 fn domain(&self) -> Result<EIP712Domain, Self::Error> {
184 Ok(eip_712_domain(self.signature_chain_id))
185 }
186
187 fn type_hash() -> Result<[u8; 32], Self::Error> {
188 Ok(eip712::make_type_hash(
189 format!("{HYPERLIQUID_EIP_PREFIX}Withdraw"),
190 &[
191 ("hyperliquidChain".to_string(), ParamType::String),
192 ("destination".to_string(), ParamType::String),
193 ("amount".to_string(), ParamType::String),
194 ("time".to_string(), ParamType::Uint(64)),
195 ],
196 ))
197 }
198
199 fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
200 let Self {
201 signature_chain_id: _,
202 hyperliquid_chain,
203 amount,
204 time,
205 destination,
206 } = self;
207 let items = vec![
208 ethers::abi::Token::Uint(Self::type_hash()?.into()),
209 encode_eip712_type(hyperliquid_chain.clone().into_token()),
210 encode_eip712_type(destination.clone().into_token()),
211 encode_eip712_type(amount.clone().into_token()),
212 encode_eip712_type(time.into_token()),
213 ];
214 Ok(keccak256(encode(&items)))
215 }
216}
217
218#[derive(Serialize, Deserialize, Debug, Clone)]
219#[serde(rename_all = "camelCase")]
220pub struct SpotSend {
221 pub hyperliquid_chain: String,
222 pub signature_chain_id: U256,
223 pub destination: String,
224 pub token: String,
225 pub amount: String,
226 pub time: u64,
227}
228
229impl Eip712 for SpotSend {
230 type Error = Eip712Error;
231
232 fn domain(&self) -> Result<EIP712Domain, Self::Error> {
233 Ok(eip_712_domain(self.signature_chain_id))
234 }
235
236 fn type_hash() -> Result<[u8; 32], Self::Error> {
237 Ok(eip712::make_type_hash(
238 format!("{HYPERLIQUID_EIP_PREFIX}SpotSend"),
239 &[
240 ("hyperliquidChain".to_string(), ParamType::String),
241 ("destination".to_string(), ParamType::String),
242 ("token".to_string(), ParamType::String),
243 ("amount".to_string(), ParamType::String),
244 ("time".to_string(), ParamType::Uint(64)),
245 ],
246 ))
247 }
248
249 fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
250 let Self {
251 signature_chain_id: _,
252 hyperliquid_chain,
253 destination,
254 token,
255 amount,
256 time,
257 } = self;
258 let items = vec![
259 ethers::abi::Token::Uint(Self::type_hash()?.into()),
260 encode_eip712_type(hyperliquid_chain.clone().into_token()),
261 encode_eip712_type(destination.clone().into_token()),
262 encode_eip712_type(token.clone().into_token()),
263 encode_eip712_type(amount.clone().into_token()),
264 encode_eip712_type(time.into_token()),
265 ];
266 Ok(keccak256(encode(&items)))
267 }
268}
269
270#[derive(Serialize, Deserialize, Debug, Clone)]
271#[serde(rename_all = "camelCase")]
272pub struct SpotUser {
273 pub class_transfer: ClassTransfer,
274}
275
276#[derive(Serialize, Deserialize, Debug, Clone)]
277#[serde(rename_all = "camelCase")]
278pub struct ClassTransfer {
279 pub usdc: u64,
280 pub to_perp: bool,
281}
282
283#[derive(Serialize, Deserialize, Debug, Clone)]
284#[serde(rename_all = "camelCase")]
285pub struct VaultTransfer {
286 pub vault_address: H160,
287 pub is_deposit: bool,
288 pub usd: String,
289}
290
291#[derive(Serialize, Deserialize, Debug, Clone)]
292#[serde(rename_all = "camelCase")]
293pub struct SetReferrer {
294 pub code: String,
295}