df_hyperliquid_core/utils/sign/
user_signed.rs1use crate::account::Signature;
10use crate::client::http::body;
11use crate::client::network::Network;
12use crate::utils::sign::eip712::{Domain, Field, TypedStruct};
13use crate::utils::sign::msgpack::Map;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum NonceKey {
19 Nonce,
20 Time,
21}
22
23impl NonceKey {
24 #[must_use]
25 pub const fn as_str(self) -> &'static str {
26 match self {
27 NonceKey::Nonce => "nonce",
28 NonceKey::Time => "time",
29 }
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct UserSigned<'a> {
36 kind: &'static str,
37 primary_type: &'static str,
38 fields: Vec<(&'static str, Field<'a>)>,
39 nonce_key: NonceKey,
40 nonce: u64,
41}
42
43impl<'a> UserSigned<'a> {
44 #[must_use]
47 pub const fn new(kind: &'static str, primary_type: &'static str, nonce_key: NonceKey, nonce: u64) -> Self {
48 UserSigned { kind, primary_type, fields: Vec::new(), nonce_key, nonce }
49 }
50
51 #[must_use]
53 pub fn field(mut self, name: &'static str, value: Field<'a>) -> Self {
54 self.fields.push((name, value));
55 self
56 }
57
58 #[must_use]
59 pub const fn kind(&self) -> &'static str {
60 self.kind
61 }
62
63 #[must_use]
64 pub const fn nonce(&self) -> u64 {
65 self.nonce
66 }
67
68 #[must_use]
70 pub fn action(&self, network: Network) -> Map {
71 let mut map = Map::new()
72 .put("type", self.kind)
73 .put("signatureChainId", network.signature_chain_id_hex())
74 .put("hyperliquidChain", network.hyperliquid_chain());
75 for (name, value) in &self.fields {
76 map = map.put(*name, value.to_wire());
77 }
78 map.put(self.nonce_key.as_str(), self.nonce)
79 }
80
81 #[must_use]
83 pub fn wire(&self, network: Network) -> String {
84 self.action(network).to_json().compact()
85 }
86
87 #[must_use]
89 pub fn typed(&self, network: Network) -> TypedStruct<'a> {
90 let mut data =
91 TypedStruct::new(self.primary_type).field("hyperliquidChain", Field::String(network.hyperliquid_chain()));
92 for &(name, value) in &self.fields {
93 data = data.field(name, value);
94 }
95 data.field(self.nonce_key.as_str(), Field::Uint64(self.nonce))
96 }
97
98 #[must_use]
99 pub fn digest(&self, network: Network) -> [u8; 32] {
100 self.typed(network).digest(&Domain::user_signed(network))
101 }
102
103 #[must_use]
106 pub fn wallet_json(&self, network: Network) -> String {
107 self.typed(network).wallet_json(&Domain::user_signed(network))
108 }
109
110 #[must_use]
112 pub fn body(&self, signature: &Signature, network: Network) -> String {
113 body::user_signed(&self.action(network), self.nonce, signature)
114 }
115}
116
117pub trait UserAction {
120 fn user_signed(&self) -> UserSigned<'_>;
121
122 #[must_use]
123 fn nonce(&self) -> u64 {
124 self.user_signed().nonce()
125 }
126
127 #[must_use]
128 fn action(&self, network: Network) -> Map {
129 self.user_signed().action(network)
130 }
131
132 #[must_use]
133 fn wire(&self, network: Network) -> String {
134 self.user_signed().wire(network)
135 }
136
137 #[must_use]
138 fn typed(&self, network: Network) -> TypedStruct<'_> {
139 self.user_signed().typed(network)
140 }
141
142 #[must_use]
143 fn digest(&self, network: Network) -> [u8; 32] {
144 self.user_signed().digest(network)
145 }
146
147 #[must_use]
148 fn wallet_json(&self, network: Network) -> String {
149 self.user_signed().wallet_json(network)
150 }
151
152 #[must_use]
153 fn body(&self, signature: &Signature, network: Network) -> String {
154 self.user_signed().body(signature, network)
155 }
156}