1use serde::{Deserialize, Deserializer, Serialize, Serializer};
6
7pub const MAX_COMMISSION_FEE_BPS: u8 = 15;
8pub const MAX_BUILDER_CODE_FEE_BPS: u8 = MAX_COMMISSION_FEE_BPS;
9
10#[repr(u8)]
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "lowercase")]
14pub enum SignatureDomain {
15 Mainnet = 1,
16 Testnet = 2,
17 Devnet = 3,
18}
19
20impl SignatureDomain {
21 pub const fn as_str(self) -> &'static str {
22 match self {
23 Self::Mainnet => "mainnet",
24 Self::Testnet => "testnet",
25 Self::Devnet => "devnet",
26 }
27 }
28}
29
30impl std::fmt::Display for SignatureDomain {
31 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 formatter.write_str(self.as_str())
33 }
34}
35
36impl std::str::FromStr for SignatureDomain {
37 type Err = crate::Error;
38
39 fn from_str(value: &str) -> crate::Result<Self> {
40 match value {
41 "mainnet" => Ok(Self::Mainnet),
42 "testnet" => Ok(Self::Testnet),
43 "devnet" => Ok(Self::Devnet),
44 _ => Err(crate::Error::InvalidSignatureDomain(value.to_string())),
45 }
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub struct Pubkey(pub [u8; 32]);
52
53impl Pubkey {
54 pub fn from_bytes(bytes: [u8; 32]) -> Self {
56 Self(bytes)
57 }
58
59 pub fn from_base58(s: &str) -> crate::Result<Self> {
61 let bytes = bs58::decode(s)
62 .into_vec()
63 .map_err(|e| crate::Error::InvalidBase58(e.to_string()))?;
64 if bytes.len() != 32 {
65 return Err(crate::Error::InvalidKeyLength {
66 expected: 32,
67 got: bytes.len(),
68 });
69 }
70 let mut arr = [0u8; 32];
71 arr.copy_from_slice(&bytes);
72 Ok(Self(arr))
73 }
74
75 pub fn to_base58(&self) -> String {
77 bs58::encode(&self.0).into_string()
78 }
79
80 pub fn as_bytes(&self) -> &[u8; 32] {
82 &self.0
83 }
84}
85
86impl std::fmt::Display for Pubkey {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 write!(f, "{}", self.to_base58())
89 }
90}
91
92impl Serialize for Pubkey {
93 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
94 where
95 S: Serializer,
96 {
97 serializer.serialize_str(&self.to_base58())
98 }
99}
100
101impl<'de> Deserialize<'de> for Pubkey {
102 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
103 where
104 D: Deserializer<'de>,
105 {
106 let s = String::deserialize(deserializer)?;
107 Pubkey::from_base58(&s).map_err(serde::de::Error::custom)
108 }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub struct Hash(pub [u8; 32]);
114
115impl Hash {
116 pub fn from_bytes(bytes: [u8; 32]) -> Self {
118 Self(bytes)
119 }
120
121 pub fn from_base58(s: &str) -> crate::Result<Self> {
123 let bytes = bs58::decode(s)
124 .into_vec()
125 .map_err(|e| crate::Error::InvalidBase58(e.to_string()))?;
126 if bytes.len() != 32 {
127 return Err(crate::Error::InvalidHashLength(bytes.len()));
128 }
129 let mut arr = [0u8; 32];
130 arr.copy_from_slice(&bytes);
131 Ok(Self(arr))
132 }
133
134 pub fn to_base58(&self) -> String {
136 bs58::encode(&self.0).into_string()
137 }
138
139 pub fn as_bytes(&self) -> &[u8; 32] {
141 &self.0
142 }
143
144 pub fn random() -> Self {
146 use rand::Rng;
147 let mut bytes = [0u8; 32];
148 rand::thread_rng().fill(&mut bytes);
149 Self(bytes)
150 }
151
152 #[inline]
154 pub fn from_wincode_bytes(wincode_bytes: &[u8]) -> Self {
155 use sha2::{Digest, Sha256};
156 let hash: [u8; 32] = Sha256::digest(wincode_bytes).into();
157 Self(hash)
158 }
159}
160
161impl std::fmt::Display for Hash {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 write!(f, "{}", self.to_base58())
164 }
165}
166
167impl Serialize for Hash {
168 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
169 where
170 S: Serializer,
171 {
172 serializer.serialize_str(&self.to_base58())
173 }
174}
175
176impl<'de> Deserialize<'de> for Hash {
177 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
178 where
179 D: Deserializer<'de>,
180 {
181 let s = String::deserialize(deserializer)?;
182 Hash::from_base58(&s).map_err(serde::de::Error::custom)
183 }
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
192#[serde(rename_all = "UPPERCASE")]
193pub enum TimeInForce {
194 Gtc,
196 Ioc,
198 Alo,
200}
201
202impl TimeInForce {
203 pub const fn discriminant(&self) -> u32 {
205 match self {
206 Self::Gtc => 0,
207 Self::Ioc => 1,
208 Self::Alo => 2,
209 }
210 }
211}
212
213#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
219#[serde(rename_all = "camelCase")]
220pub enum OrderType {
221 Limit { tif: TimeInForce },
223 Trigger {
225 #[serde(rename = "isMarket")]
226 is_market: bool,
227 #[serde(rename = "triggerPx")]
228 trigger_px: f64,
229 },
230}
231
232impl OrderType {
233 pub const fn limit(tif: TimeInForce) -> Self {
235 Self::Limit { tif }
236 }
237
238 pub const fn market() -> Self {
240 Self::Trigger {
241 is_market: true,
242 trigger_px: 0.0,
243 }
244 }
245
246 pub const fn discriminant(&self) -> u32 {
248 match self {
249 Self::Limit { .. } => 0,
250 Self::Trigger { .. } => 1,
251 }
252 }
253}
254
255#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
261pub struct Order {
262 #[serde(rename = "c")]
264 pub symbol: String,
265 #[serde(rename = "b")]
267 pub is_buy: bool,
268 #[serde(rename = "px")]
270 pub price: f64,
271 #[serde(rename = "sz")]
273 pub size: f64,
274 #[serde(rename = "r")]
276 pub reduce_only: bool,
277 #[serde(rename = "i", default)]
279 pub iso: bool,
280 #[serde(rename = "t")]
282 pub order_type: OrderType,
283 #[serde(rename = "cloid", skip_serializing_if = "Option::is_none")]
285 pub client_id: Option<Hash>,
286 #[serde(default, skip_serializing_if = "Option::is_none")]
290 pub commission: Option<Commission>,
291}
292
293#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
294pub struct Commission {
295 pub to: Pubkey,
296 pub fee: u8,
297}
298
299pub type BuilderCode = Commission;
300
301impl Commission {
302 pub fn new(to: Pubkey, fee: u8) -> crate::Result<Self> {
303 if fee == 0 || fee > MAX_COMMISSION_FEE_BPS {
304 return Err(crate::Error::InvalidOrder(
305 "builder-code fee must be 1..=15 bps".to_string(),
306 ));
307 }
308 Ok(Self { to, fee })
309 }
310}
311
312impl Order {
313 pub fn limit(
315 symbol: impl Into<String>,
316 is_buy: bool,
317 price: f64,
318 size: f64,
319 tif: TimeInForce,
320 ) -> Self {
321 Self {
322 symbol: symbol.into(),
323 is_buy,
324 price,
325 size,
326 reduce_only: false,
327 iso: false,
328 order_type: OrderType::limit(tif),
329 client_id: None,
330 commission: None,
331 }
332 }
333
334 pub fn market(symbol: impl Into<String>, is_buy: bool, size: f64) -> Self {
336 Self {
337 symbol: symbol.into(),
338 is_buy,
339 price: 0.0,
340 size,
341 reduce_only: false,
342 iso: false,
343 order_type: OrderType::market(),
344 client_id: None,
345 commission: None,
346 }
347 }
348
349 pub fn reduce_only(mut self) -> Self {
351 self.reduce_only = true;
352 self
353 }
354
355 pub fn isolated(mut self) -> Self {
357 self.iso = true;
358 self
359 }
360
361 pub fn with_client_id(mut self, client_id: Hash) -> Self {
363 self.client_id = Some(client_id);
364 self
365 }
366
367 pub fn iso(mut self) -> Self {
368 self.iso = true;
369 self
370 }
371
372 pub fn with_commission(mut self, to: Pubkey, fee: u8) -> crate::Result<Self> {
373 self.commission = Some(Commission::new(to, fee)?);
374 Ok(self)
375 }
376
377 pub fn with_builder_code(self, to: Pubkey, fee: u8) -> crate::Result<Self> {
378 self.with_commission(to, fee)
379 }
380
381 pub fn with_random_client_id(mut self) -> Self {
383 self.client_id = Some(Hash::random());
384 self
385 }
386}
387
388#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
394pub struct Cancel {
395 #[serde(rename = "c")]
397 pub symbol: String,
398 #[serde(rename = "oid")]
400 pub order_id: Hash,
401}
402
403impl Cancel {
404 pub fn new(symbol: impl Into<String>, order_id: Hash) -> Self {
406 Self {
407 symbol: symbol.into(),
408 order_id,
409 }
410 }
411}
412
413#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
415pub struct Modify {
416 #[serde(rename = "oid")]
418 pub order_id: Hash,
419 pub symbol: String,
421 pub amount: f64,
423}
424
425impl Modify {
426 pub fn new(order_id: Hash, symbol: impl Into<String>, amount: f64) -> Self {
428 Self {
429 order_id,
430 symbol: symbol.into(),
431 amount,
432 }
433 }
434}
435
436#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
442pub struct CancelAll {
443 #[serde(rename = "c")]
445 pub symbols: Vec<String>,
446}
447
448impl CancelAll {
449 pub fn all() -> Self {
451 Self { symbols: vec![] }
452 }
453
454 pub fn for_symbols(symbols: Vec<String>) -> Self {
456 Self { symbols }
457 }
458}
459
460#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
466pub struct Stop {
467 pub symbol: String,
468 pub is_buy: bool,
470 pub size: f64,
471 pub trigger_price: f64,
472 pub limit_price: f64,
474 #[serde(default)]
476 pub iso: bool,
477}
478
479#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
481pub struct TakeProfit {
482 pub symbol: String,
483 pub is_buy: bool,
485 pub size: f64,
486 pub trigger_price: f64,
487 pub limit_price: f64,
489 #[serde(default)]
491 pub iso: bool,
492}
493
494#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
496pub struct RangeOco {
497 pub symbol: String,
498 pub is_buy: bool,
500 pub size: f64,
501 pub collar_min: f64,
502 pub collar_max: f64,
503 pub limit_min: f64,
505 pub limit_max: f64,
507 #[serde(default)]
509 pub iso: bool,
510}
511
512#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
515pub struct TriggerBasket {
516 pub symbol: String,
517 pub is_buy: bool,
519 pub trigger_price: f64,
520 pub actions: Vec<OrderItem>,
521}
522
523#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
526pub struct TrailingStop {
527 pub symbol: String,
528 pub is_buy: bool,
530 pub size: f64,
531 pub trail_bps: u32,
533 pub step_bps: u32,
535 pub limit_price: Option<f64>,
537 #[serde(default)]
539 pub iso: bool,
540}
541
542#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
545pub struct OnFill {
546 pub trigger: Box<OrderItem>,
548 pub actions: Vec<OrderItem>,
550}
551
552#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
558#[serde(rename_all = "camelCase")]
559pub enum OrderItem {
560 Order(Order),
562 Modify(Modify),
564 Cancel(Cancel),
566 CancelAll(CancelAll),
568 Stop(Stop),
570 TakeProfit(TakeProfit),
572 RangeOco(RangeOco),
574 TriggerBasket(TriggerBasket),
576 OnFill(OnFill),
578 TrailingStop(TrailingStop),
580}
581
582impl OrderItem {
583 pub const fn discriminant(&self) -> u32 {
585 match self {
586 Self::Order(order) => match order.order_type {
587 OrderType::Limit { .. } => 1, OrderType::Trigger { .. } => 0, },
590 Self::Modify(_) => 2, Self::Cancel(_) => 3, Self::CancelAll(_) => 4, Self::Stop(_) => 5, Self::TakeProfit(_) => 6, Self::RangeOco(_) => 7, Self::TriggerBasket(_) => 8, Self::TrailingStop(_) => 9, Self::OnFill(_) => 10, }
600 }
601}
602
603impl From<Order> for OrderItem {
604 fn from(order: Order) -> Self {
605 Self::Order(order)
606 }
607}
608
609impl From<Cancel> for OrderItem {
610 fn from(cancel: Cancel) -> Self {
611 Self::Cancel(cancel)
612 }
613}
614
615impl From<Modify> for OrderItem {
616 fn from(modify: Modify) -> Self {
617 Self::Modify(modify)
618 }
619}
620
621impl From<CancelAll> for OrderItem {
622 fn from(cancel_all: CancelAll) -> Self {
623 Self::CancelAll(cancel_all)
624 }
625}
626
627impl From<Stop> for OrderItem {
628 fn from(stop: Stop) -> Self {
629 Self::Stop(stop)
630 }
631}
632
633impl From<TakeProfit> for OrderItem {
634 fn from(tp: TakeProfit) -> Self {
635 Self::TakeProfit(tp)
636 }
637}
638
639impl From<RangeOco> for OrderItem {
640 fn from(rng: RangeOco) -> Self {
641 Self::RangeOco(rng)
642 }
643}
644
645impl From<TriggerBasket> for OrderItem {
646 fn from(trig: TriggerBasket) -> Self {
647 Self::TriggerBasket(trig)
648 }
649}
650
651impl From<OnFill> for OrderItem {
652 fn from(of: OnFill) -> Self {
653 Self::OnFill(of)
654 }
655}
656
657impl From<TrailingStop> for OrderItem {
658 fn from(trl: TrailingStop) -> Self {
659 Self::TrailingStop(trl)
660 }
661}
662
663#[derive(Debug, Clone, PartialEq)]
669pub struct Faucet {
670 pub user: Pubkey,
672 pub amount: Option<f64>,
674}
675
676impl Faucet {
677 pub fn new(user: Pubkey) -> Self {
679 Self { user, amount: None }
680 }
681
682 pub fn with_amount(user: Pubkey, amount: f64) -> Self {
684 Self {
685 user,
686 amount: Some(amount),
687 }
688 }
689}
690
691#[derive(Debug, Clone, PartialEq)]
697pub struct AgentWallet {
698 pub agent: Pubkey,
700 pub delete: bool,
702}
703
704impl AgentWallet {
705 pub fn add(agent: Pubkey) -> Self {
707 Self {
708 agent,
709 delete: false,
710 }
711 }
712
713 pub fn remove(agent: Pubkey) -> Self {
715 Self {
716 agent,
717 delete: true,
718 }
719 }
720}
721
722#[derive(Debug, Clone, PartialEq)]
723pub struct ApproveCommissionFee {
724 pub to: Pubkey,
725 pub max_fee: u8,
726}
727
728pub type ApproveBuilderCode = ApproveCommissionFee;
729
730#[derive(Debug, Clone, PartialEq)]
731pub struct RevokeCommissionFee {
732 pub to: Pubkey,
733}
734
735pub type RevokeBuilderCode = RevokeCommissionFee;
736
737#[derive(Debug, Clone, PartialEq, Default)]
743pub struct LiquidatorInstrumentConfig {
744 pub symbol: String,
745 pub max_exposure: f64,
746 pub reserve: f64,
747 pub rfactor: f64,
748 pub volume_percent: f64,
749 pub volume_min: f64,
750 pub volume_rampup: u64,
751 pub max_sweep_bps: f64,
752 pub max_adl_notional: f64,
753 pub max_adl_percent: f64,
754}
755
756impl LiquidatorInstrumentConfig {
757 pub fn new(symbol: impl Into<String>) -> Self {
759 Self {
760 symbol: symbol.into(),
761 ..Self::default()
762 }
763 }
764}
765
766#[derive(Debug, Clone, PartialEq, Default)]
768pub struct LiquidatorConfig {
769 pub cross_exposure: f64,
770 pub scoring_skew: f64,
771 pub toxicity: f64,
773 pub urgency_size_fraction: f64,
774 pub sweep_sds: f64,
775 pub instruments: Vec<LiquidatorInstrumentConfig>,
776}
777
778impl LiquidatorConfig {
779 pub fn new(
781 cross_exposure: f64,
782 scoring_skew: f64,
783 toxicity: f64,
784 urgency_size_fraction: f64,
785 sweep_sds: f64,
786 ) -> Self {
787 Self {
788 cross_exposure,
789 scoring_skew,
790 toxicity,
791 urgency_size_fraction,
792 sweep_sds,
793 instruments: Vec::new(),
794 }
795 }
796
797 pub fn with_instrument(mut self, instrument: LiquidatorInstrumentConfig) -> Self {
799 self.instruments.retain(|i| i.symbol != instrument.symbol);
800 self.instruments.push(instrument);
801 self
802 }
803
804 pub fn sorted_instruments(&self) -> Vec<&LiquidatorInstrumentConfig> {
806 let mut sorted: Vec<_> = self.instruments.iter().collect();
807 sorted.sort_by(|a, b| a.symbol.cmp(&b.symbol));
808 sorted
809 }
810}
811
812pub(crate) fn liquidator_config_to_json(config: &LiquidatorConfig) -> serde_json::Value {
814 let instruments: Vec<_> = config
815 .sorted_instruments()
816 .into_iter()
817 .map(|i| {
818 serde_json::json!({
819 "symbol": i.symbol,
820 "max_exposure": i.max_exposure,
821 "reserve": i.reserve,
822 "rfactor": i.rfactor,
823 "volume_percent": i.volume_percent,
824 "volume_min": i.volume_min,
825 "volume_rampup": i.volume_rampup,
826 "max_sweep_bps": i.max_sweep_bps,
827 "max_adl_notional": i.max_adl_notional,
828 "max_adl_percent": i.max_adl_percent,
829 })
830 })
831 .collect();
832
833 serde_json::json!({
834 "cross_exposure": config.cross_exposure,
835 "scoring_skew": config.scoring_skew,
836 "toxicity": config.toxicity,
837 "urgency_size_fraction": config.urgency_size_fraction,
838 "sweep_sds": config.sweep_sds,
839 "instruments": instruments,
840 })
841}
842
843#[derive(Debug, Clone, PartialEq)]
849pub struct UserSettings {
850 pub max_leverage: Vec<(String, f64)>,
852}
853
854impl UserSettings {
855 pub fn new(max_leverage: Vec<(String, f64)>) -> Self {
857 Self { max_leverage }
858 }
859
860 pub fn set_leverage(symbol: impl Into<String>, leverage: f64) -> Self {
862 Self {
863 max_leverage: vec![(symbol.into(), leverage)],
864 }
865 }
866}
867
868#[derive(Debug, Clone, PartialEq)]
874pub struct OraclePrice {
875 pub timestamp: u64,
877 pub asset: String,
879 pub price: f64,
881}
882
883#[derive(Debug, Clone, PartialEq)]
885pub struct PythOraclePrice {
886 pub timestamp: u64,
888 pub feed_index: u64,
890 pub price: u64,
892 pub exponent: i16,
894}
895
896#[derive(Debug, Clone, PartialEq)]
898pub struct WhitelistFaucet {
899 pub target: Pubkey,
901 pub whitelist: bool,
903}
904
905#[derive(Debug, Clone, PartialEq)]
912pub struct CreateSubAccount {
913 pub name: String,
915 pub margin_amount: Option<f64>,
917}
918
919impl CreateSubAccount {
920 pub fn new(name: impl Into<String>) -> Self {
922 Self {
923 name: name.into(),
924 margin_amount: None,
925 }
926 }
927
928 pub fn with_margin(name: impl Into<String>, margin_amount: f64) -> Self {
930 Self {
931 name: name.into(),
932 margin_amount: Some(margin_amount),
933 }
934 }
935}
936
937#[derive(Debug, Clone, PartialEq)]
943pub struct RemoveSubAccount {
944 pub to_remove: Pubkey,
945}
946
947impl RemoveSubAccount {
948 pub fn new(to_remove: Pubkey) -> Self {
949 Self { to_remove }
950 }
951}
952
953#[derive(Debug, Clone, PartialEq)]
959pub struct RenameSubAccount {
960 pub account: Pubkey,
961 pub name: String,
962}
963
964impl RenameSubAccount {
965 pub fn new(account: Pubkey, name: impl Into<String>) -> Self {
966 Self {
967 account,
968 name: name.into(),
969 }
970 }
971}
972
973#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
979pub enum TransferKind {
980 #[default]
982 Internal,
983 External,
985}
986
987#[derive(Debug, Clone, PartialEq)]
989pub struct Transfer {
990 pub kind: TransferKind,
991 pub from: Pubkey,
992 pub to: Pubkey,
993 pub margin_amount: f64,
994}
995
996impl Transfer {
997 pub fn internal(from: Pubkey, to: Pubkey, margin_amount: f64) -> Self {
999 Self {
1000 kind: TransferKind::Internal,
1001 from,
1002 to,
1003 margin_amount,
1004 }
1005 }
1006
1007 pub fn external(from: Pubkey, to: Pubkey, margin_amount: f64) -> Self {
1009 Self {
1010 kind: TransferKind::External,
1011 from,
1012 to,
1013 margin_amount,
1014 }
1015 }
1016}
1017
1018#[derive(Debug, Clone, PartialEq)]
1024pub struct Withdraw {
1025 pub user: Pubkey,
1026 pub vault: Pubkey,
1027 pub recipient_token_account: Pubkey,
1028 pub amount: u64,
1029 pub blockhash: Hash,
1030}
1031
1032#[derive(Debug, Clone, PartialEq)]
1034pub struct WithdrawLockRecover {
1035 pub user: Pubkey,
1036 pub hash: Hash,
1037}
1038
1039#[derive(Debug, Clone, PartialEq)]
1044pub struct CreateMultisig {
1045 pub signers: Vec<Pubkey>,
1046 pub threshold: u32,
1047 pub time_lock_secs: u32,
1048 pub proposal_lifetime_secs: u32,
1049}
1050
1051impl CreateMultisig {
1052 pub fn new(signers: Vec<Pubkey>, threshold: u32) -> Self {
1053 Self {
1054 signers,
1055 threshold,
1056 time_lock_secs: 0,
1057 proposal_lifetime_secs: 7 * 24 * 3600,
1058 }
1059 }
1060}
1061
1062#[derive(Debug, Clone, PartialEq)]
1063pub struct MultisigPropose {
1064 pub multisig: Pubkey,
1065 pub actions: Vec<Action>,
1066 pub proposal_lifetime_secs: Option<u32>,
1067}
1068
1069impl MultisigPropose {
1070 pub fn new(multisig: Pubkey, actions: Vec<Action>) -> Self {
1071 Self {
1072 multisig,
1073 actions,
1074 proposal_lifetime_secs: None,
1075 }
1076 }
1077}
1078
1079#[derive(Debug, Clone, PartialEq)]
1080pub struct MultisigApprove {
1081 pub multisig: Pubkey,
1082 pub proposal_id: u64,
1083}
1084
1085impl MultisigApprove {
1086 pub fn new(multisig: Pubkey, proposal_id: u64) -> Self {
1087 Self {
1088 multisig,
1089 proposal_id,
1090 }
1091 }
1092}
1093
1094#[derive(Debug, Clone, PartialEq)]
1095pub struct MultisigReject {
1096 pub multisig: Pubkey,
1097 pub proposal_id: u64,
1098}
1099
1100impl MultisigReject {
1101 pub fn new(multisig: Pubkey, proposal_id: u64) -> Self {
1102 Self {
1103 multisig,
1104 proposal_id,
1105 }
1106 }
1107}
1108
1109#[derive(Debug, Clone, PartialEq)]
1110pub struct MultisigCancel {
1111 pub multisig: Pubkey,
1112 pub proposal_id: u64,
1113}
1114
1115impl MultisigCancel {
1116 pub fn new(multisig: Pubkey, proposal_id: u64) -> Self {
1117 Self {
1118 multisig,
1119 proposal_id,
1120 }
1121 }
1122}
1123
1124#[derive(Debug, Clone, PartialEq)]
1125pub struct MultisigExecute {
1126 pub multisig: Pubkey,
1127 pub proposal_id: u64,
1128}
1129
1130impl MultisigExecute {
1131 pub fn new(multisig: Pubkey, proposal_id: u64) -> Self {
1132 Self {
1133 multisig,
1134 proposal_id,
1135 }
1136 }
1137}
1138
1139#[derive(Debug, Clone, PartialEq)]
1140pub struct UpdateMultisigPolicy {
1141 pub multisig: Pubkey,
1142 pub signers: Vec<Pubkey>,
1143 pub threshold: u32,
1144 pub time_lock_secs: u32,
1145 pub proposal_lifetime_secs: u32,
1146}
1147
1148impl UpdateMultisigPolicy {
1149 pub fn new(multisig: Pubkey, signers: Vec<Pubkey>, threshold: u32) -> Self {
1150 Self {
1151 multisig,
1152 signers,
1153 threshold,
1154 time_lock_secs: 0,
1155 proposal_lifetime_secs: 7 * 24 * 3600,
1156 }
1157 }
1158}
1159
1160#[derive(Debug, Clone, PartialEq)]
1166pub enum Action {
1167 Order { orders: Vec<OrderItem> },
1169 Oracle { oracles: Vec<OraclePrice> },
1171 PythOracle { oracles: Vec<PythOraclePrice> },
1173 Faucet(Faucet),
1175 UpdateUserSettings(UserSettings),
1177 AgentWalletCreation(AgentWallet),
1179 WhitelistFaucet(WhitelistFaucet),
1181 CreateSubAccount(CreateSubAccount),
1183 RemoveSubAccount(RemoveSubAccount),
1185 RenameSubAccount(RenameSubAccount),
1187 Transfer(Transfer),
1189 Withdraw(Withdraw),
1191 WithdrawLockRecover(WithdrawLockRecover),
1193 CreateMultisig(CreateMultisig),
1195 MultisigPropose(MultisigPropose),
1197 MultisigApprove(MultisigApprove),
1199 MultisigReject(MultisigReject),
1201 MultisigCancel(MultisigCancel),
1203 MultisigExecute(MultisigExecute),
1205 UpdateMultisigPolicy(UpdateMultisigPolicy),
1207 ApproveCommissionFee(ApproveCommissionFee),
1209 RevokeCommissionFee(RevokeCommissionFee),
1211 UpdateLiquidatorConfig(LiquidatorConfig),
1213}
1214
1215impl Action {
1216 pub const fn discriminant(&self) -> u32 {
1218 match self {
1219 Self::Order { .. } => 0, Self::Oracle { .. } => 5, Self::PythOracle { .. } => 6,
1222 Self::Faucet(_) => 7,
1223 Self::UpdateUserSettings(_) => 9,
1224 Self::AgentWalletCreation(_) => 8,
1225 Self::WhitelistFaucet(_) => 10,
1226 Self::ApproveCommissionFee(_) => 40,
1227 Self::RevokeCommissionFee(_) => 41,
1228 Self::CreateSubAccount(_) => 27,
1229 Self::RemoveSubAccount(_) => 28,
1230 Self::Transfer(_) => 29,
1231 Self::CreateMultisig(_) => 30,
1232 Self::MultisigPropose(_) => 31,
1233 Self::MultisigApprove(_) => 32,
1234 Self::MultisigReject(_) => 33,
1235 Self::MultisigCancel(_) => 34,
1236 Self::MultisigExecute(_) => 35,
1237 Self::UpdateMultisigPolicy(_) => 36,
1238 Self::RenameSubAccount(_) => 37,
1239 Self::Withdraw(_) => 45,
1240 Self::WithdrawLockRecover(_) => 54,
1241 Self::UpdateLiquidatorConfig(_) => 43,
1242 }
1243 }
1244
1245 pub const fn type_str(&self) -> &'static str {
1247 match self {
1248 Self::Order { .. } => "order",
1249 Self::Oracle { .. } => "px",
1250 Self::PythOracle { .. } => "o",
1251 Self::Faucet(_) => "faucet",
1252 Self::UpdateUserSettings(_) => "updateUserSettings",
1253 Self::AgentWalletCreation(_) => "agentWalletCreation",
1254 Self::WhitelistFaucet(_) => "whitelistFaucet",
1255 Self::ApproveCommissionFee(_) => "abc",
1256 Self::RevokeCommissionFee(_) => "rbc",
1257 Self::CreateSubAccount(_) => "createSubAccount",
1258 Self::RemoveSubAccount(_) => "removeSubAccount",
1259 Self::Transfer(_) => "transfer",
1260 Self::CreateMultisig(_) => "createMultisig",
1261 Self::MultisigPropose(_) => "msp",
1262 Self::MultisigApprove(_) => "msa",
1263 Self::MultisigReject(_) => "msr",
1264 Self::MultisigCancel(_) => "msc",
1265 Self::MultisigExecute(_) => "mse",
1266 Self::UpdateMultisigPolicy(_) => "msu",
1267 Self::RenameSubAccount(_) => "renameSubAccount",
1268 Self::Withdraw(_) => "withdraw",
1269 Self::WithdrawLockRecover(_) => "withdrawLockRecover",
1270 Self::UpdateLiquidatorConfig(_) => "liq",
1271 }
1272 }
1273}
1274
1275impl From<LiquidatorConfig> for Action {
1276 fn from(config: LiquidatorConfig) -> Self {
1277 Self::UpdateLiquidatorConfig(config)
1278 }
1279}
1280
1281impl From<RenameSubAccount> for Action {
1282 fn from(action: RenameSubAccount) -> Self {
1283 Self::RenameSubAccount(action)
1284 }
1285}
1286
1287impl From<CreateMultisig> for Action {
1288 fn from(action: CreateMultisig) -> Self {
1289 Self::CreateMultisig(action)
1290 }
1291}
1292
1293impl From<MultisigPropose> for Action {
1294 fn from(action: MultisigPropose) -> Self {
1295 Self::MultisigPropose(action)
1296 }
1297}
1298
1299impl From<MultisigApprove> for Action {
1300 fn from(action: MultisigApprove) -> Self {
1301 Self::MultisigApprove(action)
1302 }
1303}
1304
1305impl From<MultisigReject> for Action {
1306 fn from(action: MultisigReject) -> Self {
1307 Self::MultisigReject(action)
1308 }
1309}
1310
1311impl From<MultisigCancel> for Action {
1312 fn from(action: MultisigCancel) -> Self {
1313 Self::MultisigCancel(action)
1314 }
1315}
1316
1317impl From<MultisigExecute> for Action {
1318 fn from(action: MultisigExecute) -> Self {
1319 Self::MultisigExecute(action)
1320 }
1321}
1322
1323impl From<UpdateMultisigPolicy> for Action {
1324 fn from(action: UpdateMultisigPolicy) -> Self {
1325 Self::UpdateMultisigPolicy(action)
1326 }
1327}
1328
1329#[derive(Debug, Clone, Serialize, Deserialize)]
1335pub struct SignedTransaction {
1336 pub actions: Vec<serde_json::Value>,
1338 #[serde(with = "crate::nonce::serde_decimal")]
1340 pub nonce: u64,
1341 pub account: String,
1343 pub signer: String,
1345 pub signature: String,
1347 #[serde(skip_serializing, skip_deserializing, default)]
1350 pub order_id: Option<String>,
1351 #[serde(skip_serializing, skip_deserializing, default)]
1354 pub order_ids: Option<Vec<String>>,
1355}
1356
1357impl SignedTransaction {
1358 pub fn to_json(&self) -> crate::Result<String> {
1360 serde_json::to_string(self).map_err(crate::Error::from)
1361 }
1362
1363 pub fn to_json_bytes(&self) -> crate::Result<Vec<u8>> {
1365 serde_json::to_vec(self).map_err(crate::Error::from)
1366 }
1367}