Skip to main content

dpp/data_contract/associated_token/token_configuration/
mod.rs

1use crate::data_contract::associated_token::token_configuration::v0::TokenConfigurationV0;
2#[cfg(feature = "json-conversion")]
3use crate::serialization::JsonConvertible;
4#[cfg(feature = "value-conversion")]
5use crate::serialization::ValueConvertible;
6use bincode::{Decode, Encode};
7use derive_more::From;
8use serde::{Deserialize, Serialize};
9use std::borrow::Cow;
10use std::fmt;
11
12pub mod accessors;
13mod methods;
14pub mod v0;
15
16#[cfg_attr(feature = "json-conversion", derive(JsonConvertible))]
17#[cfg_attr(feature = "value-conversion", derive(ValueConvertible))]
18#[derive(Serialize, Deserialize, Encode, Decode, Debug, Clone, PartialEq, Eq, From)]
19#[serde(tag = "$formatVersion")]
20pub enum TokenConfiguration {
21    #[serde(rename = "0")]
22    V0(TokenConfigurationV0),
23}
24impl TokenConfiguration {
25    pub fn as_cow_v0(&self) -> Cow<'_, TokenConfigurationV0> {
26        match self {
27            TokenConfiguration::V0(v0) => Cow::Borrowed(v0),
28        }
29    }
30}
31
32impl fmt::Display for TokenConfiguration {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            TokenConfiguration::V0(v0) => write!(f, "{}", v0),
36        }
37    }
38}
39
40#[cfg(all(test, feature = "json-conversion"))]
41mod tests {
42    use super::*;
43    use crate::serialization::JsonConvertible;
44
45    #[test]
46    fn token_configuration_large_supply_json_round_trip() {
47        let mut config = TokenConfigurationV0::default_most_restrictive();
48        config.base_supply = u64::MAX;
49        let config = TokenConfiguration::V0(config);
50
51        let json = config.to_json().expect("to_json should succeed");
52
53        // u64::MAX > JS MAX_SAFE_INTEGER, so it should be serialized as a string
54        assert!(
55            json["baseSupply"].is_string(),
56            "baseSupply should be a string for large values, got: {:?}",
57            json["baseSupply"]
58        );
59        assert_eq!(json["baseSupply"].as_str().unwrap(), u64::MAX.to_string());
60
61        let restored = TokenConfiguration::from_json(json).expect("from_json should succeed");
62        assert_eq!(config, restored);
63    }
64}
65
66#[cfg(all(
67    test,
68    feature = "json-conversion",
69    feature = "value-conversion",
70    feature = "serde-conversion"
71))]
72mod json_convertible_tests {
73    use super::*;
74    use crate::data_contract::associated_token::token_configuration::v0::TokenConfigurationV0;
75
76    /// `default_most_restrictive` already populates ~25 inner fields with
77    /// non-default values (decimals=8, base_supply=100_000, etc.) — exactly
78    /// what we want for the round-trip structural check below.
79    fn fixture() -> TokenConfiguration {
80        TokenConfiguration::V0(TokenConfigurationV0::default_most_restrictive())
81    }
82
83    /// Tier 3: TokenConfiguration embeds ~25 fields, several of which are
84    /// themselves versioned enums (TokenConfigurationConvention,
85    /// ChangeControlRules x7, TokenKeepsHistoryRules, TokenDistributionRules,
86    /// TokenMarketplaceRules). An inline wire-shape literal would be 200+
87    /// lines and would re-test the nested types' own assertions. Instead we
88    /// assert only the envelope (top-level keys + `$formatVersion`) and trust
89    /// the nested types' tests for inner shape correctness.
90    #[test]
91    fn json_round_trip_with_envelope_shape() {
92        use crate::serialization::JsonConvertible;
93        let original = fixture();
94        let json = original.to_json().expect("to_json");
95        // Envelope check: format version + top-level keys present.
96        assert_eq!(
97            json.get("$formatVersion").and_then(|v| v.as_str()),
98            Some("0")
99        );
100        for key in [
101            "conventions",
102            "conventionsChangeRules",
103            "baseSupply",
104            "maxSupply",
105            "keepsHistory",
106            "startAsPaused",
107            "allowTransferToFrozenBalance",
108            "maxSupplyChangeRules",
109            "distributionRules",
110            "marketplaceRules",
111            "manualMintingRules",
112            "manualBurningRules",
113            "freezeRules",
114            "unfreezeRules",
115            "destroyFrozenFundsRules",
116            "emergencyActionRules",
117            "mainControlGroup",
118            "mainControlGroupCanBeModified",
119            "description",
120        ] {
121            assert!(
122                json.get(key).is_some(),
123                "expected top-level key {:?} in JSON envelope",
124                key
125            );
126        }
127        let recovered = TokenConfiguration::from_json(json).expect("from_json");
128        assert_eq!(original, recovered);
129    }
130
131    #[test]
132    fn value_round_trip_with_envelope_shape() {
133        use crate::serialization::ValueConvertible;
134        let original = fixture();
135        let value = original.to_object().expect("to_object");
136        // Same envelope-only check on the platform_value side.
137        let map = value.as_map().expect("value is a Map");
138        let has_key = |k: &str| {
139            map.iter()
140                .any(|(key, _)| matches!(key, platform_value::Value::Text(t) if t == k))
141        };
142        assert!(has_key("$formatVersion"));
143        for key in [
144            "conventions",
145            "conventionsChangeRules",
146            "baseSupply",
147            "maxSupply",
148            "keepsHistory",
149            "startAsPaused",
150            "allowTransferToFrozenBalance",
151            "maxSupplyChangeRules",
152            "distributionRules",
153            "marketplaceRules",
154            "manualMintingRules",
155            "manualBurningRules",
156            "freezeRules",
157            "unfreezeRules",
158            "destroyFrozenFundsRules",
159            "emergencyActionRules",
160            "mainControlGroup",
161            "mainControlGroupCanBeModified",
162            "description",
163        ] {
164            assert!(
165                has_key(key),
166                "expected top-level key {:?} in Value envelope",
167                key
168            );
169        }
170        let recovered = TokenConfiguration::from_object(value).expect("from_object");
171        assert_eq!(original, recovered);
172    }
173}