Skip to main content

dpp/data_contract/associated_token/token_configuration/v0/
mod.rs

1mod accessors;
2
3use crate::balances::credits::TokenAmount;
4use crate::data_contract::associated_token::token_configuration_convention::v0::TokenConfigurationConventionV0;
5use crate::data_contract::associated_token::token_configuration_convention::TokenConfigurationConvention;
6use crate::data_contract::associated_token::token_distribution_rules::v0::TokenDistributionRulesV0;
7use crate::data_contract::associated_token::token_distribution_rules::TokenDistributionRules;
8use crate::data_contract::associated_token::token_keeps_history_rules::v0::TokenKeepsHistoryRulesV0;
9use crate::data_contract::associated_token::token_keeps_history_rules::TokenKeepsHistoryRules;
10use crate::data_contract::associated_token::token_marketplace_rules::v0::{
11    TokenMarketplaceRulesV0, TokenTradeMode,
12};
13use crate::data_contract::associated_token::token_marketplace_rules::TokenMarketplaceRules;
14use crate::data_contract::associated_token::token_perpetual_distribution::TokenPerpetualDistribution;
15use crate::data_contract::associated_token::token_pre_programmed_distribution::TokenPreProgrammedDistribution;
16use crate::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers;
17use crate::data_contract::change_control_rules::v0::ChangeControlRulesV0;
18use crate::data_contract::change_control_rules::ChangeControlRules;
19use crate::data_contract::GroupContractPosition;
20#[cfg(feature = "json-conversion")]
21use crate::serialization::json_safe_fields;
22use bincode::{Decode, Encode};
23use serde::{Deserialize, Serialize};
24use std::fmt;
25
26/// Defines the complete configuration for a version 0 token contract.
27///
28/// `TokenConfigurationV0` encapsulates all metadata, control rules, supply settings,
29/// and governance constraints used to initialize and manage a token instance on Platform.
30/// This structure serves as the core representation of a token's logic, permissions,
31/// and capabilities.
32///
33/// This configuration is designed to be deterministic and versioned for compatibility
34/// across protocol upgrades and validation environments.
35#[cfg_attr(feature = "json-conversion", json_safe_fields)]
36#[derive(Serialize, Deserialize, Decode, Encode, Debug, Clone, PartialEq, Eq)]
37#[serde(rename_all = "camelCase")]
38pub struct TokenConfigurationV0 {
39    /// Metadata conventions, including decimals and localizations.
40    pub conventions: TokenConfigurationConvention,
41
42    /// Change control rules governing who can modify the conventions field.
43    #[serde(default = "default_change_control_rules")]
44    pub conventions_change_rules: ChangeControlRules,
45
46    /// The initial token supply minted at creation.
47    #[serde(default)]
48    pub base_supply: TokenAmount,
49
50    /// The maximum allowable supply of the token.
51    ///
52    /// If `None`, the supply is unbounded unless otherwise constrained by minting logic.
53    #[serde(default)]
54    pub max_supply: Option<TokenAmount>,
55
56    /// Configuration governing which historical actions are recorded for this token.
57    #[serde(default = "default_token_keeps_history_rules")]
58    pub keeps_history: TokenKeepsHistoryRules,
59
60    /// Indicates whether the token should start in a paused state.
61    ///
62    /// When `true`, transfers are disallowed until explicitly unpaused via an emergency action.
63    #[serde(default = "default_starts_as_paused")]
64    pub start_as_paused: bool,
65
66    /// Allows minting and transferring to frozen token balances if enabled.
67    #[serde(default = "default_allow_transfer_to_frozen_balance")]
68    pub allow_transfer_to_frozen_balance: bool,
69
70    /// Change control rules for updating the `max_supply`.
71    ///
72    /// Note: The `max_supply` can never be reduced below the `base_supply`.
73    #[serde(default = "default_change_control_rules")]
74    pub max_supply_change_rules: ChangeControlRules,
75
76    /// Defines the token's distribution logic, including perpetual and pre-programmed distributions.
77    #[serde(default = "default_token_distribution_rules")]
78    pub distribution_rules: TokenDistributionRules,
79
80    /// Defines the token's marketplace logic.
81    #[serde(default = "default_token_marketplace_rules")]
82    pub marketplace_rules: TokenMarketplaceRules,
83
84    /// Rules controlling who is authorized to perform manual minting of tokens.
85    #[serde(default = "default_contract_owner_change_control_rules")]
86    pub manual_minting_rules: ChangeControlRules,
87
88    /// Rules controlling who is authorized to perform manual burning of tokens.
89    #[serde(default = "default_contract_owner_change_control_rules")]
90    pub manual_burning_rules: ChangeControlRules,
91
92    /// Rules governing who may freeze token balances.
93    #[serde(default = "default_change_control_rules")]
94    pub freeze_rules: ChangeControlRules,
95
96    /// Rules governing who may unfreeze token balances.
97    #[serde(default = "default_change_control_rules")]
98    pub unfreeze_rules: ChangeControlRules,
99
100    /// Rules governing who may destroy frozen funds.
101    #[serde(default = "default_change_control_rules")]
102    pub destroy_frozen_funds_rules: ChangeControlRules,
103
104    /// Rules governing who may invoke emergency actions, such as pausing transfers.
105    #[serde(default = "default_change_control_rules")]
106    pub emergency_action_rules: ChangeControlRules,
107
108    /// Optional reference to the group assigned as the token's main control group.
109    #[serde(default)]
110    pub main_control_group: Option<GroupContractPosition>,
111
112    /// Defines whether and how the main control group assignment may be modified.
113    #[serde(default)]
114    pub main_control_group_can_be_modified: AuthorizedActionTakers,
115
116    /// Optional textual description of the token's purpose, behavior, or metadata.
117    #[serde(default)]
118    pub description: Option<String>,
119}
120
121// Default function for `keeps_history`
122fn default_keeps_history() -> bool {
123    true // Default to `true` for keeps_history
124}
125
126// Default function for `starts_as_paused`
127fn default_starts_as_paused() -> bool {
128    false
129}
130
131// Default function for `allow_transfer_to_frozen_balance`
132fn default_allow_transfer_to_frozen_balance() -> bool {
133    true
134}
135
136fn default_token_keeps_history_rules() -> TokenKeepsHistoryRules {
137    TokenKeepsHistoryRules::V0(TokenKeepsHistoryRulesV0 {
138        keeps_transfer_history: true,
139        keeps_freezing_history: true,
140        keeps_minting_history: true,
141        keeps_burning_history: true,
142        keeps_direct_pricing_history: true,
143        keeps_direct_purchase_history: true,
144    })
145}
146
147fn default_token_distribution_rules() -> TokenDistributionRules {
148    TokenDistributionRules::V0(TokenDistributionRulesV0 {
149        perpetual_distribution: None,
150        perpetual_distribution_rules: ChangeControlRules::V0(ChangeControlRulesV0 {
151            authorized_to_make_change: AuthorizedActionTakers::NoOne,
152            admin_action_takers: AuthorizedActionTakers::NoOne,
153            changing_authorized_action_takers_to_no_one_allowed: false,
154            changing_admin_action_takers_to_no_one_allowed: false,
155            self_changing_admin_action_takers_allowed: false,
156        }),
157        pre_programmed_distribution: None,
158        new_tokens_destination_identity: None,
159        new_tokens_destination_identity_rules: ChangeControlRules::V0(ChangeControlRulesV0 {
160            authorized_to_make_change: AuthorizedActionTakers::NoOne,
161            admin_action_takers: AuthorizedActionTakers::NoOne,
162            changing_authorized_action_takers_to_no_one_allowed: false,
163            changing_admin_action_takers_to_no_one_allowed: false,
164            self_changing_admin_action_takers_allowed: false,
165        }),
166        minting_allow_choosing_destination: true,
167        minting_allow_choosing_destination_rules: ChangeControlRules::V0(ChangeControlRulesV0 {
168            authorized_to_make_change: AuthorizedActionTakers::NoOne,
169            admin_action_takers: AuthorizedActionTakers::NoOne,
170            changing_authorized_action_takers_to_no_one_allowed: false,
171            changing_admin_action_takers_to_no_one_allowed: false,
172            self_changing_admin_action_takers_allowed: false,
173        }),
174        change_direct_purchase_pricing_rules: ChangeControlRules::V0(ChangeControlRulesV0 {
175            authorized_to_make_change: AuthorizedActionTakers::NoOne,
176            admin_action_takers: AuthorizedActionTakers::NoOne,
177            changing_authorized_action_takers_to_no_one_allowed: false,
178            changing_admin_action_takers_to_no_one_allowed: false,
179            self_changing_admin_action_takers_allowed: false,
180        }),
181    })
182}
183
184fn default_token_marketplace_rules() -> TokenMarketplaceRules {
185    TokenMarketplaceRules::V0(TokenMarketplaceRulesV0 {
186        trade_mode: TokenTradeMode::NotTradeable,
187        trade_mode_change_rules: ChangeControlRules::V0(ChangeControlRulesV0 {
188            authorized_to_make_change: AuthorizedActionTakers::NoOne,
189            admin_action_takers: AuthorizedActionTakers::NoOne,
190            changing_authorized_action_takers_to_no_one_allowed: false,
191            changing_admin_action_takers_to_no_one_allowed: false,
192            self_changing_admin_action_takers_allowed: false,
193        }),
194    })
195}
196
197fn default_change_control_rules() -> ChangeControlRules {
198    ChangeControlRules::V0(ChangeControlRulesV0 {
199        authorized_to_make_change: AuthorizedActionTakers::NoOne,
200        admin_action_takers: AuthorizedActionTakers::NoOne,
201        changing_authorized_action_takers_to_no_one_allowed: false,
202        changing_admin_action_takers_to_no_one_allowed: false,
203        self_changing_admin_action_takers_allowed: false,
204    })
205}
206
207fn default_contract_owner_change_control_rules() -> ChangeControlRules {
208    ChangeControlRules::V0(ChangeControlRulesV0 {
209        authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
210        admin_action_takers: AuthorizedActionTakers::NoOne,
211        changing_authorized_action_takers_to_no_one_allowed: false,
212        changing_admin_action_takers_to_no_one_allowed: false,
213        self_changing_admin_action_takers_allowed: false,
214    })
215}
216
217impl fmt::Display for TokenConfigurationV0 {
218    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219        write!(
220            f,
221            "TokenConfigurationV0 {{\n  conventions: {:?},\n  conventions_change_rules: {:?},\n  base_supply: {},\n  max_supply: {:?},\n  keeps_history: {},\n  start_as_paused: {},\n  allow_transfer_to_frozen_balance: {},\n  max_supply_change_rules: {:?},\n  distribution_rules: {},\n  manual_minting_rules: {:?},\n  manual_burning_rules: {:?},\n  freeze_rules: {:?},\n  unfreeze_rules: {:?},\n  destroy_frozen_funds_rules: {:?},\n  emergency_action_rules: {:?},\n  main_control_group: {:?},\n  main_control_group_can_be_modified: {:?}\n}}",
222            self.conventions,
223            self.conventions_change_rules,
224            self.base_supply,
225            self.max_supply,
226            self.keeps_history,
227            self.start_as_paused,
228            self.allow_transfer_to_frozen_balance,
229            self.max_supply_change_rules,
230            self.distribution_rules,
231            self.manual_minting_rules,
232            self.manual_burning_rules,
233            self.freeze_rules,
234            self.unfreeze_rules,
235            self.destroy_frozen_funds_rules,
236            self.emergency_action_rules,
237            self.main_control_group,
238            self.main_control_group_can_be_modified
239        )
240    }
241}
242
243/// Represents predefined capability levels for token control presets.
244///
245/// `TokenConfigurationPresetFeatures` defines a hierarchy of governance capabilities
246/// that can be used to initialize rule sets for a token. Each variant enables a specific
247/// scope of permitted actions, allowing for simple selection of common governance models.
248///
249/// These presets are intended to be used in conjunction with `TokenConfigurationPreset`
250/// to simplify token setup and enforce governance constraints consistently.
251#[derive(Serialize, Deserialize, Decode, Encode, Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
252pub enum TokenConfigurationPresetFeatures {
253    /// No actions are permitted after initialization. All governance and control
254    /// settings are immutable.
255    ///
256    /// Suitable for tokens that should remain fixed and tamper-proof.
257    MostRestrictive,
258
259    /// Only emergency actions (e.g., pausing the token) are permitted.
260    ///
261    /// Minting, burning, and advanced operations (such as freezing) are disallowed.
262    /// This preset allows minimal control for critical situations without risking
263    /// token supply or ownership manipulation.
264    WithOnlyEmergencyAction,
265
266    /// Allows minting and burning operations, but not advanced features such as freezing.
267    ///
268    /// Enables supply management without enabling full administrative capabilities.
269    WithMintingAndBurningActions,
270
271    /// Grants the ability to perform advanced actions, including freezing and unfreezing balances.
272    ///
273    /// Minting and burning are also permitted. Suitable for tokens that require
274    /// moderate administrative control without total override capabilities.
275    WithAllAdvancedActions,
276
277    /// The action taker is a god, he can do everything, even taking away his own power.
278    /// This grants unrestricted control to the action taker, including the ability to revoke
279    /// their own permissions or transfer all governance.
280    ///
281    /// This includes minting, burning, freezing, emergency actions, and full rule modification.
282    /// Should only be used with trusted or self-destructible authorities.
283    WithExtremeActions,
284}
285
286/// A high-level preset representing common configurations for token governance and control.
287///
288/// `TokenConfigurationPreset` provides a simplified way to initialize a set of
289/// predefined token rules (e.g., minting, burning, freezing, emergency actions)
290/// by selecting a feature set (`features`) and defining the authorized actor (`action_taker`)
291/// responsible for performing allowed actions.
292///
293/// This abstraction allows users to choose between common control configurations
294/// ranging from immutable tokens to fully administrator-controlled assets.
295#[derive(Serialize, Deserialize, Decode, Encode, Debug, Clone, PartialEq, Eq, PartialOrd)]
296pub struct TokenConfigurationPreset {
297    /// Defines the set of capabilities enabled in this preset (e.g., whether minting,
298    /// burning, freezing, or emergency actions are permitted).
299    ///
300    /// The selected feature set determines the default rule behavior for all change control
301    /// and governance actions within the token configuration.
302    pub features: TokenConfigurationPresetFeatures,
303
304    /// The identity or group authorized to perform actions defined by the preset.
305    ///
306    /// This includes acting as the admin for various rule changes, executing allowed token
307    /// operations, or performing emergency control (depending on the selected feature set).
308    pub action_taker: AuthorizedActionTakers,
309}
310impl TokenConfigurationPreset {
311    pub fn default_main_control_group_can_be_modified(&self) -> AuthorizedActionTakers {
312        match self.features {
313            TokenConfigurationPresetFeatures::MostRestrictive
314            | TokenConfigurationPresetFeatures::WithOnlyEmergencyAction
315            | TokenConfigurationPresetFeatures::WithMintingAndBurningActions
316            | TokenConfigurationPresetFeatures::WithAllAdvancedActions => {
317                AuthorizedActionTakers::NoOne
318            }
319            TokenConfigurationPresetFeatures::WithExtremeActions => self.action_taker,
320        }
321    }
322    pub fn default_basic_change_control_rules_v0(&self) -> ChangeControlRulesV0 {
323        match self.features {
324            TokenConfigurationPresetFeatures::MostRestrictive
325            | TokenConfigurationPresetFeatures::WithOnlyEmergencyAction => ChangeControlRulesV0 {
326                authorized_to_make_change: AuthorizedActionTakers::NoOne,
327                admin_action_takers: AuthorizedActionTakers::NoOne,
328                changing_authorized_action_takers_to_no_one_allowed: false,
329                changing_admin_action_takers_to_no_one_allowed: false,
330                self_changing_admin_action_takers_allowed: false,
331            },
332            TokenConfigurationPresetFeatures::WithMintingAndBurningActions
333            | TokenConfigurationPresetFeatures::WithAllAdvancedActions => ChangeControlRulesV0 {
334                authorized_to_make_change: self.action_taker,
335                admin_action_takers: self.action_taker,
336                changing_authorized_action_takers_to_no_one_allowed: false,
337                changing_admin_action_takers_to_no_one_allowed: false,
338                self_changing_admin_action_takers_allowed: true,
339            },
340            TokenConfigurationPresetFeatures::WithExtremeActions => ChangeControlRulesV0 {
341                authorized_to_make_change: self.action_taker,
342                admin_action_takers: self.action_taker,
343                changing_authorized_action_takers_to_no_one_allowed: true,
344                changing_admin_action_takers_to_no_one_allowed: true,
345                self_changing_admin_action_takers_allowed: true,
346            },
347        }
348    }
349
350    pub fn default_advanced_change_control_rules_v0(&self) -> ChangeControlRulesV0 {
351        match self.features {
352            TokenConfigurationPresetFeatures::MostRestrictive
353            | TokenConfigurationPresetFeatures::WithOnlyEmergencyAction
354            | TokenConfigurationPresetFeatures::WithMintingAndBurningActions => {
355                ChangeControlRulesV0 {
356                    authorized_to_make_change: AuthorizedActionTakers::NoOne,
357                    admin_action_takers: AuthorizedActionTakers::NoOne,
358                    changing_authorized_action_takers_to_no_one_allowed: false,
359                    changing_admin_action_takers_to_no_one_allowed: false,
360                    self_changing_admin_action_takers_allowed: false,
361                }
362            }
363            TokenConfigurationPresetFeatures::WithAllAdvancedActions => ChangeControlRulesV0 {
364                authorized_to_make_change: self.action_taker,
365                admin_action_takers: self.action_taker,
366                changing_authorized_action_takers_to_no_one_allowed: false,
367                changing_admin_action_takers_to_no_one_allowed: false,
368                self_changing_admin_action_takers_allowed: true,
369            },
370            TokenConfigurationPresetFeatures::WithExtremeActions => ChangeControlRulesV0 {
371                authorized_to_make_change: self.action_taker,
372                admin_action_takers: self.action_taker,
373                changing_authorized_action_takers_to_no_one_allowed: true,
374                changing_admin_action_takers_to_no_one_allowed: true,
375                self_changing_admin_action_takers_allowed: true,
376            },
377        }
378    }
379
380    pub fn default_emergency_action_change_control_rules_v0(&self) -> ChangeControlRulesV0 {
381        match self.features {
382            TokenConfigurationPresetFeatures::MostRestrictive => ChangeControlRulesV0 {
383                authorized_to_make_change: AuthorizedActionTakers::NoOne,
384                admin_action_takers: AuthorizedActionTakers::NoOne,
385                changing_authorized_action_takers_to_no_one_allowed: false,
386                changing_admin_action_takers_to_no_one_allowed: false,
387                self_changing_admin_action_takers_allowed: false,
388            },
389            TokenConfigurationPresetFeatures::WithAllAdvancedActions
390            | TokenConfigurationPresetFeatures::WithMintingAndBurningActions
391            | TokenConfigurationPresetFeatures::WithOnlyEmergencyAction => ChangeControlRulesV0 {
392                authorized_to_make_change: self.action_taker,
393                admin_action_takers: self.action_taker,
394                changing_authorized_action_takers_to_no_one_allowed: false,
395                changing_admin_action_takers_to_no_one_allowed: false,
396                self_changing_admin_action_takers_allowed: true,
397            },
398            TokenConfigurationPresetFeatures::WithExtremeActions => ChangeControlRulesV0 {
399                authorized_to_make_change: self.action_taker,
400                admin_action_takers: self.action_taker,
401                changing_authorized_action_takers_to_no_one_allowed: true,
402                changing_admin_action_takers_to_no_one_allowed: true,
403                self_changing_admin_action_takers_allowed: true,
404            },
405        }
406    }
407
408    pub fn default_distribution_rules_v0(
409        &self,
410        perpetual_distribution: Option<TokenPerpetualDistribution>,
411        pre_programmed_distribution: Option<TokenPreProgrammedDistribution>,
412        with_direct_pricing: bool,
413    ) -> TokenDistributionRulesV0 {
414        TokenDistributionRulesV0 {
415            perpetual_distribution,
416            perpetual_distribution_rules: self.default_advanced_change_control_rules_v0().into(),
417            pre_programmed_distribution,
418            new_tokens_destination_identity: None,
419            new_tokens_destination_identity_rules: self
420                .default_basic_change_control_rules_v0()
421                .into(),
422            minting_allow_choosing_destination: true,
423            minting_allow_choosing_destination_rules: self
424                .default_basic_change_control_rules_v0()
425                .into(),
426            change_direct_purchase_pricing_rules: if with_direct_pricing {
427                self.default_basic_change_control_rules_v0().into()
428            } else {
429                ChangeControlRulesV0 {
430                    authorized_to_make_change: AuthorizedActionTakers::NoOne,
431                    admin_action_takers: AuthorizedActionTakers::NoOne,
432                    changing_authorized_action_takers_to_no_one_allowed: false,
433                    changing_admin_action_takers_to_no_one_allowed: false,
434                    self_changing_admin_action_takers_allowed: false,
435                }
436                .into()
437            },
438        }
439    }
440
441    pub fn default_marketplace_rules_v0(&self) -> TokenMarketplaceRulesV0 {
442        TokenMarketplaceRulesV0 {
443            trade_mode: TokenTradeMode::NotTradeable,
444            trade_mode_change_rules: self.default_basic_change_control_rules_v0().into(),
445        }
446    }
447
448    pub fn token_configuration_v0(
449        &self,
450        conventions: TokenConfigurationConvention,
451        base_supply: TokenAmount,
452        max_supply: Option<TokenAmount>,
453        keeps_all_history: bool,
454        with_direct_pricing: bool,
455    ) -> TokenConfigurationV0 {
456        TokenConfigurationV0 {
457            conventions,
458            conventions_change_rules: self.default_basic_change_control_rules_v0().into(),
459            base_supply,
460            max_supply,
461            keeps_history: TokenKeepsHistoryRulesV0::default_for_keeping_all_history(
462                keeps_all_history,
463            )
464            .into(),
465            start_as_paused: false,
466            allow_transfer_to_frozen_balance: true,
467            max_supply_change_rules: self.default_advanced_change_control_rules_v0().into(),
468            distribution_rules: self
469                .default_distribution_rules_v0(None, None, with_direct_pricing)
470                .into(),
471            marketplace_rules: self.default_marketplace_rules_v0().into(),
472            manual_minting_rules: self.default_basic_change_control_rules_v0().into(),
473            manual_burning_rules: self.default_basic_change_control_rules_v0().into(),
474            freeze_rules: self.default_advanced_change_control_rules_v0().into(),
475            unfreeze_rules: self.default_advanced_change_control_rules_v0().into(),
476            destroy_frozen_funds_rules: self.default_advanced_change_control_rules_v0().into(),
477            emergency_action_rules: self
478                .default_emergency_action_change_control_rules_v0()
479                .into(),
480            main_control_group: None,
481            main_control_group_can_be_modified: self.default_main_control_group_can_be_modified(),
482            description: None,
483        }
484    }
485}
486
487impl TokenConfigurationV0 {
488    pub fn default_most_restrictive() -> Self {
489        TokenConfigurationPreset {
490            features: TokenConfigurationPresetFeatures::MostRestrictive,
491            action_taker: AuthorizedActionTakers::NoOne,
492        }
493        .token_configuration_v0(
494            TokenConfigurationConvention::V0(TokenConfigurationConventionV0 {
495                localizations: Default::default(),
496                decimals: 8,
497            }),
498            100000,
499            None,
500            true,
501            false,
502        )
503    }
504
505    pub fn with_base_supply(mut self, base_supply: TokenAmount) -> Self {
506        self.base_supply = base_supply;
507        self
508    }
509}
510
511#[cfg(test)]
512mod tests {
513    use super::*;
514    use crate::data_contract::associated_token::token_configuration::accessors::v0::{
515        TokenConfigurationV0Getters, TokenConfigurationV0Setters,
516    };
517    use platform_value::Identifier;
518
519    fn preset(
520        features: TokenConfigurationPresetFeatures,
521        action_taker: AuthorizedActionTakers,
522    ) -> TokenConfigurationPreset {
523        TokenConfigurationPreset {
524            features,
525            action_taker,
526        }
527    }
528
529    // --- default_main_control_group_can_be_modified ---
530
531    #[test]
532    fn preset_main_control_group_can_be_modified_most_restrictive_is_no_one() {
533        let p = preset(
534            TokenConfigurationPresetFeatures::MostRestrictive,
535            AuthorizedActionTakers::ContractOwner,
536        );
537        assert_eq!(
538            p.default_main_control_group_can_be_modified(),
539            AuthorizedActionTakers::NoOne
540        );
541    }
542
543    #[test]
544    fn preset_main_control_group_can_be_modified_only_emergency_is_no_one() {
545        let p = preset(
546            TokenConfigurationPresetFeatures::WithOnlyEmergencyAction,
547            AuthorizedActionTakers::ContractOwner,
548        );
549        assert_eq!(
550            p.default_main_control_group_can_be_modified(),
551            AuthorizedActionTakers::NoOne
552        );
553    }
554
555    #[test]
556    fn preset_main_control_group_can_be_modified_minting_burning_is_no_one() {
557        let p = preset(
558            TokenConfigurationPresetFeatures::WithMintingAndBurningActions,
559            AuthorizedActionTakers::ContractOwner,
560        );
561        assert_eq!(
562            p.default_main_control_group_can_be_modified(),
563            AuthorizedActionTakers::NoOne
564        );
565    }
566
567    #[test]
568    fn preset_main_control_group_can_be_modified_advanced_is_no_one() {
569        let p = preset(
570            TokenConfigurationPresetFeatures::WithAllAdvancedActions,
571            AuthorizedActionTakers::ContractOwner,
572        );
573        assert_eq!(
574            p.default_main_control_group_can_be_modified(),
575            AuthorizedActionTakers::NoOne
576        );
577    }
578
579    #[test]
580    fn preset_main_control_group_can_be_modified_extreme_is_action_taker() {
581        let taker = AuthorizedActionTakers::Identity(Identifier::from([9u8; 32]));
582        let p = preset(TokenConfigurationPresetFeatures::WithExtremeActions, taker);
583        assert_eq!(p.default_main_control_group_can_be_modified(), taker);
584    }
585
586    // --- default_basic_change_control_rules_v0 ---
587
588    #[test]
589    fn preset_basic_rules_most_restrictive_is_no_one_locked() {
590        let p = preset(
591            TokenConfigurationPresetFeatures::MostRestrictive,
592            AuthorizedActionTakers::ContractOwner,
593        );
594        let rules = p.default_basic_change_control_rules_v0();
595        assert_eq!(
596            rules.authorized_to_make_change,
597            AuthorizedActionTakers::NoOne
598        );
599        assert_eq!(rules.admin_action_takers, AuthorizedActionTakers::NoOne);
600        assert!(!rules.changing_authorized_action_takers_to_no_one_allowed);
601        assert!(!rules.changing_admin_action_takers_to_no_one_allowed);
602        assert!(!rules.self_changing_admin_action_takers_allowed);
603    }
604
605    #[test]
606    fn preset_basic_rules_only_emergency_is_no_one_locked() {
607        let p = preset(
608            TokenConfigurationPresetFeatures::WithOnlyEmergencyAction,
609            AuthorizedActionTakers::ContractOwner,
610        );
611        let rules = p.default_basic_change_control_rules_v0();
612        assert_eq!(
613            rules.authorized_to_make_change,
614            AuthorizedActionTakers::NoOne
615        );
616    }
617
618    #[test]
619    fn preset_basic_rules_minting_burning_is_action_taker_self_mutable() {
620        let taker = AuthorizedActionTakers::ContractOwner;
621        let p = preset(
622            TokenConfigurationPresetFeatures::WithMintingAndBurningActions,
623            taker,
624        );
625        let rules = p.default_basic_change_control_rules_v0();
626        assert_eq!(rules.authorized_to_make_change, taker);
627        assert_eq!(rules.admin_action_takers, taker);
628        assert!(rules.self_changing_admin_action_takers_allowed);
629        // but not to no-one
630        assert!(!rules.changing_authorized_action_takers_to_no_one_allowed);
631    }
632
633    #[test]
634    fn preset_basic_rules_advanced_is_action_taker_self_mutable() {
635        let taker = AuthorizedActionTakers::ContractOwner;
636        let p = preset(
637            TokenConfigurationPresetFeatures::WithAllAdvancedActions,
638            taker,
639        );
640        let rules = p.default_basic_change_control_rules_v0();
641        assert_eq!(rules.authorized_to_make_change, taker);
642        assert!(rules.self_changing_admin_action_takers_allowed);
643        assert!(!rules.changing_admin_action_takers_to_no_one_allowed);
644    }
645
646    #[test]
647    fn preset_basic_rules_extreme_allows_no_one_transitions() {
648        let taker = AuthorizedActionTakers::ContractOwner;
649        let p = preset(TokenConfigurationPresetFeatures::WithExtremeActions, taker);
650        let rules = p.default_basic_change_control_rules_v0();
651        assert_eq!(rules.authorized_to_make_change, taker);
652        assert!(rules.changing_authorized_action_takers_to_no_one_allowed);
653        assert!(rules.changing_admin_action_takers_to_no_one_allowed);
654        assert!(rules.self_changing_admin_action_takers_allowed);
655    }
656
657    // --- default_advanced_change_control_rules_v0 ---
658
659    #[test]
660    fn preset_advanced_rules_most_restrictive_is_locked() {
661        let p = preset(
662            TokenConfigurationPresetFeatures::MostRestrictive,
663            AuthorizedActionTakers::ContractOwner,
664        );
665        let rules = p.default_advanced_change_control_rules_v0();
666        assert_eq!(
667            rules.authorized_to_make_change,
668            AuthorizedActionTakers::NoOne
669        );
670        assert!(!rules.self_changing_admin_action_takers_allowed);
671    }
672
673    #[test]
674    fn preset_advanced_rules_minting_burning_is_locked() {
675        let p = preset(
676            TokenConfigurationPresetFeatures::WithMintingAndBurningActions,
677            AuthorizedActionTakers::ContractOwner,
678        );
679        // Minting/burning does NOT open up advanced operations -> advanced remains NoOne
680        let rules = p.default_advanced_change_control_rules_v0();
681        assert_eq!(
682            rules.authorized_to_make_change,
683            AuthorizedActionTakers::NoOne
684        );
685        assert_eq!(rules.admin_action_takers, AuthorizedActionTakers::NoOne);
686    }
687
688    #[test]
689    fn preset_advanced_rules_only_emergency_is_locked() {
690        let p = preset(
691            TokenConfigurationPresetFeatures::WithOnlyEmergencyAction,
692            AuthorizedActionTakers::ContractOwner,
693        );
694        let rules = p.default_advanced_change_control_rules_v0();
695        assert_eq!(
696            rules.authorized_to_make_change,
697            AuthorizedActionTakers::NoOne
698        );
699    }
700
701    #[test]
702    fn preset_advanced_rules_advanced_allows_action_taker() {
703        let taker = AuthorizedActionTakers::ContractOwner;
704        let p = preset(
705            TokenConfigurationPresetFeatures::WithAllAdvancedActions,
706            taker,
707        );
708        let rules = p.default_advanced_change_control_rules_v0();
709        assert_eq!(rules.authorized_to_make_change, taker);
710        assert!(rules.self_changing_admin_action_takers_allowed);
711        assert!(!rules.changing_authorized_action_takers_to_no_one_allowed);
712    }
713
714    #[test]
715    fn preset_advanced_rules_extreme_allows_everything() {
716        let taker = AuthorizedActionTakers::ContractOwner;
717        let p = preset(TokenConfigurationPresetFeatures::WithExtremeActions, taker);
718        let rules = p.default_advanced_change_control_rules_v0();
719        assert!(rules.changing_authorized_action_takers_to_no_one_allowed);
720        assert!(rules.changing_admin_action_takers_to_no_one_allowed);
721        assert!(rules.self_changing_admin_action_takers_allowed);
722    }
723
724    // --- default_emergency_action_change_control_rules_v0 ---
725
726    #[test]
727    fn preset_emergency_rules_most_restrictive_is_no_one() {
728        let p = preset(
729            TokenConfigurationPresetFeatures::MostRestrictive,
730            AuthorizedActionTakers::ContractOwner,
731        );
732        let rules = p.default_emergency_action_change_control_rules_v0();
733        assert_eq!(
734            rules.authorized_to_make_change,
735            AuthorizedActionTakers::NoOne
736        );
737    }
738
739    #[test]
740    fn preset_emergency_rules_only_emergency_allows_action_taker() {
741        let taker = AuthorizedActionTakers::ContractOwner;
742        let p = preset(
743            TokenConfigurationPresetFeatures::WithOnlyEmergencyAction,
744            taker,
745        );
746        let rules = p.default_emergency_action_change_control_rules_v0();
747        assert_eq!(rules.authorized_to_make_change, taker);
748        assert!(rules.self_changing_admin_action_takers_allowed);
749    }
750
751    #[test]
752    fn preset_emergency_rules_minting_burning_allows_action_taker() {
753        let taker = AuthorizedActionTakers::ContractOwner;
754        let p = preset(
755            TokenConfigurationPresetFeatures::WithMintingAndBurningActions,
756            taker,
757        );
758        let rules = p.default_emergency_action_change_control_rules_v0();
759        assert_eq!(rules.authorized_to_make_change, taker);
760        assert!(rules.self_changing_admin_action_takers_allowed);
761    }
762
763    #[test]
764    fn preset_emergency_rules_advanced_allows_action_taker() {
765        let taker = AuthorizedActionTakers::ContractOwner;
766        let p = preset(
767            TokenConfigurationPresetFeatures::WithAllAdvancedActions,
768            taker,
769        );
770        let rules = p.default_emergency_action_change_control_rules_v0();
771        assert_eq!(rules.authorized_to_make_change, taker);
772    }
773
774    #[test]
775    fn preset_emergency_rules_extreme_allows_no_one_transitions() {
776        let taker = AuthorizedActionTakers::ContractOwner;
777        let p = preset(TokenConfigurationPresetFeatures::WithExtremeActions, taker);
778        let rules = p.default_emergency_action_change_control_rules_v0();
779        assert!(rules.changing_authorized_action_takers_to_no_one_allowed);
780    }
781
782    // --- default_distribution_rules_v0 with/without direct pricing ---
783
784    #[test]
785    fn preset_distribution_rules_with_direct_pricing_uses_basic_rules() {
786        let taker = AuthorizedActionTakers::ContractOwner;
787        let p = preset(TokenConfigurationPresetFeatures::WithExtremeActions, taker);
788        let rules = p.default_distribution_rules_v0(None, None, true);
789        // With direct pricing enabled, the rules match basic (extreme -> owner, all permissive)
790        assert_eq!(
791            rules
792                .change_direct_purchase_pricing_rules
793                .authorized_to_make_change_action_takers(),
794            &taker
795        );
796    }
797
798    #[test]
799    fn preset_distribution_rules_without_direct_pricing_locks_it_down() {
800        let taker = AuthorizedActionTakers::ContractOwner;
801        let p = preset(TokenConfigurationPresetFeatures::WithExtremeActions, taker);
802        let rules = p.default_distribution_rules_v0(None, None, false);
803        // Without direct pricing, change_direct_purchase_pricing_rules is hard-coded to NoOne
804        assert_eq!(
805            rules
806                .change_direct_purchase_pricing_rules
807                .authorized_to_make_change_action_takers(),
808            &AuthorizedActionTakers::NoOne
809        );
810    }
811
812    #[test]
813    fn preset_distribution_rules_minting_choosing_destination_defaults_true() {
814        let p = preset(
815            TokenConfigurationPresetFeatures::MostRestrictive,
816            AuthorizedActionTakers::NoOne,
817        );
818        let rules = p.default_distribution_rules_v0(None, None, false);
819        assert!(rules.minting_allow_choosing_destination);
820        assert!(rules.new_tokens_destination_identity.is_none());
821        assert!(rules.perpetual_distribution.is_none());
822        assert!(rules.pre_programmed_distribution.is_none());
823    }
824
825    // --- default_marketplace_rules_v0 ---
826
827    #[test]
828    fn preset_marketplace_rules_default_is_not_tradeable() {
829        let p = preset(
830            TokenConfigurationPresetFeatures::MostRestrictive,
831            AuthorizedActionTakers::NoOne,
832        );
833        let mp = p.default_marketplace_rules_v0();
834        assert_eq!(mp.trade_mode, TokenTradeMode::NotTradeable);
835    }
836
837    // --- token_configuration_v0 full config ---
838
839    #[test]
840    fn preset_token_configuration_v0_populates_fields() {
841        let taker = AuthorizedActionTakers::ContractOwner;
842        let p = preset(TokenConfigurationPresetFeatures::WithExtremeActions, taker);
843        let conventions = TokenConfigurationConvention::V0(TokenConfigurationConventionV0 {
844            localizations: Default::default(),
845            decimals: 4,
846        });
847        let config = p.token_configuration_v0(conventions, 1_000, Some(5_000), true, true);
848        assert_eq!(config.base_supply, 1_000);
849        assert_eq!(config.max_supply, Some(5_000));
850        assert_eq!(
851            config
852                .manual_minting_rules
853                .authorized_to_make_change_action_takers(),
854            &taker
855        );
856        // start_as_paused is fixed false by constructor
857        assert!(!config.start_as_paused);
858        assert!(config.allow_transfer_to_frozen_balance);
859        assert_eq!(config.main_control_group, None);
860        // extreme => main_control_group_can_be_modified becomes taker
861        assert_eq!(config.main_control_group_can_be_modified, taker);
862        // description is none
863        assert!(config.description.is_none());
864    }
865
866    #[test]
867    fn preset_token_configuration_keeps_all_history_true() {
868        let p = preset(
869            TokenConfigurationPresetFeatures::MostRestrictive,
870            AuthorizedActionTakers::NoOne,
871        );
872        let conventions = TokenConfigurationConvention::V0(TokenConfigurationConventionV0 {
873            localizations: Default::default(),
874            decimals: 8,
875        });
876        let cfg = p.token_configuration_v0(conventions, 100, None, true, false);
877        // keeps_history is TokenKeepsHistoryRules::V0; all fields should be true
878        match &cfg.keeps_history {
879            TokenKeepsHistoryRules::V0(v0) => {
880                assert!(v0.keeps_transfer_history);
881                assert!(v0.keeps_freezing_history);
882                assert!(v0.keeps_minting_history);
883                assert!(v0.keeps_burning_history);
884                assert!(v0.keeps_direct_pricing_history);
885                assert!(v0.keeps_direct_purchase_history);
886            }
887        }
888    }
889
890    #[test]
891    fn preset_token_configuration_keeps_all_history_false() {
892        let p = preset(
893            TokenConfigurationPresetFeatures::MostRestrictive,
894            AuthorizedActionTakers::NoOne,
895        );
896        let conventions = TokenConfigurationConvention::V0(TokenConfigurationConventionV0 {
897            localizations: Default::default(),
898            decimals: 8,
899        });
900        let cfg = p.token_configuration_v0(conventions, 100, None, false, false);
901        match &cfg.keeps_history {
902            TokenKeepsHistoryRules::V0(v0) => {
903                assert!(!v0.keeps_transfer_history);
904                assert!(!v0.keeps_direct_purchase_history);
905            }
906        }
907    }
908
909    // --- default_most_restrictive + with_base_supply chaining ---
910
911    #[test]
912    fn token_configuration_v0_default_most_restrictive_has_no_max_supply() {
913        let c = TokenConfigurationV0::default_most_restrictive();
914        assert_eq!(c.base_supply, 100_000);
915        assert!(c.max_supply.is_none());
916        assert_eq!(
917            c.main_control_group_can_be_modified,
918            AuthorizedActionTakers::NoOne
919        );
920    }
921
922    #[test]
923    fn token_configuration_v0_with_base_supply_overrides_value() {
924        let c = TokenConfigurationV0::default_most_restrictive().with_base_supply(42);
925        assert_eq!(c.base_supply, 42);
926    }
927
928    // --- Display trait ---
929
930    #[test]
931    fn display_token_configuration_v0_contains_key_fields() {
932        let c = TokenConfigurationV0::default_most_restrictive();
933        let s = format!("{}", c);
934        assert!(s.contains("TokenConfigurationV0"));
935        assert!(s.contains("base_supply"));
936        assert!(s.contains("main_control_group"));
937    }
938
939    // --- all_used_group_positions: the interesting branches ---
940
941    #[test]
942    fn all_used_group_positions_empty_when_no_groups_referenced() {
943        let c = TokenConfigurationV0::default_most_restrictive();
944        let (positions, uses_main) = c.all_used_group_positions();
945        assert!(positions.is_empty());
946        assert!(!uses_main);
947    }
948
949    #[test]
950    fn all_used_group_positions_collects_from_group_variant_in_rules() {
951        let mut c = TokenConfigurationV0::default_most_restrictive();
952        c.freeze_rules = ChangeControlRules::V0(ChangeControlRulesV0 {
953            authorized_to_make_change: AuthorizedActionTakers::Group(7),
954            admin_action_takers: AuthorizedActionTakers::Group(9),
955            changing_authorized_action_takers_to_no_one_allowed: false,
956            changing_admin_action_takers_to_no_one_allowed: false,
957            self_changing_admin_action_takers_allowed: false,
958        });
959        let (positions, uses_main) = c.all_used_group_positions();
960        assert!(positions.contains(&7));
961        assert!(positions.contains(&9));
962        assert!(!uses_main);
963    }
964
965    #[test]
966    fn all_used_group_positions_flags_main_group_usage() {
967        let mut c = TokenConfigurationV0::default_most_restrictive();
968        c.emergency_action_rules = ChangeControlRules::V0(ChangeControlRulesV0 {
969            authorized_to_make_change: AuthorizedActionTakers::MainGroup,
970            admin_action_takers: AuthorizedActionTakers::NoOne,
971            changing_authorized_action_takers_to_no_one_allowed: false,
972            changing_admin_action_takers_to_no_one_allowed: false,
973            self_changing_admin_action_takers_allowed: false,
974        });
975        let (_, uses_main) = c.all_used_group_positions();
976        assert!(uses_main);
977    }
978
979    #[test]
980    fn all_used_group_positions_includes_main_control_group() {
981        let mut c = TokenConfigurationV0::default_most_restrictive();
982        c.main_control_group = Some(42);
983        let (positions, _) = c.all_used_group_positions();
984        assert!(positions.contains(&42));
985    }
986
987    #[test]
988    fn all_used_group_positions_includes_positions_from_main_control_group_can_be_modified() {
989        let mut c = TokenConfigurationV0::default_most_restrictive();
990        c.main_control_group_can_be_modified = AuthorizedActionTakers::Group(11);
991        let (positions, _) = c.all_used_group_positions();
992        assert!(positions.contains(&11));
993    }
994
995    #[test]
996    fn all_used_group_positions_ignores_contract_owner_and_identity_and_no_one() {
997        let mut c = TokenConfigurationV0::default_most_restrictive();
998        c.manual_minting_rules = ChangeControlRules::V0(ChangeControlRulesV0 {
999            authorized_to_make_change: AuthorizedActionTakers::ContractOwner,
1000            admin_action_takers: AuthorizedActionTakers::Identity(Identifier::from([1u8; 32])),
1001            changing_authorized_action_takers_to_no_one_allowed: false,
1002            changing_admin_action_takers_to_no_one_allowed: false,
1003            self_changing_admin_action_takers_allowed: false,
1004        });
1005        let (positions, uses_main) = c.all_used_group_positions();
1006        assert!(positions.is_empty());
1007        assert!(!uses_main);
1008    }
1009
1010    // --- all_change_control_rules ---
1011
1012    #[test]
1013    fn all_change_control_rules_returns_expected_rule_names() {
1014        let c = TokenConfigurationV0::default_most_restrictive();
1015        let rules = c.all_change_control_rules();
1016        let names: Vec<&str> = rules.iter().map(|(name, _)| *name).collect();
1017        assert!(names.contains(&"max_supply_change_rules"));
1018        assert!(names.contains(&"conventions_change_rules"));
1019        assert!(names.contains(&"manual_minting_rules"));
1020        assert!(names.contains(&"manual_burning_rules"));
1021        assert!(names.contains(&"freeze_rules"));
1022        assert!(names.contains(&"unfreeze_rules"));
1023        assert!(names.contains(&"destroy_frozen_funds_rules"));
1024        assert!(names.contains(&"emergency_action_rules"));
1025        assert!(names.contains(&"trade_mode_change_rules"));
1026        // 13 rules total per the implementation
1027        assert_eq!(rules.len(), 13);
1028    }
1029
1030    // --- setters exercise the right fields ---
1031
1032    #[test]
1033    fn setters_set_description_max_supply_base_supply_main_control_group() {
1034        let mut c = TokenConfigurationV0::default_most_restrictive();
1035        c.set_description(Some("my token".to_string()));
1036        c.set_max_supply(Some(999));
1037        c.set_base_supply(77);
1038        c.set_main_control_group(Some(3));
1039        c.set_start_as_paused(true);
1040        c.allow_transfer_to_frozen_balance(false);
1041        c.set_main_control_group_can_be_modified(AuthorizedActionTakers::ContractOwner);
1042        assert_eq!(c.description(), &Some("my token".to_string()));
1043        assert_eq!(c.max_supply(), Some(999));
1044        assert_eq!(c.base_supply(), 77);
1045        assert_eq!(c.main_control_group(), Some(3));
1046        assert!(c.start_as_paused());
1047        assert!(!c.is_allowed_transfer_to_frozen_balance());
1048        assert_eq!(
1049            c.main_control_group_can_be_modified(),
1050            &AuthorizedActionTakers::ContractOwner
1051        );
1052    }
1053}