1use serde::{Deserialize, Serialize};
2use solana_hash::Hash;
3use solana_keypair::Pubkey;
4use crate::msgs::{AddMarket, AgentWalletCreation, Beacon, CancelAll, CancelOrder, Faucet, Join, LimitOrder, MarketOrder, Matrix, ModifyOrder, OpaqueAction, Price, PythOracle, UpdateUserSettings, WhitelistFaucet};
5use crate::msgs::conditional::{OnFill, Range, StopOrTP, Trailing, Trigger};
6use crate::msgs::multisig::{CreateMultisig, MultisigApprove, MultisigCancel, MultisigExecute, MultisigPropose, MultisigReject, UpdateMultisigPolicy};
7use crate::msgs::subaccounts::{CreateSubAccount, RemoveSubAccount, Transfer};
8
9#[derive(Clone, Copy, Debug, Default)]
11pub struct ActionMeta {
12 pub account: Pubkey,
13 pub nonce: u64,
14 pub seqno: u32,
15 pub hash: Option<Hash>,
16}
17
18#[derive(Clone, Debug, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub enum Action {
21 #[serde(rename = "m")]
23 MarketOrder(MarketOrder),
24 #[serde(rename = "l")]
26 LimitOrder(LimitOrder),
27 #[serde(rename = "mod")]
29 ModifyOrder(ModifyOrder),
30 #[serde(rename = "cx")]
32 Cancel(CancelOrder),
33 #[serde(rename = "cxa")]
35 CancelAll(CancelAll),
36 #[serde(rename = "st")]
38 Stop(StopOrTP),
39 #[serde(rename = "tp")]
41 TakeProfit(StopOrTP),
42 #[serde(rename = "rng")]
44 Range(Range),
45 #[serde(rename = "trig")]
47 Trigger(Trigger),
48 #[serde(rename = "trl")]
50 Trailing(Trailing),
51 #[serde(rename = "of")]
53 OnFill(OnFill),
54
55 #[serde(rename = "px")]
57 Price(Price),
58 #[serde(rename = "corrs")]
60 Corrs(Matrix),
61 #[serde(rename = "o")]
63 PythOracle(PythOracle),
64 #[serde(rename = "beacon")]
66 Beacon(Beacon),
67 #[serde(rename = "join")]
69 Join(Join),
70
71 Faucet(Faucet),
73 AgentWalletCreation(AgentWalletCreation),
75 UpdateUserSettings(UpdateUserSettings),
77
78 WhitelistFaucet(WhitelistFaucet),
80
81 AddMarket(AddMarket),
83 ConfigFairPrice(OpaqueAction),
85 ConfigVolatility(OpaqueAction),
87 ConfigSecurity(OpaqueAction),
89 ConfigRegime(OpaqueAction),
91 ConfigRisk(OpaqueAction),
93 #[serde(rename = "cfgf")]
95 ConfigFeePolicy(OpaqueAction),
96
97 CreateSubAccount(CreateSubAccount),
99 RemoveSubAccount(RemoveSubAccount),
101 Transfer(Transfer),
103 CreateMultisig(CreateMultisig),
105
106 #[serde(rename = "msp")]
108 MultisigPropose(MultisigPropose),
109 #[serde(rename = "msa")]
111 MultisigApprove(MultisigApprove),
112 #[serde(rename = "msr")]
114 MultisigReject(MultisigReject),
115 #[serde(rename = "msc")]
117 MultisigCancel(MultisigCancel),
118 #[serde(rename = "mse")]
120 MultisigExecute(MultisigExecute),
121 #[serde(rename = "msu")]
123 UpdateMultisigPolicy(UpdateMultisigPolicy),
124}
125
126macro_rules! dispatch {
127 ($self:expr, $x:ident => $body:expr) => {
128 match $self {
129 Action::MarketOrder($x) => $body,
130 Action::LimitOrder($x) => $body,
131 Action::ModifyOrder($x) => $body,
132 Action::Cancel($x) => $body,
133 Action::CancelAll($x) => $body,
134 Action::Stop($x) => $body,
135 Action::TakeProfit($x) => $body,
136 Action::Range($x) => $body,
137 Action::Trigger($x) => $body,
138 Action::Trailing($x) => $body,
139 Action::OnFill($x) => $body,
140 Action::Price($x) => $body,
141 Action::Corrs($x) => $body,
142 Action::PythOracle($x) => $body,
143 Action::Beacon($x) => $body,
144 Action::Join($x) => $body,
145
146 Action::Faucet($x) => $body,
147 Action::AgentWalletCreation($x) => $body,
148 Action::UpdateUserSettings($x) => $body,
149 Action::WhitelistFaucet($x) => $body,
150
151 Action::AddMarket($x) => $body,
152 Action::ConfigFairPrice($x) => $body,
153 Action::ConfigVolatility($x) => $body,
154 Action::ConfigSecurity($x) => $body,
155 Action::ConfigRegime($x) => $body,
156 Action::ConfigRisk($x) => $body,
157 Action::ConfigFeePolicy($x) => $body,
158
159 Action::CreateSubAccount($x) => $body,
160 Action::RemoveSubAccount($x) => $body,
161 Action::Transfer($x) => $body,
162
163 Action::CreateMultisig($x) => $body,
164 Action::MultisigPropose($x) => $body,
165 Action::MultisigApprove($x) => $body,
166 Action::MultisigReject($x) => $body,
167 Action::MultisigCancel($x) => $body,
168 Action::MultisigExecute($x) => $body,
169 Action::UpdateMultisigPolicy($x) => $body,
170 }
171 };
172}
173
174impl Action {
175 pub fn account(&self) -> &Pubkey {
177 dispatch!(self, x => &x.meta.account)
178 }
179
180 pub fn nonce(&self) -> u64 {
182 dispatch!(self, x => x.meta.nonce)
183 }
184
185 pub fn seqno(&self) -> u32 {
187 dispatch!(self, x => x.meta.seqno)
188 }
189
190 pub fn hash(&mut self) -> Hash {
192 use sha2::Digest;
193
194 let meta_ptr: *mut ActionMeta = dispatch!(self, x => {
196 if let Some(h) = x.meta.hash {
197 return h;
198 }
199 &mut x.meta as *mut ActionMeta
200 });
201
202 let (seqno, account, nonce) = unsafe {
204 let m = &*meta_ptr;
205 (m.seqno, m.account, m.nonce)
206 };
207
208 let mut hasher = sha2::Sha256::new();
209 hasher.update(&seqno.to_le_bytes());
210 bincode::serialize_into(&mut hasher, &*self).expect("serialization failed");
211 hasher.update(account.as_ref());
213 hasher.update(&nonce.to_le_bytes());
214
215 let hash = Hash::from(Into::<[u8; 32]>::into(hasher.finalize()));
216
217 unsafe {
219 (*meta_ptr).hash = Some(hash);
220 }
221 hash
222 }
223
224 pub fn link(&mut self, meta: ActionMeta) {
226 dispatch!(self, x => {
227 x.meta = meta;
228 })
229 }
230}
231
232
233impl From<MarketOrder> for Action {
234 fn from(o: MarketOrder) -> Self {
235 Action::MarketOrder(o)
236 }
237}
238
239impl From<LimitOrder> for Action {
240 fn from(o: LimitOrder) -> Self {
241 Action::LimitOrder(o)
242 }
243}
244
245impl From<ModifyOrder> for Action {
246 fn from(o: ModifyOrder) -> Self {
247 Action::ModifyOrder(o)
248 }
249}
250
251impl From<CancelAll> for Action {
252 fn from(o: CancelAll) -> Self {
253 Action::CancelAll(o)
254 }
255}
256
257impl From<CancelOrder> for Action {
258 fn from(o: CancelOrder) -> Self {
259 Action::Cancel(o)
260 }
261}
262
263impl From<Price> for Action {
264 fn from(o: Price) -> Self {
265 Action::Price(o)
266 }
267}
268
269impl From<PythOracle> for Action {
270 fn from(o: PythOracle) -> Self {
271 Action::PythOracle(o)
272 }
273}
274
275impl From<Faucet> for Action {
276 fn from(o: Faucet) -> Self {
277 Action::Faucet(o)
278 }
279}
280
281impl From<AgentWalletCreation> for Action {
282 fn from(o: AgentWalletCreation) -> Self {
283 Action::AgentWalletCreation(o)
284 }
285}
286
287impl From<UpdateUserSettings> for Action {
288 fn from(o: UpdateUserSettings) -> Self {
289 Action::UpdateUserSettings(o)
290 }
291}
292
293impl From<WhitelistFaucet> for Action {
294 fn from(o: WhitelistFaucet) -> Self {
295 Action::WhitelistFaucet(o)
296 }
297}
298
299
300
301#[cfg(test)]
302mod tests {
303 use std::sync::Arc;
304 use crate::common::tif::TimeInForce;
305 use super::*;
306
307 #[test]
308 fn test_limit_hash() {
309 let limit = LimitOrder {
310 symbol: Arc::from("BTC-USD"),
311 is_buy: true,
312 price: 100000.0,
313 size: 1.0,
314 tif: TimeInForce::ALO,
315 reduce_only: false,
316 iso: false,
317 meta: ActionMeta {
318 account: Default::default(),
319 nonce: 1_776_128_000_000_000_000,
320 seqno: 0,
321 hash: None,
322 },
323 };
324
325 let mut action = Action::LimitOrder(limit);
326 let hash = action.hash();
327
328 assert_eq!(
329 hash.to_string(),
330 "9BreqftLa7ZAsYLkvJDRBRxiSukzGoTfbQNMBWWkUAUJ"
331 );
332 }
333}