1use alloy::primitives::B256;
2use serde;
3
4use crate::l1_action;
5use crate::types::requests::{
6 BuilderInfo, CancelRequest, CancelRequestCloid, ModifyRequest, OrderRequest,
7};
8
9#[derive(Debug, Clone, serde::Serialize)]
13#[serde(rename_all = "camelCase")]
14pub struct UsdSend {
15 #[serde(serialize_with = "serialize_chain_id")]
16 pub signature_chain_id: u64,
17 pub hyperliquid_chain: String,
18 pub destination: String,
19 pub amount: String,
20 pub time: u64,
21}
22
23impl crate::types::eip712::HyperliquidAction for UsdSend {
24 const TYPE_STRING: &'static str =
25 "UsdSend(string hyperliquidChain,string destination,string amount,uint64 time)";
26 const USE_PREFIX: bool = true;
27
28 fn chain_id(&self) -> Option<u64> {
29 Some(self.signature_chain_id)
30 }
31
32 fn encode_data(&self) -> Vec<u8> {
33 use crate::types::eip712::encode_value;
34 let mut encoded = Vec::new();
35 encoded.extend_from_slice(&Self::type_hash()[..]);
36 encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
37 encoded.extend_from_slice(&encode_value(&self.destination)[..]);
38 encoded.extend_from_slice(&encode_value(&self.amount)[..]);
39 encoded.extend_from_slice(&encode_value(&self.time)[..]);
40 encoded
41 }
42}
43
44#[derive(Debug, Clone, serde::Serialize)]
46#[serde(rename_all = "camelCase")]
47pub struct Withdraw {
48 #[serde(serialize_with = "serialize_chain_id")]
49 pub signature_chain_id: u64,
50 pub hyperliquid_chain: String,
51 pub destination: String,
52 pub amount: String,
53 pub time: u64,
54}
55
56impl crate::types::eip712::HyperliquidAction for Withdraw {
57 const TYPE_STRING: &'static str =
58 "Withdraw(string hyperliquidChain,string destination,string amount,uint64 time)";
59 const USE_PREFIX: bool = true;
60
61 fn chain_id(&self) -> Option<u64> {
62 Some(self.signature_chain_id)
63 }
64
65 fn encode_data(&self) -> Vec<u8> {
66 use crate::types::eip712::encode_value;
67 let mut encoded = Vec::new();
68 encoded.extend_from_slice(&Self::type_hash()[..]);
69 encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
70 encoded.extend_from_slice(&encode_value(&self.destination)[..]);
71 encoded.extend_from_slice(&encode_value(&self.amount)[..]);
72 encoded.extend_from_slice(&encode_value(&self.time)[..]);
73 encoded
74 }
75}
76
77#[derive(Debug, Clone, serde::Serialize)]
79#[serde(rename_all = "camelCase")]
80pub struct SpotSend {
81 #[serde(serialize_with = "serialize_chain_id")]
82 pub signature_chain_id: u64,
83 pub hyperliquid_chain: String,
84 pub destination: String,
85 pub token: String,
86 pub amount: String,
87 pub time: u64,
88}
89
90impl crate::types::eip712::HyperliquidAction for SpotSend {
91 const TYPE_STRING: &'static str = "SpotSend(string hyperliquidChain,string destination,string token,string amount,uint64 time)";
92 const USE_PREFIX: bool = true;
93
94 fn chain_id(&self) -> Option<u64> {
95 Some(self.signature_chain_id)
96 }
97
98 fn encode_data(&self) -> Vec<u8> {
99 use crate::types::eip712::encode_value;
100 let mut encoded = Vec::new();
101 encoded.extend_from_slice(&Self::type_hash()[..]);
102 encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
103 encoded.extend_from_slice(&encode_value(&self.destination)[..]);
104 encoded.extend_from_slice(&encode_value(&self.token)[..]);
105 encoded.extend_from_slice(&encode_value(&self.amount)[..]);
106 encoded.extend_from_slice(&encode_value(&self.time)[..]);
107 encoded
108 }
109}
110
111#[derive(Debug, Clone, serde::Serialize)]
113#[serde(rename_all = "camelCase")]
114pub struct ApproveAgent {
115 #[serde(serialize_with = "serialize_chain_id")]
116 pub signature_chain_id: u64,
117 pub hyperliquid_chain: String,
118 #[serde(serialize_with = "serialize_address")]
119 pub agent_address: alloy::primitives::Address,
120 pub agent_name: Option<String>,
121 pub nonce: u64,
122}
123
124pub(crate) fn serialize_address<S>(
125 address: &alloy::primitives::Address,
126 serializer: S,
127) -> Result<S::Ok, S::Error>
128where
129 S: serde::Serializer,
130{
131 serializer.serialize_str(&format!("{:#x}", address))
132}
133
134pub(crate) fn serialize_chain_id<S>(
135 chain_id: &u64,
136 serializer: S,
137) -> Result<S::Ok, S::Error>
138where
139 S: serde::Serializer,
140{
141 serializer.serialize_str(&format!("{:#x}", chain_id))
143}
144
145impl crate::types::eip712::HyperliquidAction for ApproveAgent {
146 const TYPE_STRING: &'static str = "ApproveAgent(string hyperliquidChain,address agentAddress,string agentName,uint64 nonce)";
147 const USE_PREFIX: bool = true;
148
149 fn chain_id(&self) -> Option<u64> {
150 Some(self.signature_chain_id)
151 }
152
153 fn encode_data(&self) -> Vec<u8> {
154 use crate::types::eip712::encode_value;
155 let mut encoded = Vec::new();
156 encoded.extend_from_slice(&Self::type_hash()[..]);
157 encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
158 encoded.extend_from_slice(&encode_value(&self.agent_address)[..]);
159 let agent_name = self.agent_name.clone().unwrap_or_default();
161 encoded.extend_from_slice(&encode_value(&agent_name)[..]);
162 encoded.extend_from_slice(&encode_value(&self.nonce)[..]);
163 encoded
164 }
165}
166
167#[derive(Debug, Clone, serde::Serialize)]
169#[serde(rename_all = "camelCase")]
170pub struct ApproveBuilderFee {
171 #[serde(serialize_with = "serialize_chain_id")]
172 pub signature_chain_id: u64,
173 pub hyperliquid_chain: String,
174 pub max_fee_rate: String,
175 pub builder: String,
176 pub nonce: u64,
177}
178
179impl crate::types::eip712::HyperliquidAction for ApproveBuilderFee {
180 const TYPE_STRING: &'static str = "ApproveBuilderFee(string hyperliquidChain,string maxFeeRate,string builder,uint64 nonce)";
181 const USE_PREFIX: bool = true;
182
183 fn chain_id(&self) -> Option<u64> {
184 Some(self.signature_chain_id)
185 }
186
187 fn encode_data(&self) -> Vec<u8> {
188 use crate::types::eip712::encode_value;
189 let mut encoded = Vec::new();
190 encoded.extend_from_slice(&Self::type_hash()[..]);
191 encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
192 encoded.extend_from_slice(&encode_value(&self.max_fee_rate)[..]);
193 encoded.extend_from_slice(&encode_value(&self.builder)[..]);
194 encoded.extend_from_slice(&encode_value(&self.nonce)[..]);
195 encoded
196 }
197}
198
199l1_action! {
202 struct Agent {
204 pub source: String,
205 pub connection_id: B256,
206 }
207 => "Agent(string source,bytes32 connectionId)"
208 => encode(source, connection_id)
209}
210
211#[derive(Debug, Clone, serde::Serialize)]
214#[serde(rename_all = "camelCase")]
215pub struct UpdateLeverage {
216 pub asset: u32,
217 pub is_cross: bool,
218 pub leverage: u32,
219}
220
221#[derive(Debug, Clone, serde::Serialize)]
222#[serde(rename_all = "camelCase")]
223pub struct UpdateIsolatedMargin {
224 pub asset: u32,
225 pub is_buy: bool,
226 pub ntli: i64,
227}
228
229#[derive(Debug, Clone, serde::Serialize)]
230#[serde(rename_all = "camelCase")]
231pub struct VaultTransfer {
232 pub vault_address: String,
233 pub is_deposit: bool,
234 pub usd: u64,
235}
236
237#[derive(Debug, Clone, serde::Serialize)]
238#[serde(rename_all = "camelCase")]
239pub struct SpotUser {
240 pub class_transfer: ClassTransfer,
241}
242
243#[derive(Debug, Clone, serde::Serialize)]
244#[serde(rename_all = "camelCase")]
245pub struct ClassTransfer {
246 pub usd_size: u64,
247 pub to_perp: bool,
248}
249
250#[derive(Debug, Clone, serde::Serialize)]
251#[serde(rename_all = "camelCase")]
252pub struct SetReferrer {
253 pub code: String,
254}
255
256#[derive(Debug, Clone, serde::Serialize)]
259#[serde(rename_all = "camelCase")]
260pub struct BulkOrder {
261 pub orders: Vec<OrderRequest>,
262 pub grouping: String,
263 #[serde(default, skip_serializing_if = "Option::is_none")]
264 pub builder: Option<BuilderInfo>,
265}
266
267#[derive(Debug, Clone, serde::Serialize)]
268#[serde(rename_all = "camelCase")]
269pub struct BulkCancel {
270 pub cancels: Vec<CancelRequest>,
271}
272
273#[derive(Debug, Clone, serde::Serialize)]
274#[serde(rename_all = "camelCase")]
275pub struct BulkModify {
276 pub modifies: Vec<ModifyRequest>,
277}
278
279#[derive(Debug, Clone, serde::Serialize)]
280#[serde(rename_all = "camelCase")]
281pub struct BulkCancelCloid {
282 pub cancels: Vec<CancelRequestCloid>,
283}
284
285#[derive(Debug, Clone, serde::Serialize)]
289#[serde(rename_all = "camelCase")]
290pub struct ScheduleCancel {
291 pub time: Option<u64>,
292}
293
294#[derive(Debug, Clone, serde::Serialize)]
296#[serde(rename_all = "camelCase")]
297pub struct CreateSubAccount {
298 pub name: Option<String>,
299}
300
301#[derive(Debug, Clone, serde::Serialize)]
303#[serde(rename_all = "camelCase")]
304pub struct SubAccountTransfer {
305 pub sub_account_user: String,
306 pub is_deposit: bool,
307 pub usd: u64,
308}
309
310#[derive(Debug, Clone, serde::Serialize)]
312#[serde(rename_all = "camelCase")]
313pub struct SubAccountSpotTransfer {
314 pub sub_account_user: String,
315 pub is_deposit: bool,
316 pub token: String,
317 pub amount: String,
318}
319
320#[derive(Debug, Clone, serde::Serialize)]
322#[serde(rename_all = "camelCase")]
323pub struct UsdClassTransfer {
324 pub amount: String,
325 pub to_perp: bool,
326}
327
328#[derive(Debug, Clone, serde::Serialize)]
332#[serde(rename_all = "camelCase")]
333pub struct TwapOrder {
334 #[serde(rename = "a")]
336 pub asset: u32,
337 #[serde(rename = "b")]
339 pub is_buy: bool,
340 #[serde(rename = "s")]
342 pub sz: String,
343 #[serde(rename = "r")]
345 pub reduce_only: bool,
346 #[serde(rename = "m")]
348 pub duration_minutes: u32,
349 #[serde(rename = "t")]
351 pub randomize: bool,
352}
353
354#[derive(Debug, Clone, serde::Serialize)]
356#[serde(rename_all = "camelCase")]
357pub struct BulkTwapOrder {
358 pub twap: TwapOrder,
359}
360
361#[derive(Debug, Clone, serde::Serialize)]
363#[serde(rename_all = "camelCase")]
364pub struct TwapCancel {
365 #[serde(rename = "a")]
367 pub asset: u32,
368 #[serde(rename = "t")]
370 pub twap_id: u64,
371}
372
373#[derive(Debug, Clone, serde::Serialize)]
375#[serde(rename_all = "camelCase")]
376pub struct ConvertToMultiSigUser {
377 #[serde(serialize_with = "serialize_chain_id")]
378 pub signature_chain_id: u64,
379 pub hyperliquid_chain: String,
380 pub signers: Vec<MultiSigSigner>,
382 pub threshold: u32,
384 pub nonce: u64,
385}
386
387#[derive(Debug, Clone, serde::Serialize)]
389#[serde(rename_all = "camelCase")]
390pub struct MultiSigSigner {
391 pub address: String,
392 pub weight: u32,
393}
394
395impl crate::types::eip712::HyperliquidAction for ConvertToMultiSigUser {
396 const TYPE_STRING: &'static str =
397 "ConvertToMultiSigUser(string hyperliquidChain,address[] authorizedUsers,uint32 threshold,uint64 nonce)";
398 const USE_PREFIX: bool = true;
399
400 fn chain_id(&self) -> Option<u64> {
401 Some(self.signature_chain_id)
402 }
403
404 fn encode_data(&self) -> Vec<u8> {
405 use crate::types::eip712::encode_value;
406 use alloy::primitives::keccak256;
407
408 let mut encoded = Vec::new();
409 encoded.extend_from_slice(&Self::type_hash()[..]);
410 encoded.extend_from_slice(&encode_value(&self.hyperliquid_chain)[..]);
411
412 let mut addresses_encoded = Vec::new();
414 for signer in &self.signers {
415 if let Ok(addr) = signer.address.parse::<alloy::primitives::Address>() {
417 addresses_encoded.extend_from_slice(&encode_value(&addr)[..]);
418 }
419 }
420 let addresses_hash = keccak256(&addresses_encoded);
421 encoded.extend_from_slice(&addresses_hash[..]);
422
423 encoded.extend_from_slice(&encode_value(&(self.threshold as u64))[..]);
424 encoded.extend_from_slice(&encode_value(&self.nonce)[..]);
425 encoded
426 }
427}
428
429#[derive(Debug, Clone, serde::Serialize)]
431#[serde(rename_all = "camelCase")]
432pub struct MultiSig {
433 #[serde(serialize_with = "serialize_chain_id")]
434 pub signature_chain_id: u64,
435 pub multi_sig_user: String,
437 pub outer_signer: String,
439 pub inner_action: serde_json::Value,
441 pub signatures: Vec<MultiSigSignature>,
443 pub nonce: u64,
444}
445
446#[derive(Debug, Clone, serde::Serialize)]
448#[serde(rename_all = "camelCase")]
449pub struct MultiSigSignature {
450 pub r: String,
451 pub s: String,
452 pub v: u8,
453}
454
455#[derive(Debug, Clone, serde::Serialize)]
457#[serde(rename_all = "camelCase")]
458pub struct AgentEnableDexAbstraction {
459 }
461
462#[derive(Debug, Clone, serde::Serialize)]
468#[serde(rename_all = "camelCase")]
469pub struct SpotDeployRegisterToken {
470 pub token_name: String,
472 pub sz_decimals: u32,
474 pub wei_decimals: u32,
476 pub max_gas: String,
478 #[serde(skip_serializing_if = "Option::is_none")]
480 pub full_name: Option<String>,
481}
482
483#[derive(Debug, Clone, serde::Serialize)]
485#[serde(rename_all = "camelCase")]
486pub struct SpotDeployUserGenesis {
487 pub token: String,
489 pub user_and_wei: Vec<(String, String)>,
491 #[serde(skip_serializing_if = "Option::is_none")]
493 pub existing_token_and_wei: Option<(String, String)>,
494}
495
496#[derive(Debug, Clone, serde::Serialize)]
498#[serde(rename_all = "camelCase")]
499pub struct SpotDeployFreezeUser {
500 pub token: String,
502 pub user: String,
504 pub freeze: bool,
506}
507
508#[derive(Debug, Clone, serde::Serialize)]
510#[serde(rename_all = "camelCase")]
511pub struct SpotDeployEnableFreezePrivilege {
512 pub token: String,
514}
515
516#[derive(Debug, Clone, serde::Serialize)]
518#[serde(rename_all = "camelCase")]
519pub struct SpotDeployRevokeFreezePrivilege {
520 pub token: String,
522}
523
524#[derive(Debug, Clone, serde::Serialize)]
526#[serde(rename_all = "camelCase")]
527pub struct SpotDeployEnableQuoteToken {
528 pub token: String,
530}
531
532#[derive(Debug, Clone, serde::Serialize)]
534#[serde(rename_all = "camelCase")]
535pub struct SpotDeployGenesis {
536 pub token: String,
538 pub max_supply: String,
540 #[serde(skip_serializing_if = "Option::is_none")]
542 pub no_hyperliquidity: Option<bool>,
543}
544
545#[derive(Debug, Clone, serde::Serialize)]
547#[serde(rename_all = "camelCase")]
548pub struct SpotDeployRegisterSpot {
549 pub base_token: String,
551 pub quote_token: String,
553}
554
555#[derive(Debug, Clone, serde::Serialize)]
557#[serde(rename_all = "camelCase")]
558pub struct SpotDeployRegisterHyperliquidity {
559 pub spot: String,
561 pub start_px: String,
563 pub order_sz: String,
565 pub n_orders: u32,
567 pub n_seeded_levels: u32,
569}
570
571#[derive(Debug, Clone, serde::Serialize)]
573#[serde(rename_all = "camelCase")]
574pub struct SpotDeploySetDeployerTradingFeeShare {
575 pub token: String,
577 pub share: String,
579}
580
581#[derive(Debug, Clone, serde::Serialize)]
585#[serde(rename_all = "camelCase")]
586pub struct PerpDeployRegisterAsset {
587 pub dex: u32,
589 pub max_gas: String,
591 pub coin: String,
593 pub sz_decimals: u32,
595 pub oracle_px: String,
597 #[serde(skip_serializing_if = "Option::is_none")]
599 pub margin_table_id: Option<u32>,
600 #[serde(skip_serializing_if = "Option::is_none")]
602 pub only_isolated: Option<bool>,
603 #[serde(skip_serializing_if = "Option::is_none")]
605 pub schema: Option<String>,
606}
607
608#[derive(Debug, Clone, serde::Serialize)]
610#[serde(rename_all = "camelCase")]
611pub struct PerpDeploySetOracle {
612 pub dex: u32,
614 pub oracle_pxs: Vec<String>,
616 pub all_mark_pxs: Vec<String>,
618 #[serde(skip_serializing_if = "Option::is_none")]
620 pub external_perp_pxs: Option<Vec<String>>,
621}
622
623#[derive(Debug, Clone, serde::Serialize)]
627#[serde(rename_all = "camelCase")]
628pub struct CSignerUnjailSelf {
629 }
631
632#[derive(Debug, Clone, serde::Serialize)]
634#[serde(rename_all = "camelCase")]
635pub struct CSignerJailSelf {
636 }
638
639#[derive(Debug, Clone, serde::Serialize)]
641#[serde(rename_all = "camelCase")]
642pub struct CValidatorRegister {
643 pub node_ip: String,
645 pub name: String,
647 pub description: String,
649 pub delegations_disabled: bool,
651 pub commission_bps: u32,
653 pub signer: String,
655 pub unjailed: bool,
657 pub initial_wei: String,
659}
660
661#[derive(Debug, Clone, serde::Serialize)]
663#[serde(rename_all = "camelCase")]
664pub struct CValidatorChangeProfile {
665 #[serde(skip_serializing_if = "Option::is_none")]
667 pub node_ip: Option<String>,
668 #[serde(skip_serializing_if = "Option::is_none")]
670 pub name: Option<String>,
671 #[serde(skip_serializing_if = "Option::is_none")]
673 pub description: Option<String>,
674 #[serde(skip_serializing_if = "Option::is_none")]
676 pub unjailed: Option<bool>,
677 #[serde(skip_serializing_if = "Option::is_none")]
679 pub disable_delegations: Option<bool>,
680 #[serde(skip_serializing_if = "Option::is_none")]
682 pub commission_bps: Option<u32>,
683 #[serde(skip_serializing_if = "Option::is_none")]
685 pub signer: Option<String>,
686}
687
688#[derive(Debug, Clone, serde::Serialize)]
690#[serde(rename_all = "camelCase")]
691pub struct CValidatorUnregister {
692 }
694
695#[derive(Debug, Clone, serde::Serialize)]
697#[serde(rename_all = "camelCase")]
698pub struct TokenDelegate {
699 pub validator: String,
701 pub wei: String,
703 pub is_undelegate: bool,
705}
706
707#[derive(Debug, Clone, serde::Serialize)]
711#[serde(rename_all = "camelCase")]
712pub struct UseBigBlocks {
713 pub enable: bool,
715}
716
717#[derive(Debug, Clone, serde::Serialize)]
719#[serde(rename_all = "camelCase")]
720pub struct Noop {
721 pub nonce: u64,
723}
724
725#[cfg(test)]
730mod tests {
731 use alloy::primitives::keccak256;
732
733 use super::*;
734 use crate::types::eip712::HyperliquidAction;
735
736 #[test]
737 fn test_usd_send_type_hash() {
738 let expected = keccak256(
739 "HyperliquidTransaction:UsdSend(string hyperliquidChain,string destination,string amount,uint64 time)",
740 );
741 assert_eq!(UsdSend::type_hash(), expected);
742 }
743
744 #[test]
745 fn test_agent_type_hash() {
746 let expected = keccak256("Agent(string source,bytes32 connectionId)");
748 assert_eq!(Agent::type_hash(), expected);
749 }
750
751 #[test]
752 fn test_agent_domain() {
753 let agent = Agent {
754 source: "a".to_string(),
755 connection_id: B256::default(),
756 };
757
758 let domain = agent.domain();
760 let expected_domain = alloy::sol_types::eip712_domain! {
761 name: "Exchange",
762 version: "1",
763 chain_id: 1337u64,
764 verifying_contract: alloy::primitives::address!("0000000000000000000000000000000000000000"),
765 };
766
767 assert_eq!(domain.separator(), expected_domain.separator());
769 }
770}