1use crate::balances::credits::TokenAmount;
2use crate::data_contract::associated_token::token_configuration_convention::TokenConfigurationConvention;
3use crate::data_contract::associated_token::token_marketplace_rules::v0::TokenTradeMode;
4use crate::data_contract::associated_token::token_perpetual_distribution::TokenPerpetualDistribution;
5use crate::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers;
6use crate::data_contract::GroupContractPosition;
7use crate::ProtocolError;
8use bincode::Encode;
9use platform_serialization::de::Decode;
10use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize};
11use platform_value::Identifier;
12#[cfg(feature = "serde-conversion")]
13use serde::{Deserialize, Serialize};
14use std::fmt;
15
16#[derive(
17 Debug,
18 Clone,
19 Default,
20 PartialOrd,
21 Encode,
22 Decode,
23 PlatformSerialize,
24 PlatformDeserialize,
25 PartialEq,
26 Eq,
27)]
28#[cfg_attr(
29 feature = "serde-conversion",
30 derive(Serialize, Deserialize),
31 serde(rename_all = "camelCase")
32)]
33pub enum TokenConfigurationChangeItem {
34 #[default]
35 TokenConfigurationNoChange,
36 Conventions(TokenConfigurationConvention),
37 ConventionsControlGroup(AuthorizedActionTakers),
38 ConventionsAdminGroup(AuthorizedActionTakers),
39 MaxSupply(Option<TokenAmount>),
40 MaxSupplyControlGroup(AuthorizedActionTakers),
41 MaxSupplyAdminGroup(AuthorizedActionTakers),
42 PerpetualDistribution(Option<TokenPerpetualDistribution>),
43 PerpetualDistributionControlGroup(AuthorizedActionTakers),
44 PerpetualDistributionAdminGroup(AuthorizedActionTakers),
45 NewTokensDestinationIdentity(Option<Identifier>),
46 NewTokensDestinationIdentityControlGroup(AuthorizedActionTakers),
47 NewTokensDestinationIdentityAdminGroup(AuthorizedActionTakers),
48 MintingAllowChoosingDestination(bool),
49 MintingAllowChoosingDestinationControlGroup(AuthorizedActionTakers),
50 MintingAllowChoosingDestinationAdminGroup(AuthorizedActionTakers),
51 ManualMinting(AuthorizedActionTakers),
52 ManualMintingAdminGroup(AuthorizedActionTakers),
53 ManualBurning(AuthorizedActionTakers),
54 ManualBurningAdminGroup(AuthorizedActionTakers),
55 Freeze(AuthorizedActionTakers),
56 FreezeAdminGroup(AuthorizedActionTakers),
57 Unfreeze(AuthorizedActionTakers),
58 UnfreezeAdminGroup(AuthorizedActionTakers),
59 DestroyFrozenFunds(AuthorizedActionTakers),
60 DestroyFrozenFundsAdminGroup(AuthorizedActionTakers),
61 EmergencyAction(AuthorizedActionTakers),
62 EmergencyActionAdminGroup(AuthorizedActionTakers),
63 MarketplaceTradeMode(TokenTradeMode),
64 MarketplaceTradeModeControlGroup(AuthorizedActionTakers),
65 MarketplaceTradeModeAdminGroup(AuthorizedActionTakers),
66 MainControlGroup(Option<GroupContractPosition>),
67}
68impl TokenConfigurationChangeItem {
69 pub fn payload_serialization(&self) -> Result<Option<Vec<u8>>, ProtocolError> {
70 Ok(match self {
71 TokenConfigurationChangeItem::TokenConfigurationNoChange => None,
72 TokenConfigurationChangeItem::Conventions(convention) => Some(
73 bincode::encode_to_vec(convention, bincode::config::standard())
74 .map_err(|e| ProtocolError::EncodingError(e.to_string()))?,
75 ),
76 TokenConfigurationChangeItem::ConventionsControlGroup(a)
77 | TokenConfigurationChangeItem::ConventionsAdminGroup(a)
78 | TokenConfigurationChangeItem::MaxSupplyControlGroup(a)
79 | TokenConfigurationChangeItem::MaxSupplyAdminGroup(a)
80 | TokenConfigurationChangeItem::PerpetualDistributionControlGroup(a)
81 | TokenConfigurationChangeItem::PerpetualDistributionAdminGroup(a)
82 | TokenConfigurationChangeItem::NewTokensDestinationIdentityControlGroup(a)
83 | TokenConfigurationChangeItem::NewTokensDestinationIdentityAdminGroup(a)
84 | TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup(a)
85 | TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup(a)
86 | TokenConfigurationChangeItem::ManualMinting(a)
87 | TokenConfigurationChangeItem::ManualMintingAdminGroup(a)
88 | TokenConfigurationChangeItem::ManualBurning(a)
89 | TokenConfigurationChangeItem::ManualBurningAdminGroup(a)
90 | TokenConfigurationChangeItem::Freeze(a)
91 | TokenConfigurationChangeItem::FreezeAdminGroup(a)
92 | TokenConfigurationChangeItem::Unfreeze(a)
93 | TokenConfigurationChangeItem::UnfreezeAdminGroup(a)
94 | TokenConfigurationChangeItem::DestroyFrozenFunds(a)
95 | TokenConfigurationChangeItem::DestroyFrozenFundsAdminGroup(a)
96 | TokenConfigurationChangeItem::EmergencyAction(a)
97 | TokenConfigurationChangeItem::EmergencyActionAdminGroup(a)
98 | TokenConfigurationChangeItem::MarketplaceTradeModeControlGroup(a)
99 | TokenConfigurationChangeItem::MarketplaceTradeModeAdminGroup(a) => Some(a.to_bytes()),
100 TokenConfigurationChangeItem::MaxSupply(max_supply) => {
101 max_supply.map(|amount| amount.to_be_bytes().to_vec())
102 }
103 TokenConfigurationChangeItem::PerpetualDistribution(distribution) => distribution
104 .as_ref()
105 .map(|dist| {
106 bincode::encode_to_vec(dist, bincode::config::standard())
107 .map_err(|e| ProtocolError::EncodingError(e.to_string()))
108 })
109 .transpose()?,
110 TokenConfigurationChangeItem::NewTokensDestinationIdentity(identity) => {
111 identity.map(|id| id.to_vec())
112 }
113 TokenConfigurationChangeItem::MintingAllowChoosingDestination(allow) => {
114 Some(vec![*allow as u8])
115 }
116 TokenConfigurationChangeItem::MarketplaceTradeMode(mode) => Some(
117 bincode::encode_to_vec(mode, bincode::config::standard())
118 .map_err(|e| ProtocolError::EncodingError(e.to_string()))?,
119 ),
120 TokenConfigurationChangeItem::MainControlGroup(position) => {
121 position.map(|pos| pos.to_be_bytes().to_vec())
122 }
123 })
124 }
125 pub fn u8_item_index(&self) -> u8 {
126 match self {
127 TokenConfigurationChangeItem::TokenConfigurationNoChange => 0,
128 TokenConfigurationChangeItem::Conventions(_) => 1,
129 TokenConfigurationChangeItem::ConventionsControlGroup(_) => 2,
130 TokenConfigurationChangeItem::ConventionsAdminGroup(_) => 3,
131 TokenConfigurationChangeItem::MaxSupply(_) => 4,
132 TokenConfigurationChangeItem::MaxSupplyControlGroup(_) => 5,
133 TokenConfigurationChangeItem::MaxSupplyAdminGroup(_) => 6,
134 TokenConfigurationChangeItem::PerpetualDistribution(_) => 7,
135 TokenConfigurationChangeItem::PerpetualDistributionControlGroup(_) => 8,
136 TokenConfigurationChangeItem::PerpetualDistributionAdminGroup(_) => 9,
137 TokenConfigurationChangeItem::NewTokensDestinationIdentity(_) => 10,
138 TokenConfigurationChangeItem::NewTokensDestinationIdentityControlGroup(_) => 11,
139 TokenConfigurationChangeItem::NewTokensDestinationIdentityAdminGroup(_) => 12,
140 TokenConfigurationChangeItem::MintingAllowChoosingDestination(_) => 13,
141 TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup(_) => 14,
142 TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup(_) => 15,
143 TokenConfigurationChangeItem::ManualMinting(_) => 16,
144 TokenConfigurationChangeItem::ManualMintingAdminGroup(_) => 17,
145 TokenConfigurationChangeItem::ManualBurning(_) => 18,
146 TokenConfigurationChangeItem::ManualBurningAdminGroup(_) => 19,
147 TokenConfigurationChangeItem::Freeze(_) => 20,
148 TokenConfigurationChangeItem::FreezeAdminGroup(_) => 21,
149 TokenConfigurationChangeItem::Unfreeze(_) => 22,
150 TokenConfigurationChangeItem::UnfreezeAdminGroup(_) => 23,
151 TokenConfigurationChangeItem::DestroyFrozenFunds(_) => 24,
152 TokenConfigurationChangeItem::DestroyFrozenFundsAdminGroup(_) => 25,
153 TokenConfigurationChangeItem::EmergencyAction(_) => 26,
154 TokenConfigurationChangeItem::EmergencyActionAdminGroup(_) => 27,
155 TokenConfigurationChangeItem::MarketplaceTradeMode(_) => 28,
156 TokenConfigurationChangeItem::MarketplaceTradeModeControlGroup(_) => 29,
157 TokenConfigurationChangeItem::MarketplaceTradeModeAdminGroup(_) => 30,
158 TokenConfigurationChangeItem::MainControlGroup(_) => 31,
159 }
160 }
161}
162
163impl fmt::Display for TokenConfigurationChangeItem {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 match self {
166 TokenConfigurationChangeItem::TokenConfigurationNoChange => {
167 write!(f, "No Change in Token Configuration")
168 }
169 TokenConfigurationChangeItem::Conventions(convention) => {
170 write!(f, "Conventions: {}", convention)
171 }
172 TokenConfigurationChangeItem::ConventionsControlGroup(control_group) => {
173 write!(f, "Conventions Control Group: {}", control_group)
174 }
175 TokenConfigurationChangeItem::ConventionsAdminGroup(admin_group) => {
176 write!(f, "Conventions Admin Group: {}", admin_group)
177 }
178 TokenConfigurationChangeItem::MaxSupply(max_supply) => match max_supply {
179 Some(amount) => write!(f, "Max Supply: {}", amount),
180 None => write!(f, "Max Supply: No Limit"),
181 },
182 TokenConfigurationChangeItem::MaxSupplyControlGroup(control_group) => {
183 write!(f, "Max Supply Control Group: {}", control_group)
184 }
185 TokenConfigurationChangeItem::MaxSupplyAdminGroup(admin_group) => {
186 write!(f, "Max Supply Admin Group: {}", admin_group)
187 }
188 TokenConfigurationChangeItem::PerpetualDistribution(distribution) => match distribution
189 {
190 Some(dist) => write!(f, "Perpetual Distribution: {}", dist),
191 None => write!(f, "Perpetual Distribution: None"),
192 },
193 TokenConfigurationChangeItem::PerpetualDistributionControlGroup(control_group) => {
194 write!(f, "Perpetual Distribution Control Group: {}", control_group)
195 }
196 TokenConfigurationChangeItem::PerpetualDistributionAdminGroup(admin_group) => {
197 write!(f, "Perpetual Distribution Admin Group: {}", admin_group)
198 }
199 TokenConfigurationChangeItem::NewTokensDestinationIdentity(identity) => {
200 match identity {
201 Some(id) => write!(f, "New Tokens Destination Identity: {}", id),
202 None => write!(f, "New Tokens Destination Identity: None"),
203 }
204 }
205 TokenConfigurationChangeItem::NewTokensDestinationIdentityControlGroup(
206 control_group,
207 ) => {
208 write!(
209 f,
210 "New Tokens Destination Identity Control Group: {}",
211 control_group
212 )
213 }
214 TokenConfigurationChangeItem::NewTokensDestinationIdentityAdminGroup(admin_group) => {
215 write!(
216 f,
217 "New Tokens Destination Identity Admin Group: {}",
218 admin_group
219 )
220 }
221 TokenConfigurationChangeItem::MintingAllowChoosingDestination(allow) => {
222 write!(f, "Minting Allow Choosing Destination: {}", allow)
223 }
224 TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup(
225 control_group,
226 ) => {
227 write!(
228 f,
229 "Minting Allow Choosing Destination Control Group: {}",
230 control_group
231 )
232 }
233 TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup(
234 admin_group,
235 ) => {
236 write!(
237 f,
238 "Minting Allow Choosing Destination Admin Group: {}",
239 admin_group
240 )
241 }
242 TokenConfigurationChangeItem::ManualMinting(control_group) => {
243 write!(f, "Manual Minting: {}", control_group)
244 }
245 TokenConfigurationChangeItem::ManualMintingAdminGroup(admin_group) => {
246 write!(f, "Manual Minting Admin Group: {}", admin_group)
247 }
248 TokenConfigurationChangeItem::ManualBurning(control_group) => {
249 write!(f, "Manual Burning: {}", control_group)
250 }
251 TokenConfigurationChangeItem::ManualBurningAdminGroup(admin_group) => {
252 write!(f, "Manual Burning Admin Group: {}", admin_group)
253 }
254 TokenConfigurationChangeItem::Freeze(control_group) => {
255 write!(f, "Freeze: {}", control_group)
256 }
257 TokenConfigurationChangeItem::FreezeAdminGroup(admin_group) => {
258 write!(f, "Freeze Admin Group: {}", admin_group)
259 }
260 TokenConfigurationChangeItem::Unfreeze(control_group) => {
261 write!(f, "Unfreeze: {}", control_group)
262 }
263 TokenConfigurationChangeItem::UnfreezeAdminGroup(admin_group) => {
264 write!(f, "Unfreeze Admin Group: {}", admin_group)
265 }
266 TokenConfigurationChangeItem::DestroyFrozenFunds(control_group) => {
267 write!(f, "Destroy Frozen Funds: {}", control_group)
268 }
269 TokenConfigurationChangeItem::DestroyFrozenFundsAdminGroup(admin_group) => {
270 write!(f, "Destroy Frozen Funds Admin Group: {}", admin_group)
271 }
272 TokenConfigurationChangeItem::EmergencyAction(control_group) => {
273 write!(f, "Emergency Action: {}", control_group)
274 }
275 TokenConfigurationChangeItem::EmergencyActionAdminGroup(admin_group) => {
276 write!(f, "Emergency Action Admin Group: {}", admin_group)
277 }
278 TokenConfigurationChangeItem::MainControlGroup(position) => match position {
279 Some(pos) => write!(f, "Main Control Group: {}", pos),
280 None => write!(f, "Main Control Group: None"),
281 },
282 TokenConfigurationChangeItem::MarketplaceTradeMode(mode) => {
283 write!(f, "Marketplace Trade Mode: {:?}", mode)
284 }
285 TokenConfigurationChangeItem::MarketplaceTradeModeControlGroup(control_group) => {
286 write!(f, "Marketplace Trade Mode Control Group: {}", control_group)
287 }
288 TokenConfigurationChangeItem::MarketplaceTradeModeAdminGroup(admin_group) => {
289 write!(f, "Marketplace Trade Mode Admin Group: {}", admin_group)
290 }
291 }
292 }
293}
294
295#[cfg(test)]
296mod tests {
297 use super::*;
298 use std::collections::BTreeSet;
299
300 fn all_variants() -> Vec<TokenConfigurationChangeItem> {
302 let aat = AuthorizedActionTakers::NoOne;
303 vec![
304 TokenConfigurationChangeItem::TokenConfigurationNoChange,
305 TokenConfigurationChangeItem::Conventions(
306 TokenConfigurationConvention::V0(
307 crate::data_contract::associated_token::token_configuration_convention::v0::TokenConfigurationConventionV0::default(),
308 ),
309 ),
310 TokenConfigurationChangeItem::ConventionsControlGroup(aat),
311 TokenConfigurationChangeItem::ConventionsAdminGroup(aat),
312 TokenConfigurationChangeItem::MaxSupply(None),
313 TokenConfigurationChangeItem::MaxSupplyControlGroup(aat),
314 TokenConfigurationChangeItem::MaxSupplyAdminGroup(aat),
315 TokenConfigurationChangeItem::PerpetualDistribution(None),
316 TokenConfigurationChangeItem::PerpetualDistributionControlGroup(aat),
317 TokenConfigurationChangeItem::PerpetualDistributionAdminGroup(aat),
318 TokenConfigurationChangeItem::NewTokensDestinationIdentity(None),
319 TokenConfigurationChangeItem::NewTokensDestinationIdentityControlGroup(aat),
320 TokenConfigurationChangeItem::NewTokensDestinationIdentityAdminGroup(aat),
321 TokenConfigurationChangeItem::MintingAllowChoosingDestination(false),
322 TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup(aat),
323 TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup(aat),
324 TokenConfigurationChangeItem::ManualMinting(aat),
325 TokenConfigurationChangeItem::ManualMintingAdminGroup(aat),
326 TokenConfigurationChangeItem::ManualBurning(aat),
327 TokenConfigurationChangeItem::ManualBurningAdminGroup(aat),
328 TokenConfigurationChangeItem::Freeze(aat),
329 TokenConfigurationChangeItem::FreezeAdminGroup(aat),
330 TokenConfigurationChangeItem::Unfreeze(aat),
331 TokenConfigurationChangeItem::UnfreezeAdminGroup(aat),
332 TokenConfigurationChangeItem::DestroyFrozenFunds(aat),
333 TokenConfigurationChangeItem::DestroyFrozenFundsAdminGroup(aat),
334 TokenConfigurationChangeItem::EmergencyAction(aat),
335 TokenConfigurationChangeItem::EmergencyActionAdminGroup(aat),
336 TokenConfigurationChangeItem::MarketplaceTradeMode(TokenTradeMode::default()),
337 TokenConfigurationChangeItem::MarketplaceTradeModeControlGroup(aat),
338 TokenConfigurationChangeItem::MarketplaceTradeModeAdminGroup(aat),
339 TokenConfigurationChangeItem::MainControlGroup(None),
340 ]
341 }
342
343 #[test]
346 fn u8_item_index_values_are_unique() {
347 let variants = all_variants();
348 let indices: Vec<u8> = variants.iter().map(|v| v.u8_item_index()).collect();
349 let unique: BTreeSet<u8> = indices.iter().cloned().collect();
350 assert_eq!(
351 indices.len(),
352 unique.len(),
353 "Duplicate u8_item_index values found: {:?}",
354 indices
355 );
356 }
357
358 #[test]
359 fn u8_item_index_covers_0_through_31() {
360 let variants = all_variants();
361 let indices: BTreeSet<u8> = variants.iter().map(|v| v.u8_item_index()).collect();
362 for i in 0u8..=31 {
363 assert!(indices.contains(&i), "Missing u8_item_index value: {}", i);
364 }
365 }
366
367 #[test]
368 fn u8_item_index_all_within_range() {
369 let variants = all_variants();
370 for v in &variants {
371 let idx = v.u8_item_index();
372 assert!(idx <= 31, "Index {} exceeds expected max of 31", idx);
373 }
374 }
375
376 #[test]
377 fn u8_item_index_specific_known_values() {
378 assert_eq!(
379 TokenConfigurationChangeItem::TokenConfigurationNoChange.u8_item_index(),
380 0
381 );
382 assert_eq!(
383 TokenConfigurationChangeItem::MaxSupply(Some(100)).u8_item_index(),
384 4
385 );
386 assert_eq!(
387 TokenConfigurationChangeItem::ManualMinting(AuthorizedActionTakers::NoOne)
388 .u8_item_index(),
389 16
390 );
391 assert_eq!(
392 TokenConfigurationChangeItem::MainControlGroup(Some(5)).u8_item_index(),
393 31
394 );
395 }
396
397 #[test]
398 fn u8_item_index_variant_count() {
399 let variants = all_variants();
401 assert_eq!(variants.len(), 32);
402 }
403
404 #[test]
407 fn payload_serialization_no_change_is_none() {
408 let item = TokenConfigurationChangeItem::TokenConfigurationNoChange;
409 assert!(item.payload_serialization().unwrap().is_none());
410 }
411
412 #[test]
413 fn payload_serialization_max_supply_none_is_none() {
414 let item = TokenConfigurationChangeItem::MaxSupply(None);
415 assert!(item.payload_serialization().unwrap().is_none());
416 }
417
418 #[test]
419 fn payload_serialization_max_supply_some_encodes_be_bytes() {
420 let amount: u64 = 0x0102_0304_0506_0708;
421 let item = TokenConfigurationChangeItem::MaxSupply(Some(amount));
422 let bytes = item.payload_serialization().unwrap().unwrap();
423 assert_eq!(bytes, amount.to_be_bytes().to_vec());
424 assert_eq!(bytes.len(), 8);
425 }
426
427 #[test]
428 fn payload_serialization_new_tokens_destination_identity_none_is_none() {
429 let item = TokenConfigurationChangeItem::NewTokensDestinationIdentity(None);
430 assert!(item.payload_serialization().unwrap().is_none());
431 }
432
433 #[test]
434 fn payload_serialization_new_tokens_destination_identity_some_is_32_bytes() {
435 let id = Identifier::from([0x77u8; 32]);
436 let item = TokenConfigurationChangeItem::NewTokensDestinationIdentity(Some(id));
437 let bytes = item.payload_serialization().unwrap().unwrap();
438 assert_eq!(bytes.len(), 32);
439 assert_eq!(bytes, vec![0x77u8; 32]);
440 }
441
442 #[test]
443 fn payload_serialization_perpetual_distribution_none_is_none() {
444 let item = TokenConfigurationChangeItem::PerpetualDistribution(None);
445 assert!(item.payload_serialization().unwrap().is_none());
446 }
447
448 #[test]
449 fn payload_serialization_main_control_group_none_is_none() {
450 let item = TokenConfigurationChangeItem::MainControlGroup(None);
451 assert!(item.payload_serialization().unwrap().is_none());
452 }
453
454 #[test]
455 fn payload_serialization_main_control_group_some_is_two_be_bytes() {
456 let pos: u16 = 0xABCD;
457 let item = TokenConfigurationChangeItem::MainControlGroup(Some(pos));
458 let bytes = item.payload_serialization().unwrap().unwrap();
459 assert_eq!(bytes, pos.to_be_bytes().to_vec());
460 assert_eq!(bytes.len(), 2);
461 }
462
463 #[test]
464 fn payload_serialization_minting_allow_choosing_destination_true() {
465 let item = TokenConfigurationChangeItem::MintingAllowChoosingDestination(true);
466 let bytes = item.payload_serialization().unwrap().unwrap();
467 assert_eq!(bytes, vec![1]);
468 }
469
470 #[test]
471 fn payload_serialization_minting_allow_choosing_destination_false() {
472 let item = TokenConfigurationChangeItem::MintingAllowChoosingDestination(false);
473 let bytes = item.payload_serialization().unwrap().unwrap();
474 assert_eq!(bytes, vec![0]);
475 }
476
477 #[test]
478 fn payload_serialization_authorized_action_takers_variants_use_to_bytes() {
479 let aat = AuthorizedActionTakers::ContractOwner;
482 let expected = aat.to_bytes();
483
484 let variants = [
485 TokenConfigurationChangeItem::ConventionsControlGroup(aat),
486 TokenConfigurationChangeItem::ConventionsAdminGroup(aat),
487 TokenConfigurationChangeItem::MaxSupplyControlGroup(aat),
488 TokenConfigurationChangeItem::MaxSupplyAdminGroup(aat),
489 TokenConfigurationChangeItem::PerpetualDistributionControlGroup(aat),
490 TokenConfigurationChangeItem::PerpetualDistributionAdminGroup(aat),
491 TokenConfigurationChangeItem::NewTokensDestinationIdentityControlGroup(aat),
492 TokenConfigurationChangeItem::NewTokensDestinationIdentityAdminGroup(aat),
493 TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup(aat),
494 TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup(aat),
495 TokenConfigurationChangeItem::ManualMinting(aat),
496 TokenConfigurationChangeItem::ManualMintingAdminGroup(aat),
497 TokenConfigurationChangeItem::ManualBurning(aat),
498 TokenConfigurationChangeItem::ManualBurningAdminGroup(aat),
499 TokenConfigurationChangeItem::Freeze(aat),
500 TokenConfigurationChangeItem::FreezeAdminGroup(aat),
501 TokenConfigurationChangeItem::Unfreeze(aat),
502 TokenConfigurationChangeItem::UnfreezeAdminGroup(aat),
503 TokenConfigurationChangeItem::DestroyFrozenFunds(aat),
504 TokenConfigurationChangeItem::DestroyFrozenFundsAdminGroup(aat),
505 TokenConfigurationChangeItem::EmergencyAction(aat),
506 TokenConfigurationChangeItem::EmergencyActionAdminGroup(aat),
507 TokenConfigurationChangeItem::MarketplaceTradeModeControlGroup(aat),
508 TokenConfigurationChangeItem::MarketplaceTradeModeAdminGroup(aat),
509 ];
510 for v in &variants {
511 let bytes = v.payload_serialization().unwrap().unwrap();
512 assert_eq!(bytes, expected, "mismatch for variant {:?}", v);
513 }
514 }
515
516 #[test]
517 fn payload_serialization_conventions_roundtrips_via_bincode() {
518 use crate::data_contract::associated_token::token_configuration_convention::v0::TokenConfigurationConventionV0;
521 let convention = TokenConfigurationConvention::V0(TokenConfigurationConventionV0 {
522 localizations: Default::default(),
523 decimals: 5,
524 });
525 let item = TokenConfigurationChangeItem::Conventions(convention.clone());
526 let bytes = item.payload_serialization().unwrap().unwrap();
527 assert!(!bytes.is_empty());
528 let (decoded, _): (TokenConfigurationConvention, _) =
529 bincode::decode_from_slice(&bytes, bincode::config::standard()).unwrap();
530 assert_eq!(decoded, convention);
531 }
532
533 #[test]
534 fn payload_serialization_marketplace_trade_mode_produces_bytes() {
535 let item = TokenConfigurationChangeItem::MarketplaceTradeMode(TokenTradeMode::NotTradeable);
536 let bytes = item.payload_serialization().unwrap().unwrap();
537 assert!(!bytes.is_empty());
538 let (decoded, _): (TokenTradeMode, _) =
540 bincode::decode_from_slice(&bytes, bincode::config::standard()).unwrap();
541 assert_eq!(decoded, TokenTradeMode::NotTradeable);
542 }
543
544 #[test]
547 fn display_no_change() {
548 let s = format!(
549 "{}",
550 TokenConfigurationChangeItem::TokenConfigurationNoChange
551 );
552 assert_eq!(s, "No Change in Token Configuration");
553 }
554
555 #[test]
556 fn display_max_supply_some_vs_none() {
557 let some = format!("{}", TokenConfigurationChangeItem::MaxSupply(Some(500)));
558 let none = format!("{}", TokenConfigurationChangeItem::MaxSupply(None));
559 assert!(some.contains("500"));
560 assert!(none.contains("No Limit"));
561 }
562
563 #[test]
564 fn display_perpetual_distribution_none_uses_none_marker() {
565 let s = format!(
566 "{}",
567 TokenConfigurationChangeItem::PerpetualDistribution(None)
568 );
569 assert!(s.contains("None"));
570 }
571
572 #[test]
573 fn display_new_tokens_destination_identity_none_uses_none_marker() {
574 let s = format!(
575 "{}",
576 TokenConfigurationChangeItem::NewTokensDestinationIdentity(None)
577 );
578 assert!(s.contains("None"));
579 }
580
581 #[test]
582 fn display_new_tokens_destination_identity_some_includes_id() {
583 let id = Identifier::from([3u8; 32]);
584 let s = format!(
585 "{}",
586 TokenConfigurationChangeItem::NewTokensDestinationIdentity(Some(id))
587 );
588 assert!(s.contains("New Tokens Destination Identity"));
589 }
590
591 #[test]
592 fn display_main_control_group_none_uses_none_marker() {
593 let s = format!("{}", TokenConfigurationChangeItem::MainControlGroup(None));
594 assert!(s.contains("None"));
595 }
596
597 #[test]
598 fn display_main_control_group_some_contains_position() {
599 let s = format!(
600 "{}",
601 TokenConfigurationChangeItem::MainControlGroup(Some(7))
602 );
603 assert!(s.contains("7"));
604 }
605
606 #[test]
607 fn display_minting_allow_choosing_destination_contains_value() {
608 let s_true = format!(
609 "{}",
610 TokenConfigurationChangeItem::MintingAllowChoosingDestination(true)
611 );
612 let s_false = format!(
613 "{}",
614 TokenConfigurationChangeItem::MintingAllowChoosingDestination(false)
615 );
616 assert!(s_true.contains("true"));
617 assert!(s_false.contains("false"));
618 }
619
620 #[test]
621 fn display_marketplace_trade_mode_uses_debug() {
622 let s = format!(
623 "{}",
624 TokenConfigurationChangeItem::MarketplaceTradeMode(TokenTradeMode::NotTradeable)
625 );
626 assert!(s.contains("NotTradeable"));
627 }
628
629 #[test]
630 fn display_action_takers_group_variants() {
631 let aat = AuthorizedActionTakers::ContractOwner;
634 let cases = vec![
635 (
636 format!("{}", TokenConfigurationChangeItem::ManualMinting(aat)),
637 "Manual Minting",
638 ),
639 (
640 format!(
641 "{}",
642 TokenConfigurationChangeItem::ManualMintingAdminGroup(aat)
643 ),
644 "Manual Minting Admin Group",
645 ),
646 (
647 format!("{}", TokenConfigurationChangeItem::ManualBurning(aat)),
648 "Manual Burning",
649 ),
650 (
651 format!("{}", TokenConfigurationChangeItem::Freeze(aat)),
652 "Freeze",
653 ),
654 (
655 format!("{}", TokenConfigurationChangeItem::Unfreeze(aat)),
656 "Unfreeze",
657 ),
658 (
659 format!("{}", TokenConfigurationChangeItem::DestroyFrozenFunds(aat)),
660 "Destroy Frozen Funds",
661 ),
662 (
663 format!("{}", TokenConfigurationChangeItem::EmergencyAction(aat)),
664 "Emergency Action",
665 ),
666 (
667 format!(
668 "{}",
669 TokenConfigurationChangeItem::MarketplaceTradeModeControlGroup(aat)
670 ),
671 "Marketplace Trade Mode Control Group",
672 ),
673 (
674 format!(
675 "{}",
676 TokenConfigurationChangeItem::ConventionsControlGroup(aat)
677 ),
678 "Conventions Control Group",
679 ),
680 ];
681 for (output, expected_prefix) in cases {
682 assert!(
683 output.contains(expected_prefix),
684 "expected {:?} in {:?}",
685 expected_prefix,
686 output
687 );
688 }
689 }
690
691 #[test]
694 fn equality_same_variant_same_data() {
695 let a = TokenConfigurationChangeItem::MaxSupply(Some(42));
696 let b = TokenConfigurationChangeItem::MaxSupply(Some(42));
697 assert_eq!(a, b);
698 }
699
700 #[test]
701 fn equality_same_variant_different_data_unequal() {
702 let a = TokenConfigurationChangeItem::MaxSupply(Some(42));
703 let b = TokenConfigurationChangeItem::MaxSupply(Some(43));
704 assert_ne!(a, b);
705 }
706
707 #[test]
708 fn equality_different_variants_unequal() {
709 let a = TokenConfigurationChangeItem::MaxSupply(None);
710 let b = TokenConfigurationChangeItem::MainControlGroup(None);
711 assert_ne!(a, b);
712 }
713
714 #[test]
715 fn equality_authorized_action_takers_sensitivity() {
716 let a = TokenConfigurationChangeItem::ManualMinting(AuthorizedActionTakers::NoOne);
717 let b = TokenConfigurationChangeItem::ManualMinting(AuthorizedActionTakers::ContractOwner);
718 assert_ne!(a, b);
719 }
720
721 #[test]
722 fn equality_bool_variant_distinguishes_values() {
723 let a = TokenConfigurationChangeItem::MintingAllowChoosingDestination(true);
724 let b = TokenConfigurationChangeItem::MintingAllowChoosingDestination(false);
725 assert_ne!(a, b);
726 }
727
728 #[test]
731 fn default_is_no_change() {
732 let d: TokenConfigurationChangeItem = Default::default();
733 assert_eq!(d, TokenConfigurationChangeItem::TokenConfigurationNoChange);
734 }
735
736 #[test]
737 fn clone_preserves_variant_and_data() {
738 let aat = AuthorizedActionTakers::Identity(Identifier::from([5u8; 32]));
739 let a = TokenConfigurationChangeItem::Freeze(aat);
740 let b = a.clone();
741 assert_eq!(a, b);
742 }
743
744 #[test]
747 fn debug_trait_contains_variant_name() {
748 let a = TokenConfigurationChangeItem::ManualMinting(AuthorizedActionTakers::NoOne);
749 let dbg = format!("{:?}", a);
750 assert!(dbg.contains("ManualMinting"));
751 }
752}