git.delta.rocks / unique-network / refs/commits / 1f98ccc154b2

difftreelog

Merge pull request #414 from UniqueNetwork/feature/prop-check-root-owner

kozyrevdev2022-07-05parents: #6d9bbad #abb3797.patch.diff
in: master
Feature/prop check root owner

9 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
1311 sender: T::CrossAccountId,1311 sender: T::CrossAccountId,
1312 token_id: TokenId,1312 token_id: TokenId,
1313 property: Vec<Property>,1313 property: Vec<Property>,
1314 nesting_budget: &dyn Budget,
1314 ) -> DispatchResultWithPostInfo;1315 ) -> DispatchResultWithPostInfo;
1315 fn delete_token_properties(1316 fn delete_token_properties(
1316 &self,1317 &self,
1317 sender: T::CrossAccountId,1318 sender: T::CrossAccountId,
1318 token_id: TokenId,1319 token_id: TokenId,
1319 property_keys: Vec<PropertyKey>,1320 property_keys: Vec<PropertyKey>,
1321 nesting_budget: &dyn Budget,
1320 ) -> DispatchResultWithPostInfo;1322 ) -> DispatchResultWithPostInfo;
1321 fn set_token_property_permissions(1323 fn set_token_property_permissions(
1322 &self,1324 &self,
1361 sender: T::CrossAccountId,1363 sender: T::CrossAccountId,
1362 from: (CollectionId, TokenId),1364 from: (CollectionId, TokenId),
1363 under: TokenId,1365 under: TokenId,
1364 budget: &dyn Budget,1366 nesting_budget: &dyn Budget,
1365 ) -> DispatchResult;1367 ) -> DispatchResult;
13661368
1367 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));1369 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
298 _sender: T::CrossAccountId,298 _sender: T::CrossAccountId,
299 _token_id: TokenId,299 _token_id: TokenId,
300 _property: Vec<Property>,300 _property: Vec<Property>,
301 _nesting_budget: &dyn Budget,
301 ) -> DispatchResultWithPostInfo {302 ) -> DispatchResultWithPostInfo {
302 fail!(<Error<T>>::SettingPropertiesNotAllowed)303 fail!(<Error<T>>::SettingPropertiesNotAllowed)
303 }304 }
315 _sender: T::CrossAccountId,316 _sender: T::CrossAccountId,
316 _token_id: TokenId,317 _token_id: TokenId,
317 _property_keys: Vec<PropertyKey>,318 _property_keys: Vec<PropertyKey>,
319 _nesting_budget: &dyn Budget,
318 ) -> DispatchResultWithPostInfo {320 ) -> DispatchResultWithPostInfo {
319 fail!(<Error<T>>::SettingPropertiesNotAllowed)321 fail!(<Error<T>>::SettingPropertiesNotAllowed)
320 }322 }
324 _sender: <T>::CrossAccountId,326 _sender: <T>::CrossAccountId,
325 _from: (CollectionId, TokenId),327 _from: (CollectionId, TokenId),
326 _under: TokenId,328 _under: TokenId,
327 _budget: &dyn Budget,329 _nesting_budget: &dyn Budget,
328 ) -> sp_runtime::DispatchResult {330 ) -> sp_runtime::DispatchResult {
329 fail!(<Error<T>>::FungibleDisallowsNesting)331 fail!(<Error<T>>::FungibleDisallowsNesting)
330 }332 }
modifiedpallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth
183 value: property_value(),183 value: property_value(),
184 }).collect::<Vec<_>>();184 }).collect::<Vec<_>>();
185 let item = create_max_item(&collection, &owner, owner.clone())?;185 let item = create_max_item(&collection, &owner, owner.clone())?;
186 }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props, false)?}186 }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props, false, &Unlimited)?}
187187
188 delete_token_properties {188 delete_token_properties {
189 let b in 0..MAX_PROPERTIES_PER_ITEM;189 let b in 0..MAX_PROPERTIES_PER_ITEM;
205 value: property_value(),205 value: property_value(),
206 }).collect::<Vec<_>>();206 }).collect::<Vec<_>>();
207 let item = create_max_item(&collection, &owner, owner.clone())?;207 let item = create_max_item(&collection, &owner, owner.clone())?;
208 <Pallet<T>>::set_token_properties(&collection, &owner, item, props, false)?;208 <Pallet<T>>::set_token_properties(&collection, &owner, item, props, false, &Unlimited)?;
209 let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();209 let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();
210 }: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete)?}210 }: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete, &Unlimited)?}
211}211}
212212
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
220 sender: T::CrossAccountId,220 sender: T::CrossAccountId,
221 token_id: TokenId,221 token_id: TokenId,
222 properties: Vec<Property>,222 properties: Vec<Property>,
223 nesting_budget: &dyn Budget,
223 ) -> DispatchResultWithPostInfo {224 ) -> DispatchResultWithPostInfo {
224 let weight = <CommonWeights<T>>::set_token_properties(properties.len() as u32);225 let weight = <CommonWeights<T>>::set_token_properties(properties.len() as u32);
225226
226 with_weight(227 with_weight(
227 <Pallet<T>>::set_token_properties(self, &sender, token_id, properties, false),228 <Pallet<T>>::set_token_properties(
229 self,
230 &sender,
231 token_id,
232 properties.into_iter(),
233 false,
234 nesting_budget,
235 ),
228 weight,236 weight,
229 )237 )
234 sender: T::CrossAccountId,242 sender: T::CrossAccountId,
235 token_id: TokenId,243 token_id: TokenId,
236 property_keys: Vec<PropertyKey>,244 property_keys: Vec<PropertyKey>,
245 nesting_budget: &dyn Budget,
237 ) -> DispatchResultWithPostInfo {246 ) -> DispatchResultWithPostInfo {
238 let weight = <CommonWeights<T>>::delete_token_properties(property_keys.len() as u32);247 let weight = <CommonWeights<T>>::delete_token_properties(property_keys.len() as u32);
239248
240 with_weight(249 with_weight(
241 <Pallet<T>>::delete_token_properties(self, &sender, token_id, property_keys),250 <Pallet<T>>::delete_token_properties(
251 self,
252 &sender,
253 token_id,
254 property_keys.into_iter(),
255 nesting_budget,
256 ),
242 weight,257 weight,
243 )258 )
368 sender: T::CrossAccountId,383 sender: T::CrossAccountId,
369 from: (CollectionId, TokenId),384 from: (CollectionId, TokenId),
370 under: TokenId,385 under: TokenId,
371 budget: &dyn Budget,386 nesting_budget: &dyn Budget,
372 ) -> sp_runtime::DispatchResult {387 ) -> sp_runtime::DispatchResult {
373 <Pallet<T>>::check_nesting(self, sender, from, under, budget)388 <Pallet<T>>::check_nesting(self, sender, from, under, nesting_budget)
374 }389 }
375390
376 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId)) {391 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId)) {
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
82 .map_err(|_| "key too long")?;82 .map_err(|_| "key too long")?;
83 let value = value.try_into().map_err(|_| "value too long")?;83 let value = value.try_into().map_err(|_| "value too long")?;
84
85 let nesting_budget = self
86 .recorder
87 .weight_calls_budget(<StructureWeight<T>>::find_parent());
8488
85 <Pallet<T>>::set_token_property(89 <Pallet<T>>::set_token_property(
86 self,90 self,
87 &caller,91 &caller,
88 TokenId(token_id),92 TokenId(token_id),
89 Property { key, value },93 Property { key, value },
90 false,94 &nesting_budget,
91 )95 )
92 .map_err(dispatch_to_evm::<T>)96 .map_err(dispatch_to_evm::<T>)
93 }97 }
99 .try_into()103 .try_into()
100 .map_err(|_| "key too long")?;104 .map_err(|_| "key too long")?;
105
106 let nesting_budget = self
107 .recorder
108 .weight_calls_budget(<StructureWeight<T>>::find_parent());
101109
102 <Pallet<T>>::delete_token_property(self, &caller, TokenId(token_id), key)110 <Pallet<T>>::delete_token_property(self, &caller, TokenId(token_id), key, &nesting_budget)
103 .map_err(dispatch_to_evm::<T>)111 .map_err(dispatch_to_evm::<T>)
104 }112 }
105113
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
28use up_data_structs::{28use up_data_structs::{
29 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,29 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,
30 mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, PropertyKey,30 mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, PropertyKey,
31 PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild, AuxPropertyValue,31 PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild,
32 AuxPropertyValue,
32};33};
33use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};34use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
480 })481 })
481 }482 }
482483
484 #[transactional]
483 pub fn set_token_property(485 fn modify_token_properties(
484 collection: &NonfungibleHandle<T>,486 collection: &NonfungibleHandle<T>,
485 sender: &T::CrossAccountId,487 sender: &T::CrossAccountId,
486 token_id: TokenId,488 token_id: TokenId,
487 property: Property,489 properties: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,
488 is_token_create: bool,490 is_token_create: bool,
491 nesting_budget: &dyn Budget,
489 ) -> DispatchResult {492 ) -> DispatchResult {
493 let mut collection_admin_status = None;
494 let mut token_owner_result = None;
495
496 let mut is_collection_admin =
497 || *collection_admin_status.get_or_insert_with(|| collection.is_owner_or_admin(sender));
498
499 let mut is_token_owner = || {
490 Self::check_token_change_permission(500 *token_owner_result.get_or_insert_with(|| -> Result<bool, DispatchError> {
491 collection,501 let is_owned = <PalletStructure<T>>::check_indirectly_owned(
492 sender,502 sender.clone(),
503 collection.id,
493 token_id,504 token_id,
494 &property.key,505 None,
495 is_token_create,506 nesting_budget,
507 )?;
508
509 Ok(is_owned)
496 )?;510 })
497511 };
512
513 for (key, value) in properties {
514 let permission = <PalletCommon<T>>::property_permissions(collection.id)
515 .get(&key)
516 .cloned()
517 .unwrap_or_else(PropertyPermission::none);
518
519 let is_property_exists = TokenProperties::<T>::get((collection.id, token_id))
520 .get(&key)
521 .is_some();
522
523 match permission {
524 PropertyPermission { mutable: false, .. } if is_property_exists => {
525 return Err(<CommonError<T>>::NoPermission.into());
526 }
527
528 PropertyPermission {
529 collection_admin,
530 token_owner,
531 ..
532 } => {
533 //TODO: investigate threats during public minting.
534 if is_token_create && (collection_admin || token_owner) && value.is_some() {
535 // Pass
536 } else if collection_admin && is_collection_admin() {
537 // Pass
538 } else if token_owner && is_token_owner()? {
539 // Pass
540 } else {
541 fail!(<CommonError<T>>::NoPermission);
542 }
543 }
544 }
545
546 match value {
547 Some(value) => {
498 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {548 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {
499 let property = property.clone();
500 properties.try_set(property.key, property.value)549 properties.try_set(key.clone(), value)
501 })550 })
551 .map_err(<CommonError<T>>::from)?;
552
502 .map_err(<CommonError<T>>::from)?;553 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(
503554 collection.id,
504 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(555 token_id,
505 collection.id,556 key,
506 token_id,557 ));
507 property.key,558 }
508 ));559 None => {
560 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {
561 properties.remove(&key)
562 })
563 .map_err(<CommonError<T>>::from)?;
564
565 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(
566 collection.id,
567 token_id,
568 key,
569 ));
570 }
571 }
572 }
509573
510 Ok(())574 Ok(())
511 }575 }
512576
513 #[transactional]
514 pub fn set_token_properties(577 pub fn set_token_properties(
515 collection: &NonfungibleHandle<T>,578 collection: &NonfungibleHandle<T>,
516 sender: &T::CrossAccountId,579 sender: &T::CrossAccountId,
517 token_id: TokenId,580 token_id: TokenId,
518 properties: Vec<Property>,581 properties: impl Iterator<Item = Property>,
519 is_token_create: bool,582 is_token_create: bool,
583 nesting_budget: &dyn Budget,
520 ) -> DispatchResult {584 ) -> DispatchResult {
521 for property in properties {
522 Self::set_token_property(collection, sender, token_id, property, is_token_create)?;585 Self::modify_token_properties(
523 }586 collection,
524587 sender,
525 Ok(())588 token_id,
589 properties.map(|p| (p.key, Some(p.value))),
590 is_token_create,
591 nesting_budget,
592 )
526 }593 }
527594
528 pub fn delete_token_property(595 pub fn set_token_property(
529 collection: &NonfungibleHandle<T>,596 collection: &NonfungibleHandle<T>,
530 sender: &T::CrossAccountId,597 sender: &T::CrossAccountId,
531 token_id: TokenId,598 token_id: TokenId,
532 property_key: PropertyKey,599 property: Property,
600 nesting_budget: &dyn Budget,
533 ) -> DispatchResult {601 ) -> DispatchResult {
602 let is_token_create = false;
603
534 Self::check_token_change_permission(collection, sender, token_id, &property_key, false)?;604 Self::set_token_properties(
535605 collection,
536 <TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {606 sender,
537 properties.remove(&property_key)607 token_id,
538 })608 [property].into_iter(),
539 .map_err(<CommonError<T>>::from)?;609 is_token_create,
540610 nesting_budget,
541 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(611 )
542 collection.id,
543 token_id,
544 property_key,
545 ));
546
547 Ok(())
548 }612 }
549613
550 fn check_token_change_permission(614 pub fn delete_token_properties(
551 collection: &NonfungibleHandle<T>,615 collection: &NonfungibleHandle<T>,
552 sender: &T::CrossAccountId,616 sender: &T::CrossAccountId,
553 token_id: TokenId,617 token_id: TokenId,
554 property_key: &PropertyKey,618 property_keys: impl Iterator<Item = PropertyKey>,
555 is_token_create: bool,619 nesting_budget: &dyn Budget,
556 ) -> DispatchResult {620 ) -> DispatchResult {
557 let permission = <PalletCommon<T>>::property_permissions(collection.id)621 let is_token_create = false;
558 .get(property_key)622
559 .cloned()
560 .unwrap_or_else(PropertyPermission::none);
561
562 let token_data = <TokenData<T>>::get((collection.id, token_id))
563 .ok_or(<CommonError<T>>::TokenNotFound)?;
564
565 let check_token_owner = || -> DispatchResult {
566 ensure!(&token_data.owner == sender, <CommonError<T>>::NoPermission);
567 Ok(())
568 };
569
570 let is_property_exists = TokenProperties::<T>::get((collection.id, token_id))
571 .get(property_key)
572 .is_some();
573
574 match permission {
575 PropertyPermission { mutable: false, .. } if is_property_exists => {
576 Err(<CommonError<T>>::NoPermission.into())623 Self::modify_token_properties(
577 }
578
579 PropertyPermission {
580 collection_admin,624 collection,
581 token_owner,625 sender,
582 ..626 token_id,
583 } => {
584 //TODO: investigate threats during public minting.
585 if is_token_create && (collection_admin || token_owner) {
586 return Ok(());627 property_keys.into_iter().map(|key| (key, None)),
587 }628 is_token_create,
588629 nesting_budget,
589 let mut check_result = Err(<CommonError<T>>::NoPermission.into());630 )
590
591 if collection_admin {
592 check_result = collection.check_is_owner_or_admin(sender);
593 }
594
595 if token_owner {
596 check_result.or_else(|_| check_token_owner())
597 } else {
598 check_result
599 }
600 }
601 }
602 }631 }
603632
604 #[transactional]
605 pub fn delete_token_properties(633 pub fn delete_token_property(
606 collection: &NonfungibleHandle<T>,634 collection: &NonfungibleHandle<T>,
607 sender: &T::CrossAccountId,635 sender: &T::CrossAccountId,
608 token_id: TokenId,636 token_id: TokenId,
609 property_keys: Vec<PropertyKey>,637 property_key: PropertyKey,
638 nesting_budget: &dyn Budget,
610 ) -> DispatchResult {639 ) -> DispatchResult {
611 for key in property_keys {
612 Self::delete_token_property(collection, sender, token_id, key)?;640 Self::delete_token_properties(
613 }641 collection,
614642 sender,
615 Ok(())643 token_id,
644 [property_key].into_iter(),
645 nesting_budget,
646 )
616 }647 }
617648
818 collection,849 collection,
819 sender,850 sender,
820 TokenId(token),851 TokenId(token),
821 data.properties.clone().into_inner(),852 data.properties.clone().into_iter(),
822 true,853 true,
854 nesting_budget,
823 ) {855 ) {
824 return TransactionOutcome::Rollback(Err(e));856 return TransactionOutcome::Rollback(Err(e));
825 }857 }
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
314 _sender: T::CrossAccountId,314 _sender: T::CrossAccountId,
315 _token_id: TokenId,315 _token_id: TokenId,
316 _property: Vec<Property>,316 _property: Vec<Property>,
317 _nesting_budget: &dyn Budget,
317 ) -> DispatchResultWithPostInfo {318 ) -> DispatchResultWithPostInfo {
318 fail!(<Error<T>>::SettingPropertiesNotAllowed)319 fail!(<Error<T>>::SettingPropertiesNotAllowed)
319 }320 }
331 _sender: T::CrossAccountId,332 _sender: T::CrossAccountId,
332 _token_id: TokenId,333 _token_id: TokenId,
333 _property_keys: Vec<PropertyKey>,334 _property_keys: Vec<PropertyKey>,
335 _nesting_budget: &dyn Budget,
334 ) -> DispatchResultWithPostInfo {336 ) -> DispatchResultWithPostInfo {
335 fail!(<Error<T>>::SettingPropertiesNotAllowed)337 fail!(<Error<T>>::SettingPropertiesNotAllowed)
336 }338 }
340 _sender: <T>::CrossAccountId,342 _sender: <T>::CrossAccountId,
341 _from: (CollectionId, TokenId),343 _from: (CollectionId, TokenId),
342 _under: TokenId,344 _under: TokenId,
343 _budget: &dyn Budget,345 _nesting_budget: &dyn Budget,
344 ) -> sp_runtime::DispatchResult {346 ) -> sp_runtime::DispatchResult {
345 fail!(<Error<T>>::RefungibleDisallowsNesting)347 fail!(<Error<T>>::RefungibleDisallowsNesting)
346 }348 }
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
654 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);654 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);
655655
656 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);656 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
657 let budget = budget::Value::new(NESTING_BUDGET);
657658
658 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))659 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))
659 }660 }
660661
661 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]662 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]
669 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);670 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);
670671
671 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);672 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
673 let budget = budget::Value::new(NESTING_BUDGET);
672674
673 dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))675 dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))
674 }676 }
675677
676 #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]678 #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]
modifiedtests/src/nesting/properties.test.tsdiffbeforeafterboth
3import {3import {
4 addCollectionAdminExpectSuccess,4 addCollectionAdminExpectSuccess,
5 createCollectionExpectSuccess,5 createCollectionExpectSuccess,
6 setCollectionPermissionsExpectSuccess,
6 createItemExpectSuccess,7 createItemExpectSuccess,
7 getCreateCollectionResult,8 getCreateCollectionResult,
8 transferExpectSuccess,9 transferExpectSuccess,
9} from '../util/helpers';10} from '../util/helpers';
10import {IKeyringPair} from '@polkadot/types/types';11import {IKeyringPair} from '@polkadot/types/types';
12import {tokenIdToAddress} from '../eth/util/helpers';
1113
12let alice: IKeyringPair;14let alice: IKeyringPair;
13let bob: IKeyringPair;15let bob: IKeyringPair;
522describe('Integration Test: Token Properties', () => {524describe('Integration Test: Token Properties', () => {
523 let collection: number;525 let collection: number;
524 let token: number;526 let token: number;
527 let nestedToken: number;
525 let permissions: {permission: any, signers: IKeyringPair[]}[];528 let permissions: {permission: any, signers: IKeyringPair[]}[];
526529
527 before(async () => {530 before(async () => {
544 beforeEach(async () => {547 beforeEach(async () => {
545 await usingApi(async () => {548 await usingApi(async () => {
546 collection = await createCollectionExpectSuccess();549 collection = await createCollectionExpectSuccess();
550 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
551
547 token = await createItemExpectSuccess(alice, collection, 'NFT');552 token = await createItemExpectSuccess(alice, collection, 'NFT');
553 nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)});
554
548 await addCollectionAdminExpectSuccess(alice, collection, bob.address);555 await addCollectionAdminExpectSuccess(alice, collection, bob.address);
549 await transferExpectSuccess(collection, token, alice, charlie);556 await transferExpectSuccess(collection, token, alice, charlie);
682 });689 });
683 });690 });
691
692 it('Assigns properties to a nested token according to permissions', async () => {
693 await usingApi(async api => {
694 const propertyKeys: string[] = [];
695 let i = 0;
696 for (const permission of permissions) {
697 for (const signer of permission.signers) {
698 const key = i + '_' + signer.address;
699 propertyKeys.push(key);
700
701 await expect(executeTransaction(
702 api,
703 alice,
704 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]),
705 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;
706
707 await expect(executeTransaction(
708 api,
709 signer,
710 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]),
711 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;
712 }
713
714 i++;
715 }
716
717 const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toHuman() as any[];
718 const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toHuman().properties as any[];
719 for (let i = 0; i < properties.length; i++) {
720 expect(properties[i].value).to.be.equal('Serotonin increase');
721 expect(tokensData[i].value).to.be.equal('Serotonin increase');
722 }
723 });
724 });
725
726 it('Changes properties of a nested token according to permissions', async () => {
727 await usingApi(async api => {
728 const propertyKeys: string[] = [];
729 let i = 0;
730 for (const permission of permissions) {
731 if (!permission.permission.mutable) continue;
732
733 for (const signer of permission.signers) {
734 const key = i + '_' + signer.address;
735 propertyKeys.push(key);
736
737 await expect(executeTransaction(
738 api,
739 alice,
740 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]),
741 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;
742
743 await expect(executeTransaction(
744 api,
745 signer,
746 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]),
747 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;
748
749 await expect(executeTransaction(
750 api,
751 signer,
752 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin stable'}]),
753 ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;
754 }
755
756 i++;
757 }
758
759 const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toHuman() as any[];
760 const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toHuman().properties as any[];
761 for (let i = 0; i < properties.length; i++) {
762 expect(properties[i].value).to.be.equal('Serotonin stable');
763 expect(tokensData[i].value).to.be.equal('Serotonin stable');
764 }
765 });
766 });
767
768 it('Deletes properties of a nested token according to permissions', async () => {
769 await usingApi(async api => {
770 const propertyKeys: string[] = [];
771 let i = 0;
772
773 for (const permission of permissions) {
774 if (!permission.permission.mutable) continue;
775
776 for (const signer of permission.signers) {
777 const key = i + '_' + signer.address;
778 propertyKeys.push(key);
779
780 await expect(executeTransaction(
781 api,
782 alice,
783 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]),
784 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;
785
786 await expect(executeTransaction(
787 api,
788 signer,
789 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]),
790 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;
791
792 await expect(executeTransaction(
793 api,
794 signer,
795 api.tx.unique.deleteTokenProperties(collection, nestedToken, [key]),
796 ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;
797 }
798
799 i++;
800 }
801
802 const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toJSON() as any[];
803 expect(properties).to.be.empty;
804 const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toJSON().properties as any[];
805 expect(tokensData).to.be.empty;
806 expect((await api.query.nonfungible.tokenProperties(collection, nestedToken)).toJSON().consumedSpace).to.be.equal(0);
807 });
808 });
684});809});
685810
686describe('Negative Integration Test: Token Properties', () => {811describe('Negative Integration Test: Token Properties', () => {