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