Skip to main content

hypercall_sdk_types/directives/
registry.rs

1use std::fmt;
2use std::str::FromStr;
3
4use super::{
5    HC_ACTION_ID_TRANSFER_OPTION, HC_ACTION_ID_UPDATE_API_WALLET, HC_ACTION_VERSION,
6    HL_ACTION_ID_CANCEL_BY_CLOID, HL_ACTION_ID_CANCEL_BY_OID, HL_ACTION_ID_LIMIT_ORDER,
7    HL_ACTION_ID_SEND_ASSET, HL_ACTION_VERSION, SYSTEM_ACTION_ID_CREDIT_OPTION,
8    SYSTEM_ACTION_ID_CREDIT_TOKEN, SYSTEM_ACTION_ID_START_LIQUIDATION,
9    SYSTEM_ACTION_ID_STOP_LIQUIDATION, SYSTEM_ACTION_ID_WITHDRAW_TOKEN, SYSTEM_ACTION_VERSION,
10};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum SignerType {
14    ApiWallet,
15    Manager,
16    Rsm,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum DomainKind {
21    Api,
22    Manager,
23    Rsm,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
27#[non_exhaustive]
28pub enum ActionKey {
29    HlLimitOrder,
30    HlCancelByOid,
31    HlCancelByCloid,
32    HcUpdateApiWallet,
33    HlSendAsset,
34    HcTransferOption,
35    RsmHlLimitOrder,
36    RsmHlCancelByOid,
37    RsmHlCancelByCloid,
38    RsmHlSendAsset,
39    SystemCreditToken,
40    SystemCreditOption,
41    SystemStartLiquidation,
42    SystemStopLiquidation,
43    SystemWithdrawToken,
44}
45
46#[derive(Debug, Clone, Copy)]
47pub struct ActionSpec {
48    pub action_key: &'static str,
49    pub version: u8,
50    pub id: u32,
51    pub primary_type: &'static str,
52    pub signer_type: SignerType,
53    pub domain: DomainKind,
54    pub supported: bool,
55    pub typed_data: Option<TypedDataSpec>,
56}
57
58#[derive(Debug, Clone, Copy)]
59pub struct TypeFieldSpec {
60    pub name: &'static str,
61    pub kind: &'static str,
62}
63
64#[derive(Debug, Clone, Copy)]
65pub struct TypedDataSpec {
66    pub inner_type: &'static str,
67    pub inner_fields: &'static [TypeFieldSpec],
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct ParseActionKeyError {
72    value: String,
73}
74
75impl ParseActionKeyError {
76    pub fn new(value: impl Into<String>) -> Self {
77        Self {
78            value: value.into(),
79        }
80    }
81
82    pub fn value(&self) -> &str {
83        &self.value
84    }
85}
86
87impl fmt::Display for ParseActionKeyError {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        write!(f, "Unsupported action key: {}", self.value)
90    }
91}
92
93impl std::error::Error for ParseActionKeyError {}
94
95const HL_LIMIT_ORDER_FIELDS: &[TypeFieldSpec] = &[
96    TypeFieldSpec {
97        name: "asset",
98        kind: "uint32",
99    },
100    TypeFieldSpec {
101        name: "isBuy",
102        kind: "bool",
103    },
104    TypeFieldSpec {
105        name: "limitPx",
106        kind: "uint64",
107    },
108    TypeFieldSpec {
109        name: "sz",
110        kind: "uint64",
111    },
112    TypeFieldSpec {
113        name: "reduceOnly",
114        kind: "bool",
115    },
116    TypeFieldSpec {
117        name: "encodedTif",
118        kind: "uint8",
119    },
120    TypeFieldSpec {
121        name: "cloid",
122        kind: "uint128",
123    },
124];
125
126const HL_CANCEL_BY_OID_FIELDS: &[TypeFieldSpec] = &[
127    TypeFieldSpec {
128        name: "asset",
129        kind: "uint32",
130    },
131    TypeFieldSpec {
132        name: "oid",
133        kind: "uint64",
134    },
135];
136
137const HL_CANCEL_BY_CLOID_FIELDS: &[TypeFieldSpec] = &[
138    TypeFieldSpec {
139        name: "asset",
140        kind: "uint32",
141    },
142    TypeFieldSpec {
143        name: "cloid",
144        kind: "uint128",
145    },
146];
147
148const HL_SEND_ASSET_FIELDS: &[TypeFieldSpec] = &[
149    TypeFieldSpec {
150        name: "destination",
151        kind: "address",
152    },
153    TypeFieldSpec {
154        name: "subAccount",
155        kind: "address",
156    },
157    TypeFieldSpec {
158        name: "srcDex",
159        kind: "uint32",
160    },
161    TypeFieldSpec {
162        name: "dstDex",
163        kind: "uint32",
164    },
165    TypeFieldSpec {
166        name: "token",
167        kind: "uint64",
168    },
169    TypeFieldSpec {
170        name: "amountWei",
171        kind: "uint64",
172    },
173];
174
175const HC_UPDATE_API_WALLET_FIELDS: &[TypeFieldSpec] = &[
176    TypeFieldSpec {
177        name: "name",
178        kind: "bytes32",
179    },
180    TypeFieldSpec {
181        name: "addr",
182        kind: "address",
183    },
184];
185
186const SYSTEM_CREDIT_TOKEN_FIELDS: &[TypeFieldSpec] = &[
187    TypeFieldSpec {
188        name: "srcDex",
189        kind: "uint32",
190    },
191    TypeFieldSpec {
192        name: "dstDex",
193        kind: "uint32",
194    },
195    TypeFieldSpec {
196        name: "token",
197        kind: "uint64",
198    },
199    TypeFieldSpec {
200        name: "amountWei",
201        kind: "uint64",
202    },
203];
204
205const SYSTEM_CREDIT_OPTION_FIELDS: &[TypeFieldSpec] = &[
206    TypeFieldSpec {
207        name: "underlying",
208        kind: "bytes32",
209    },
210    TypeFieldSpec {
211        name: "expiry",
212        kind: "uint256",
213    },
214    TypeFieldSpec {
215        name: "strike",
216        kind: "uint256",
217    },
218    TypeFieldSpec {
219        name: "isCall",
220        kind: "bool",
221    },
222    TypeFieldSpec {
223        name: "amountWei",
224        kind: "uint256",
225    },
226];
227
228const SYSTEM_START_LIQUIDATION_FIELDS: &[TypeFieldSpec] = &[
229    TypeFieldSpec {
230        name: "equity",
231        kind: "int256",
232    },
233    TypeFieldSpec {
234        name: "marginNeeded",
235        kind: "uint256",
236    },
237];
238
239const SYSTEM_STOP_LIQUIDATION_FIELDS: &[TypeFieldSpec] = &[TypeFieldSpec {
240    name: "startTime",
241    kind: "uint256",
242}];
243
244const SYSTEM_WITHDRAW_TOKEN_FIELDS: &[TypeFieldSpec] = &[
245    TypeFieldSpec {
246        name: "destination",
247        kind: "address",
248    },
249    TypeFieldSpec {
250        name: "subAccount",
251        kind: "address",
252    },
253    TypeFieldSpec {
254        name: "srcDex",
255        kind: "uint32",
256    },
257    TypeFieldSpec {
258        name: "dstDex",
259        kind: "uint32",
260    },
261    TypeFieldSpec {
262        name: "token",
263        kind: "uint64",
264    },
265    TypeFieldSpec {
266        name: "amountWei",
267        kind: "uint64",
268    },
269];
270
271const HL_LIMIT_ORDER_TYPED_DATA: TypedDataSpec = TypedDataSpec {
272    inner_type: "LimitOrder",
273    inner_fields: HL_LIMIT_ORDER_FIELDS,
274};
275
276const HL_CANCEL_BY_OID_TYPED_DATA: TypedDataSpec = TypedDataSpec {
277    inner_type: "CancelOrderByOid",
278    inner_fields: HL_CANCEL_BY_OID_FIELDS,
279};
280
281const HL_CANCEL_BY_CLOID_TYPED_DATA: TypedDataSpec = TypedDataSpec {
282    inner_type: "CancelOrderByCloid",
283    inner_fields: HL_CANCEL_BY_CLOID_FIELDS,
284};
285
286const HL_SEND_ASSET_TYPED_DATA: TypedDataSpec = TypedDataSpec {
287    inner_type: "SendAsset",
288    inner_fields: HL_SEND_ASSET_FIELDS,
289};
290
291const HC_UPDATE_API_WALLET_TYPED_DATA: TypedDataSpec = TypedDataSpec {
292    inner_type: "UpdateApiWallet",
293    inner_fields: HC_UPDATE_API_WALLET_FIELDS,
294};
295
296const SYSTEM_CREDIT_TOKEN_TYPED_DATA: TypedDataSpec = TypedDataSpec {
297    inner_type: "CreditToken",
298    inner_fields: SYSTEM_CREDIT_TOKEN_FIELDS,
299};
300
301const SYSTEM_CREDIT_OPTION_TYPED_DATA: TypedDataSpec = TypedDataSpec {
302    inner_type: "CreditOption",
303    inner_fields: SYSTEM_CREDIT_OPTION_FIELDS,
304};
305
306const SYSTEM_START_LIQUIDATION_TYPED_DATA: TypedDataSpec = TypedDataSpec {
307    inner_type: "StartLiquidation",
308    inner_fields: SYSTEM_START_LIQUIDATION_FIELDS,
309};
310
311const SYSTEM_STOP_LIQUIDATION_TYPED_DATA: TypedDataSpec = TypedDataSpec {
312    inner_type: "StopLiquidation",
313    inner_fields: SYSTEM_STOP_LIQUIDATION_FIELDS,
314};
315
316const SYSTEM_WITHDRAW_TOKEN_TYPED_DATA: TypedDataSpec = TypedDataSpec {
317    inner_type: "WithdrawToken",
318    inner_fields: SYSTEM_WITHDRAW_TOKEN_FIELDS,
319};
320
321const HL_LIMIT_ORDER_SPEC: ActionSpec = ActionSpec {
322    action_key: "hl_limit_order",
323    version: HL_ACTION_VERSION,
324    id: HL_ACTION_ID_LIMIT_ORDER,
325    primary_type: "HLOrder",
326    signer_type: SignerType::ApiWallet,
327    domain: DomainKind::Api,
328    supported: true,
329    typed_data: Some(HL_LIMIT_ORDER_TYPED_DATA),
330};
331
332const HL_CANCEL_BY_OID_SPEC: ActionSpec = ActionSpec {
333    action_key: "hl_cancel_by_oid",
334    version: HL_ACTION_VERSION,
335    id: HL_ACTION_ID_CANCEL_BY_OID,
336    primary_type: "HLCancel",
337    signer_type: SignerType::ApiWallet,
338    domain: DomainKind::Api,
339    supported: true,
340    typed_data: Some(HL_CANCEL_BY_OID_TYPED_DATA),
341};
342
343const HL_CANCEL_BY_CLOID_SPEC: ActionSpec = ActionSpec {
344    action_key: "hl_cancel_by_cloid",
345    version: HL_ACTION_VERSION,
346    id: HL_ACTION_ID_CANCEL_BY_CLOID,
347    primary_type: "HLCancelByCloid",
348    signer_type: SignerType::ApiWallet,
349    domain: DomainKind::Api,
350    supported: true,
351    typed_data: Some(HL_CANCEL_BY_CLOID_TYPED_DATA),
352};
353
354const HC_UPDATE_API_WALLET_SPEC: ActionSpec = ActionSpec {
355    action_key: "hc_update_api_wallet",
356    version: HC_ACTION_VERSION,
357    id: HC_ACTION_ID_UPDATE_API_WALLET,
358    primary_type: "HCUpdateApiWallet",
359    signer_type: SignerType::Manager,
360    domain: DomainKind::Manager,
361    supported: true,
362    typed_data: Some(HC_UPDATE_API_WALLET_TYPED_DATA),
363};
364
365const HL_SEND_ASSET_SPEC: ActionSpec = ActionSpec {
366    action_key: "hl_send_asset",
367    version: HL_ACTION_VERSION,
368    id: HL_ACTION_ID_SEND_ASSET,
369    primary_type: "HLSendAsset",
370    signer_type: SignerType::Manager,
371    domain: DomainKind::Manager,
372    supported: false,
373    typed_data: Some(HL_SEND_ASSET_TYPED_DATA),
374};
375
376const HC_TRANSFER_OPTION_SPEC: ActionSpec = ActionSpec {
377    action_key: "hc_transfer_option",
378    version: HC_ACTION_VERSION,
379    id: HC_ACTION_ID_TRANSFER_OPTION,
380    primary_type: "HCTransferOption",
381    signer_type: SignerType::Manager,
382    domain: DomainKind::Manager,
383    supported: false,
384    typed_data: None,
385};
386
387const RSM_HL_LIMIT_ORDER_SPEC: ActionSpec = ActionSpec {
388    action_key: "rsm_hl_limit_order",
389    version: HL_ACTION_VERSION,
390    id: HL_ACTION_ID_LIMIT_ORDER,
391    primary_type: "HLOrder",
392    signer_type: SignerType::Rsm,
393    domain: DomainKind::Rsm,
394    supported: false,
395    typed_data: Some(HL_LIMIT_ORDER_TYPED_DATA),
396};
397
398const RSM_HL_CANCEL_BY_OID_SPEC: ActionSpec = ActionSpec {
399    action_key: "rsm_hl_cancel_by_oid",
400    version: HL_ACTION_VERSION,
401    id: HL_ACTION_ID_CANCEL_BY_OID,
402    primary_type: "HLCancel",
403    signer_type: SignerType::Rsm,
404    domain: DomainKind::Rsm,
405    supported: false,
406    typed_data: Some(HL_CANCEL_BY_OID_TYPED_DATA),
407};
408
409const RSM_HL_CANCEL_BY_CLOID_SPEC: ActionSpec = ActionSpec {
410    action_key: "rsm_hl_cancel_by_cloid",
411    version: HL_ACTION_VERSION,
412    id: HL_ACTION_ID_CANCEL_BY_CLOID,
413    primary_type: "HLCancelByCloid",
414    signer_type: SignerType::Rsm,
415    domain: DomainKind::Rsm,
416    supported: false,
417    typed_data: Some(HL_CANCEL_BY_CLOID_TYPED_DATA),
418};
419
420const RSM_HL_SEND_ASSET_SPEC: ActionSpec = ActionSpec {
421    action_key: "rsm_hl_send_asset",
422    version: HL_ACTION_VERSION,
423    id: HL_ACTION_ID_SEND_ASSET,
424    primary_type: "HLSendAsset",
425    signer_type: SignerType::Rsm,
426    domain: DomainKind::Rsm,
427    supported: false,
428    typed_data: Some(HL_SEND_ASSET_TYPED_DATA),
429};
430
431const SYSTEM_CREDIT_TOKEN_SPEC: ActionSpec = ActionSpec {
432    action_key: "system_credit_token",
433    version: SYSTEM_ACTION_VERSION,
434    id: SYSTEM_ACTION_ID_CREDIT_TOKEN,
435    primary_type: "SystemCreditToken",
436    signer_type: SignerType::Rsm,
437    domain: DomainKind::Rsm,
438    supported: false,
439    typed_data: Some(SYSTEM_CREDIT_TOKEN_TYPED_DATA),
440};
441
442const SYSTEM_CREDIT_OPTION_SPEC: ActionSpec = ActionSpec {
443    action_key: "system_credit_option",
444    version: SYSTEM_ACTION_VERSION,
445    id: SYSTEM_ACTION_ID_CREDIT_OPTION,
446    primary_type: "SystemCreditOption",
447    signer_type: SignerType::Rsm,
448    domain: DomainKind::Rsm,
449    supported: false,
450    typed_data: Some(SYSTEM_CREDIT_OPTION_TYPED_DATA),
451};
452
453const SYSTEM_START_LIQUIDATION_SPEC: ActionSpec = ActionSpec {
454    action_key: "system_start_liquidation",
455    version: SYSTEM_ACTION_VERSION,
456    id: SYSTEM_ACTION_ID_START_LIQUIDATION,
457    primary_type: "SystemStartLiquidation",
458    signer_type: SignerType::Rsm,
459    domain: DomainKind::Rsm,
460    supported: false,
461    typed_data: Some(SYSTEM_START_LIQUIDATION_TYPED_DATA),
462};
463
464const SYSTEM_STOP_LIQUIDATION_SPEC: ActionSpec = ActionSpec {
465    action_key: "system_stop_liquidation",
466    version: SYSTEM_ACTION_VERSION,
467    id: SYSTEM_ACTION_ID_STOP_LIQUIDATION,
468    primary_type: "SystemStopLiquidation",
469    signer_type: SignerType::Rsm,
470    domain: DomainKind::Rsm,
471    supported: false,
472    typed_data: Some(SYSTEM_STOP_LIQUIDATION_TYPED_DATA),
473};
474
475const SYSTEM_WITHDRAW_TOKEN_SPEC: ActionSpec = ActionSpec {
476    action_key: "system_withdraw_token",
477    version: SYSTEM_ACTION_VERSION,
478    id: SYSTEM_ACTION_ID_WITHDRAW_TOKEN,
479    primary_type: "SystemWithdrawToken",
480    signer_type: SignerType::Rsm,
481    domain: DomainKind::Rsm,
482    supported: false,
483    typed_data: Some(SYSTEM_WITHDRAW_TOKEN_TYPED_DATA),
484};
485
486impl ActionKey {
487    pub fn parse(value: &str) -> Result<Self, ParseActionKeyError> {
488        match value {
489            "hl_limit_order" => Ok(Self::HlLimitOrder),
490            "hl_cancel_by_oid" => Ok(Self::HlCancelByOid),
491            "hl_cancel_by_cloid" => Ok(Self::HlCancelByCloid),
492            "hc_update_api_wallet" => Ok(Self::HcUpdateApiWallet),
493            "hl_send_asset" => Ok(Self::HlSendAsset),
494            "hc_transfer_option" => Ok(Self::HcTransferOption),
495            "rsm_hl_limit_order" => Ok(Self::RsmHlLimitOrder),
496            "rsm_hl_cancel_by_oid" => Ok(Self::RsmHlCancelByOid),
497            "rsm_hl_cancel_by_cloid" => Ok(Self::RsmHlCancelByCloid),
498            "rsm_hl_send_asset" => Ok(Self::RsmHlSendAsset),
499            "system_credit_token" => Ok(Self::SystemCreditToken),
500            "system_credit_option" => Ok(Self::SystemCreditOption),
501            "system_start_liquidation" => Ok(Self::SystemStartLiquidation),
502            "system_stop_liquidation" => Ok(Self::SystemStopLiquidation),
503            "system_withdraw_token" => Ok(Self::SystemWithdrawToken),
504            _ => Err(ParseActionKeyError::new(value)),
505        }
506    }
507
508    pub const fn as_str(self) -> &'static str {
509        self.spec().action_key
510    }
511
512    pub const fn spec(self) -> &'static ActionSpec {
513        match self {
514            Self::HlLimitOrder => &HL_LIMIT_ORDER_SPEC,
515            Self::HlCancelByOid => &HL_CANCEL_BY_OID_SPEC,
516            Self::HlCancelByCloid => &HL_CANCEL_BY_CLOID_SPEC,
517            Self::HcUpdateApiWallet => &HC_UPDATE_API_WALLET_SPEC,
518            Self::HlSendAsset => &HL_SEND_ASSET_SPEC,
519            Self::HcTransferOption => &HC_TRANSFER_OPTION_SPEC,
520            Self::RsmHlLimitOrder => &RSM_HL_LIMIT_ORDER_SPEC,
521            Self::RsmHlCancelByOid => &RSM_HL_CANCEL_BY_OID_SPEC,
522            Self::RsmHlCancelByCloid => &RSM_HL_CANCEL_BY_CLOID_SPEC,
523            Self::RsmHlSendAsset => &RSM_HL_SEND_ASSET_SPEC,
524            Self::SystemCreditToken => &SYSTEM_CREDIT_TOKEN_SPEC,
525            Self::SystemCreditOption => &SYSTEM_CREDIT_OPTION_SPEC,
526            Self::SystemStartLiquidation => &SYSTEM_START_LIQUIDATION_SPEC,
527            Self::SystemStopLiquidation => &SYSTEM_STOP_LIQUIDATION_SPEC,
528            Self::SystemWithdrawToken => &SYSTEM_WITHDRAW_TOKEN_SPEC,
529        }
530    }
531
532    pub const fn from_rsm_action_parts(version: u8, id: u32) -> Option<Self> {
533        match (version, id) {
534            (SYSTEM_ACTION_VERSION, SYSTEM_ACTION_ID_CREDIT_TOKEN) => Some(Self::SystemCreditToken),
535            (SYSTEM_ACTION_VERSION, SYSTEM_ACTION_ID_CREDIT_OPTION) => {
536                Some(Self::SystemCreditOption)
537            }
538            (SYSTEM_ACTION_VERSION, SYSTEM_ACTION_ID_START_LIQUIDATION) => {
539                Some(Self::SystemStartLiquidation)
540            }
541            (SYSTEM_ACTION_VERSION, SYSTEM_ACTION_ID_STOP_LIQUIDATION) => {
542                Some(Self::SystemStopLiquidation)
543            }
544            (SYSTEM_ACTION_VERSION, SYSTEM_ACTION_ID_WITHDRAW_TOKEN) => {
545                Some(Self::SystemWithdrawToken)
546            }
547            (HL_ACTION_VERSION, HL_ACTION_ID_LIMIT_ORDER) => Some(Self::RsmHlLimitOrder),
548            (HL_ACTION_VERSION, HL_ACTION_ID_CANCEL_BY_OID) => Some(Self::RsmHlCancelByOid),
549            (HL_ACTION_VERSION, HL_ACTION_ID_CANCEL_BY_CLOID) => Some(Self::RsmHlCancelByCloid),
550            (HL_ACTION_VERSION, HL_ACTION_ID_SEND_ASSET) => Some(Self::RsmHlSendAsset),
551            _ => None,
552        }
553    }
554}
555
556impl FromStr for ActionKey {
557    type Err = ParseActionKeyError;
558
559    fn from_str(s: &str) -> Result<Self, Self::Err> {
560        Self::parse(s)
561    }
562}
563
564impl fmt::Display for ActionKey {
565    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
566        write!(f, "{}", self.as_str())
567    }
568}
569
570#[cfg(test)]
571mod tests {
572    use super::ActionKey;
573    use crate::directives::{
574        HL_ACTION_ID_CANCEL_BY_CLOID, HL_ACTION_ID_CANCEL_BY_OID, HL_ACTION_ID_LIMIT_ORDER,
575        HL_ACTION_ID_SEND_ASSET, HL_ACTION_VERSION, SYSTEM_ACTION_ID_CREDIT_OPTION,
576        SYSTEM_ACTION_ID_CREDIT_TOKEN, SYSTEM_ACTION_ID_START_LIQUIDATION,
577        SYSTEM_ACTION_ID_STOP_LIQUIDATION, SYSTEM_ACTION_ID_WITHDRAW_TOKEN, SYSTEM_ACTION_VERSION,
578    };
579    use std::str::FromStr;
580
581    #[test]
582    fn action_key_round_trip_string_mappings() {
583        let cases = [
584            (ActionKey::HlLimitOrder, "hl_limit_order"),
585            (ActionKey::HlCancelByOid, "hl_cancel_by_oid"),
586            (ActionKey::HlCancelByCloid, "hl_cancel_by_cloid"),
587            (ActionKey::HcUpdateApiWallet, "hc_update_api_wallet"),
588            (ActionKey::HlSendAsset, "hl_send_asset"),
589            (ActionKey::HcTransferOption, "hc_transfer_option"),
590            (ActionKey::RsmHlLimitOrder, "rsm_hl_limit_order"),
591            (ActionKey::RsmHlCancelByOid, "rsm_hl_cancel_by_oid"),
592            (ActionKey::RsmHlCancelByCloid, "rsm_hl_cancel_by_cloid"),
593            (ActionKey::RsmHlSendAsset, "rsm_hl_send_asset"),
594            (ActionKey::SystemCreditToken, "system_credit_token"),
595            (ActionKey::SystemCreditOption, "system_credit_option"),
596            (
597                ActionKey::SystemStartLiquidation,
598                "system_start_liquidation",
599            ),
600            (ActionKey::SystemStopLiquidation, "system_stop_liquidation"),
601            (ActionKey::SystemWithdrawToken, "system_withdraw_token"),
602        ];
603
604        for (action_key, expected) in cases {
605            assert_eq!(action_key.as_str(), expected);
606            assert_eq!(action_key.to_string(), expected);
607            assert_eq!(ActionKey::from_str(expected).unwrap(), action_key);
608        }
609    }
610
611    #[test]
612    fn parse_rejects_unknown_action_key() {
613        let err = ActionKey::from_str("not_real").unwrap_err();
614        assert_eq!(err.to_string(), "Unsupported action key: not_real");
615    }
616
617    #[test]
618    fn action_key_serde_preserves_persisted_variant_names() {
619        assert_eq!(
620            serde_json::to_string(&ActionKey::HlLimitOrder).unwrap(),
621            "\"HlLimitOrder\""
622        );
623        assert_eq!(
624            serde_json::from_str::<ActionKey>("\"HlLimitOrder\"").unwrap(),
625            ActionKey::HlLimitOrder
626        );
627        assert!(serde_json::from_str::<ActionKey>("\"hl_limit_order\"").is_err());
628    }
629
630    #[test]
631    fn maps_rsm_version_and_action_id_to_action_key() {
632        let cases = [
633            (
634                SYSTEM_ACTION_VERSION,
635                SYSTEM_ACTION_ID_CREDIT_TOKEN,
636                ActionKey::SystemCreditToken,
637            ),
638            (
639                SYSTEM_ACTION_VERSION,
640                SYSTEM_ACTION_ID_CREDIT_OPTION,
641                ActionKey::SystemCreditOption,
642            ),
643            (
644                SYSTEM_ACTION_VERSION,
645                SYSTEM_ACTION_ID_START_LIQUIDATION,
646                ActionKey::SystemStartLiquidation,
647            ),
648            (
649                SYSTEM_ACTION_VERSION,
650                SYSTEM_ACTION_ID_STOP_LIQUIDATION,
651                ActionKey::SystemStopLiquidation,
652            ),
653            (
654                SYSTEM_ACTION_VERSION,
655                SYSTEM_ACTION_ID_WITHDRAW_TOKEN,
656                ActionKey::SystemWithdrawToken,
657            ),
658            (
659                HL_ACTION_VERSION,
660                HL_ACTION_ID_LIMIT_ORDER,
661                ActionKey::RsmHlLimitOrder,
662            ),
663            (
664                HL_ACTION_VERSION,
665                HL_ACTION_ID_CANCEL_BY_OID,
666                ActionKey::RsmHlCancelByOid,
667            ),
668            (
669                HL_ACTION_VERSION,
670                HL_ACTION_ID_CANCEL_BY_CLOID,
671                ActionKey::RsmHlCancelByCloid,
672            ),
673            (
674                HL_ACTION_VERSION,
675                HL_ACTION_ID_SEND_ASSET,
676                ActionKey::RsmHlSendAsset,
677            ),
678        ];
679
680        for (version, id, expected) in cases {
681            assert_eq!(
682                ActionKey::from_rsm_action_parts(version, id),
683                Some(expected)
684            );
685        }
686
687        assert_eq!(ActionKey::from_rsm_action_parts(42, 42), None);
688    }
689}