df_hyperliquid_core/utils/sign/
l1.rs1use crate::account::Signature;
6use crate::client::http::body;
7use crate::client::network::Network;
8use crate::utils::address::Address;
9use crate::utils::sign::eip712::{Domain, Field, TypedStruct};
10use crate::utils::sign::keccak::Keccak256;
11use crate::utils::sign::msgpack::Map;
12
13#[must_use]
16pub fn connection_id(msgpack: &[u8], nonce: u64, vault: Option<Address>) -> [u8; 32] {
17 connection_id_expiring(msgpack, nonce, vault, None)
18}
19
20#[must_use]
24pub fn connection_id_expiring(
25 msgpack: &[u8],
26 nonce: u64,
27 vault: Option<Address>,
28 expires_after: Option<u64>,
29) -> [u8; 32] {
30 let sponge = Keccak256::new().chain(msgpack).chain(&nonce.to_be_bytes());
31 let sponge = match vault {
32 Some(vault) => sponge.chain(&[0x01]).chain(vault.bytes()),
33 None => sponge.chain(&[0x00]),
34 };
35 match expires_after {
36 Some(expires_after) => sponge.chain(&[0x00]).chain(&expires_after.to_be_bytes()),
37 None => sponge,
38 }
39 .finalize()
40}
41
42#[must_use]
44pub fn phantom_agent(connection_id: [u8; 32], network: Network) -> TypedStruct<'static> {
45 TypedStruct::new("Agent")
46 .field("source", Field::String(network.agent_source()))
47 .field("connectionId", Field::Bytes32(connection_id))
48}
49
50#[must_use]
52pub fn l1_digest(msgpack: &[u8], nonce: u64, vault: Option<Address>, network: Network) -> [u8; 32] {
53 phantom_agent(connection_id(msgpack, nonce, vault), network).digest(&Domain::EXCHANGE)
54}
55
56pub trait L1Action {
60 fn action(&self) -> Map;
62
63 #[must_use]
64 fn msgpack(&self) -> Vec<u8> {
65 self.action().to_msgpack()
66 }
67
68 #[must_use]
70 fn wire(&self) -> String {
71 self.action().to_json().compact()
72 }
73
74 #[must_use]
75 fn connection_id(&self, nonce: u64, vault: Option<Address>) -> [u8; 32] {
76 connection_id(&self.msgpack(), nonce, vault)
77 }
78
79 #[must_use]
80 fn typed(&self, nonce: u64, vault: Option<Address>, network: Network) -> TypedStruct<'static> {
81 phantom_agent(self.connection_id(nonce, vault), network)
82 }
83
84 #[must_use]
85 fn digest(&self, nonce: u64, vault: Option<Address>, network: Network) -> [u8; 32] {
86 self.typed(nonce, vault, network).digest(&Domain::EXCHANGE)
87 }
88
89 #[must_use]
91 fn wallet_json(&self, nonce: u64, vault: Option<Address>, network: Network) -> String {
92 self.typed(nonce, vault, network).wallet_json(&Domain::EXCHANGE)
93 }
94
95 #[must_use]
96 fn body(&self, nonce: u64, signature: &Signature, vault: Option<Address>) -> String {
97 body::l1(&self.action(), nonce, signature, vault)
98 }
99}
100
101impl L1Action for Map {
103 fn action(&self) -> Map {
104 self.clone()
105 }
106}
107
108#[cfg(test)]
109#[path = "tests/l1.rs"]
110mod tests;