df_hyperliquid_core/transfer/
actions.rs1use crate::account::signers::ActionKind;
6use crate::client::Network;
7use crate::transfer::amount::Amount;
8use crate::transfer::keeping::Keeping;
9use crate::transfer::plan::Key;
10use crate::utils::Address;
11use crate::utils::sign::{Field, L1Action, Map, NonceKey, UserAction, UserSigned};
12
13pub const SPOT_DEX: &str = "spot";
15pub const PERP_DEX: &str = "";
17
18#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct SendAsset {
22 destination: String,
23 source_dex: String,
24 destination_dex: String,
25 token: String,
26 amount: Amount,
27 from_sub_account: String,
28 nonce: u64,
29}
30
31impl SendAsset {
32 pub const KEY: Key = ActionKind::SendAsset.signers();
34
35 #[must_use]
37 pub fn new(
38 destination: Address,
39 source_dex: &str,
40 destination_dex: &str,
41 token: &str,
42 amount: Amount,
43 nonce: u64,
44 ) -> SendAsset {
45 SendAsset {
46 destination: destination.to_hex(),
47 source_dex: source_dex.to_owned(),
48 destination_dex: destination_dex.to_owned(),
49 token: token.to_owned(),
50 amount,
51 from_sub_account: String::new(),
52 nonce,
53 }
54 }
55
56 #[must_use]
60 pub fn usdc(
61 network: Network,
62 destination: Address,
63 source_dex: &str,
64 destination_dex: &str,
65 amount: Amount,
66 nonce: u64,
67 ) -> SendAsset {
68 SendAsset::new(destination, source_dex, destination_dex, network.usdc().wire, amount, nonce)
69 }
70
71 #[must_use]
73 pub fn from_sub_account(mut self, sub_account: Address) -> SendAsset {
74 self.from_sub_account = sub_account.to_hex();
75 self
76 }
77
78 #[must_use]
79 pub const fn amount(&self) -> &Amount {
80 &self.amount
81 }
82}
83
84impl UserAction for SendAsset {
85 fn user_signed(&self) -> UserSigned<'_> {
86 UserSigned::new("sendAsset", "HyperliquidTransaction:SendAsset", NonceKey::Nonce, self.nonce)
87 .field("destination", Field::String(&self.destination))
88 .field("sourceDex", Field::String(&self.source_dex))
89 .field("destinationDex", Field::String(&self.destination_dex))
90 .field("token", Field::String(&self.token))
91 .field("amount", Field::String(self.amount.as_str()))
92 .field("fromSubAccount", Field::String(&self.from_sub_account))
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct SpotSend {
99 destination: String,
100 token: String,
101 amount: Amount,
102 time: u64,
103}
104
105impl SpotSend {
106 pub const KEY: Key = ActionKind::SpotSend.signers();
108
109 #[must_use]
110 pub fn new(destination: Address, token: &str, amount: Amount, time: u64) -> SpotSend {
111 SpotSend { destination: destination.to_hex(), token: token.to_owned(), amount, time }
112 }
113}
114
115impl UserAction for SpotSend {
116 fn user_signed(&self) -> UserSigned<'_> {
117 UserSigned::new("spotSend", "HyperliquidTransaction:SpotSend", NonceKey::Time, self.time)
118 .field("destination", Field::String(&self.destination))
119 .field("token", Field::String(&self.token))
120 .field("amount", Field::String(self.amount.as_str()))
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct UsdSend {
127 destination: String,
128 amount: Amount,
129 time: u64,
130}
131
132impl UsdSend {
133 pub const KEY: Key = ActionKind::UsdSend.signers();
135
136 #[must_use]
137 pub fn new(destination: Address, amount: Amount, time: u64) -> UsdSend {
138 UsdSend { destination: destination.to_hex(), amount, time }
139 }
140}
141
142impl UserAction for UsdSend {
143 fn user_signed(&self) -> UserSigned<'_> {
144 UserSigned::new("usdSend", "HyperliquidTransaction:UsdSend", NonceKey::Time, self.time)
145 .field("destination", Field::String(&self.destination))
146 .field("amount", Field::String(self.amount.as_str()))
147 }
148}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
152pub struct UsdClassTransfer {
153 amount: Amount,
154 to_perp: bool,
155 nonce: u64,
156}
157
158impl UsdClassTransfer {
159 pub const KEY: Key = ActionKind::UsdClassTransfer.signers();
161
162 #[must_use]
163 pub const fn new(amount: Amount, to_perp: bool, nonce: u64) -> UsdClassTransfer {
164 UsdClassTransfer { amount, to_perp, nonce }
165 }
166}
167
168impl UserAction for UsdClassTransfer {
169 fn user_signed(&self) -> UserSigned<'_> {
170 UserSigned::new("usdClassTransfer", "HyperliquidTransaction:UsdClassTransfer", NonceKey::Nonce, self.nonce)
171 .field("amount", Field::String(self.amount.as_str()))
172 .field("toPerp", Field::Bool(self.to_perp))
173 }
174}
175
176#[derive(Debug, Clone, PartialEq, Eq)]
179pub struct Withdraw3 {
180 destination: String,
181 amount: Amount,
182 time: u64,
183}
184
185impl Withdraw3 {
186 pub const KEY: Key = ActionKind::Withdraw3.signers();
188
189 #[must_use]
190 pub fn new(destination: Address, amount: Amount, time: u64) -> Withdraw3 {
191 Withdraw3 { destination: destination.to_hex(), amount, time }
192 }
193
194 #[must_use]
195 pub const fn amount(&self) -> &Amount {
196 &self.amount
197 }
198}
199
200impl UserAction for Withdraw3 {
201 fn user_signed(&self) -> UserSigned<'_> {
202 UserSigned::new("withdraw3", "HyperliquidTransaction:Withdraw", NonceKey::Time, self.time)
203 .field("destination", Field::String(&self.destination))
204 .field("amount", Field::String(self.amount.as_str()))
205 }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq)]
212pub enum SpotOut {
213 SpotSend(SpotSend),
214 SendAsset(SendAsset),
215}
216
217impl SpotOut {
218 pub const KEY: Key = SpotSend::KEY;
220
221 #[must_use]
223 pub fn new(destination: Address, token: &str, amount: Amount, keeping: Keeping, nonce: u64) -> SpotOut {
224 match keeping {
225 Keeping::Apart => SpotOut::SpotSend(SpotSend::new(destination, token, amount, nonce)),
226 Keeping::Pooled | Keeping::Unknown => {
227 SpotOut::SendAsset(SendAsset::new(destination, SPOT_DEX, SPOT_DEX, token, amount, nonce))
228 }
229 }
230 }
231}
232
233impl UserAction for SpotOut {
234 fn user_signed(&self) -> UserSigned<'_> {
235 match self {
236 SpotOut::SpotSend(send) => send.user_signed(),
237 SpotOut::SendAsset(send) => send.user_signed(),
238 }
239 }
240}
241
242#[derive(Debug, Clone, PartialEq, Eq)]
246pub struct AgentSendAsset {
247 account: Address,
248 source_dex: String,
249 destination_dex: String,
250 token: String,
251 amount: Amount,
252 nonce: u64,
253}
254
255impl AgentSendAsset {
256 pub const KEY: Key = ActionKind::AgentSendAsset.signers();
258
259 #[must_use]
262 pub fn new(
263 account: Address,
264 source_dex: &str,
265 destination_dex: &str,
266 token: &str,
267 amount: Amount,
268 nonce: u64,
269 ) -> AgentSendAsset {
270 AgentSendAsset {
271 account,
272 source_dex: source_dex.to_owned(),
273 destination_dex: destination_dex.to_owned(),
274 token: token.to_owned(),
275 amount,
276 nonce,
277 }
278 }
279
280 #[must_use]
282 pub const fn nonce(&self) -> u64 {
283 self.nonce
284 }
285
286 #[must_use]
287 pub const fn amount(&self) -> &Amount {
288 &self.amount
289 }
290}
291
292impl L1Action for AgentSendAsset {
293 fn action(&self) -> Map {
294 Map::new()
295 .put("type", "agentSendAsset")
296 .put("destination", self.account.to_hex())
297 .put("sourceDex", self.source_dex.clone())
298 .put("destinationDex", self.destination_dex.clone())
299 .put("token", self.token.clone())
300 .put("amount", self.amount.as_str().to_owned())
301 .put("fromSubAccount", "")
302 .put("nonce", self.nonce)
303 }
304}
305
306#[cfg(test)]
307#[path = "tests/actions.rs"]
308mod tests;