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