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