git.delta.rocks / unique-network / refs/commits / 48f959311e08

difftreelog

chore code review requests

Grigoriy Simonov2022-10-12parent: #f8de52d.patch.diff
in: master

8 files changed

modifiedpallets/common/src/erc.rsdiffbeforeafterboth
--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -709,9 +709,6 @@
 		/// Value "1" ERC721 metadata supported.
 		pub const ERC721_METADATA_SUPPORTED: &[u8] = b"1";
 
-		/// Value "0" ERC721 metadata supported.
-		pub const ERC721_METADATA_UNSUPPORTED: &[u8] = b"0";
-
 		/// Value for [`ERC721_METADATA`].
 		pub fn erc721() -> up_data_structs::PropertyValue {
 			property_value_from_bytes(ERC721_METADATA).expect(EXPECT_CONVERT_ERROR)
modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -104,7 +104,7 @@
 		.try_push(up_data_structs::PropertyKeyPermission {
 			key: key::url(),
 			permission: up_data_structs::PropertyPermission {
-				mutable: false,
+				mutable: true,
 				collection_admin: true,
 				token_owner: false,
 			},
@@ -115,17 +115,6 @@
 		token_property_permissions
 			.try_push(up_data_structs::PropertyKeyPermission {
 				key: key::suffix(),
-				permission: up_data_structs::PropertyPermission {
-					mutable: true,
-					collection_admin: true,
-					token_owner: false,
-				},
-			})
-			.map_err(|e| Error::Revert(format!("{:?}", e)))?;
-
-		token_property_permissions
-			.try_push(up_data_structs::PropertyKeyPermission {
-				key: key::url(),
 				permission: up_data_structs::PropertyPermission {
 					mutable: true,
 					collection_admin: true,
modifiedtests/src/deprecated-helpers/helpers.tsdiffbeforeafterboth
--- a/tests/src/deprecated-helpers/helpers.ts
+++ b/tests/src/deprecated-helpers/helpers.ts
@@ -433,7 +433,6 @@
   mode: {type: 'NFT'},
   name: 'name',
   tokenPrefix: 'prefix',
-  properties: [{key: 'ERC721Metadata', value: '1'}],
 };
 
 export async function
modifiedtests/src/eth/base.test.tsdiffbeforeafterboth
--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -80,7 +80,7 @@
     await usingEthPlaygrounds(async (helper, privateKey) => {
       const donor = privateKey('//Alice');
       const [alice] = await helper.arrange.createAccounts([10n], donor);
-      ({collectionId: collection} = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}));
+      ({collectionId: collection} = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test', properties: [{key: 'ERC721Metadata', value: '1'}]}));
       minter = helper.eth.createAccount();
     });
   });
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -501,7 +501,7 @@
 
   itEth('Returns collection name', async ({helper}) => {
     const caller = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE'});
+    const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE', properties: [{key: 'ERC721Metadata', value: '1'}]});
 
     const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
     const name = await contract.methods.name().call();
@@ -510,7 +510,7 @@
 
   itEth('Returns symbol name', async ({helper}) => {
     const caller = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE'});
+    const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE', properties: [{key: 'ERC721Metadata', value: '1'}]});
 
     const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
     const symbol = await contract.methods.symbol().call();
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -377,7 +377,7 @@
 
   itEth('Returns collection name', async ({helper}) => {
     const caller = helper.eth.createAccount();
-    const collection = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '11'});
+    const collection = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '11', properties: [{key: 'ERC721Metadata', value: '1'}]});
     
     const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);
     const name = await contract.methods.name().call();
@@ -386,7 +386,7 @@
 
   itEth('Returns symbol name', async ({helper}) => {
     const caller = await helper.eth.createAccountWithBalance(donor);
-    const {collectionId} = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '12'});
+    const {collectionId} = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '12', properties: [{key: 'ERC721Metadata', value: '1'}]});
     const contract = helper.ethNativeContract.collectionById(collectionId, 'rft', caller);
     const symbol = await contract.methods.symbol().call();
     expect(symbol).to.equal('12');
modifiedtests/src/nesting/properties.test.tsdiffbeforeafterboth
before · tests/src/nesting/properties.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../util/playgrounds';19import {UniqueHelper, UniqueBaseCollection, UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection, UniqueRFToken} from '../util/playgrounds/unique';202122describe('Composite Properties Test', () => {23  let alice: IKeyringPair;2425  before(async () => {26    await usingPlaygrounds(async (helper, privateKey) => {27      const donor = privateKey('//Alice');28      [alice] = await helper.arrange.createAccounts([50n], donor);29    });30  });3132  async function testMakeSureSuppliesRequired(baseCollection: UniqueNFTCollection | UniqueRFTCollection) {3334    const collectionOption = await baseCollection.getOptions();35    expect(collectionOption).is.not.null;36    let collection = collectionOption;37    expect(collection.tokenPropertyPermissions).to.be.empty;38    expect(collection.properties).to.be.deep.equal([{key: 'ERC721Metadata', value: '1'}]);3940    const propertyPermissions = [41      {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},42      {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},43    ];44    await expect(await baseCollection.setTokenPropertyPermissions(alice, propertyPermissions)).to.be.true;4546    const collectionProperties = [47      {key: 'ERC721Metadata', value: '1'}, 48      {key: 'black_hole', value: 'LIGO'},49      {key: 'electron', value: 'come bond'}, 50    ];51    52    await expect(await baseCollection.setProperties(alice, collectionProperties)).to.be.true;5354    collection = await baseCollection.getOptions();55    expect(collection.tokenPropertyPermissions).to.be.deep.equal(propertyPermissions);56    expect(collection.properties).to.be.deep.equal(collectionProperties);57  }5859  itSub('Makes sure collectionById supplies required fields for NFT',  async ({helper}) => {60    await testMakeSureSuppliesRequired(await helper.nft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}));61  });6263  itSub.ifWithPallets('Makes sure collectionById supplies required fields for ReFungible', [Pallets.ReFungible],  async ({helper}) => {64    await testMakeSureSuppliesRequired(await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}));65  });66});67// ---------- COLLECTION PROPERTIES6869describe('Integration Test: Collection Properties', () => {70  let alice: IKeyringPair;71  let bob: IKeyringPair;7273  before(async () => {74    await usingPlaygrounds(async (helper, privateKey) => {75      const donor = privateKey('//Alice');76      [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);77    });78  });7980  itSub('Properties are initially empty', async ({helper}) => {81    const collection = await helper.nft.mintCollection(alice);82    const properties = await collection.getProperties();83    expect(properties).to.be.deep.equal([{84      'key': 'ERC721Metadata',85      'value': '1',86    }]);87  });8889  async function testSetsPropertiesForCollection(collection: UniqueBaseCollection) {90    // As owner91    await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}])).to.be.fulfilled;9293    await collection.addAdmin(alice, {Substrate: bob.address});9495    // As administrator96    await expect(collection.setProperties(bob, [{key: 'black_hole'}])).to.be.fulfilled;9798    const properties = await collection.getProperties();99    expect(properties).to.include.deep.members([100      {key: 'electron', value: 'come bond'},101      {key: 'black_hole', value: ''},102    ]);103  }104105  itSub('Sets properties for a NFT collection', async ({helper}) =>  {106    await testSetsPropertiesForCollection(await helper.nft.mintCollection(alice));107  });108109  itSub.ifWithPallets('Sets properties for a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {110    await testSetsPropertiesForCollection(await helper.rft.mintCollection(alice));111  });112113  async function testCheckValidNames(collection: UniqueBaseCollection) {114    // alpha symbols115    await expect(collection.setProperties(alice, [{key: 'answer'}])).to.be.fulfilled;116117    // numeric symbols118    await expect(collection.setProperties(alice, [{key: '451'}])).to.be.fulfilled;119120    // underscore symbol121    await expect(collection.setProperties(alice, [{key: 'black_hole'}])).to.be.fulfilled;122123    // dash symbol124    await expect(collection.setProperties(alice, [{key: '-'}])).to.be.fulfilled;125126    // dot symbol127    await expect(collection.setProperties(alice, [{key: 'once.in.a.long.long.while...', value: 'you get a little lost'}])).to.be.fulfilled;128129    const properties = await collection.getProperties();130    expect(properties).to.include.deep.members([131      {key: 'answer', value: ''},132      {key: '451', value: ''},133      {key: 'black_hole', value: ''},134      {key: '-', value: ''},135      {key: 'once.in.a.long.long.while...', value: 'you get a little lost'},136    ]);137  }138139  itSub('Check valid names for NFT collection properties keys', async ({helper}) =>  {140    await testCheckValidNames(await helper.nft.mintCollection(alice));141  });142143  itSub.ifWithPallets('Check valid names for ReFungible collection properties keys', [Pallets.ReFungible], async ({helper}) => {144    await testCheckValidNames(await helper.rft.mintCollection(alice));145  });146147  async function testChangesProperties(collection: UniqueBaseCollection) {148    await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: ''}])).to.be.fulfilled;149150    // Mutate the properties151    await expect(collection.setProperties(alice, [{key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;152153    const properties = await collection.getProperties();154    expect(properties).to.include.deep.members([155      {key: 'electron', value: 'come bond'},156      {key: 'black_hole', value: 'LIGO'},157    ]);158  }159160  itSub('Changes properties of a NFT collection', async ({helper}) =>  {161    await testChangesProperties(await helper.nft.mintCollection(alice));162  });163164  itSub.ifWithPallets('Changes properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {165    await testChangesProperties(await helper.rft.mintCollection(alice));166  });167168  async function testDeleteProperties(collection: UniqueBaseCollection) {169    await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;170171    await expect(collection.deleteProperties(alice, ['electron'])).to.be.fulfilled;172173    const properties = await collection.getProperties(['black_hole', 'electron']);174    expect(properties).to.be.deep.equal([175      {key: 'black_hole', value: 'LIGO'},176    ]);177  }178179  itSub('Deletes properties of a NFT collection', async ({helper}) =>  {180    await testDeleteProperties(await helper.nft.mintCollection(alice));181  });182183  itSub.ifWithPallets('Deletes properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {184    await testDeleteProperties(await helper.rft.mintCollection(alice));185  });186});187188describe('Negative Integration Test: Collection Properties', () => {189  let alice: IKeyringPair;190  let bob: IKeyringPair;191192  before(async () => {193    await usingPlaygrounds(async (helper, privateKey) => {194      const donor = privateKey('//Alice');195      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);196    });197  });198  199  async function testFailsSetPropertiesIfNotOwnerOrAdmin(collection: UniqueBaseCollection) {  200    await expect(collection.setProperties(bob, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]))201      .to.be.rejectedWith(/common\.NoPermission/);202203    const properties = await collection.getProperties();204    expect(properties).to.be.deep.equal([{205      'key': 'ERC721Metadata',206      'value': '1',207    }]);208  }209210  itSub('Fails to set properties in a NFT collection if not its onwer/administrator', async ({helper}) =>  {211    await testFailsSetPropertiesIfNotOwnerOrAdmin(await helper.nft.mintCollection(alice));212  });213214  itSub.ifWithPallets('Fails to set properties in a ReFungible collection if not its onwer/administrator', [Pallets.ReFungible], async ({helper}) => {215    await testFailsSetPropertiesIfNotOwnerOrAdmin(await helper.rft.mintCollection(alice));216  });217  218  async function testFailsSetPropertiesThatExeedLimits(collection: UniqueBaseCollection) {219    const spaceLimit = (await (collection.helper!.api! as any).query.common.collectionProperties(collection.collectionId)).spaceLimit.toNumber();220  221    // Mute the general tx parsing error, too many bytes to process222    {223      console.error = () => {};224      await expect(collection.setProperties(alice, [225        {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))},226      ])).to.be.rejected;227    }228229    expect(await collection.getProperties(['electron'])).to.be.empty;230231    await expect(collection.setProperties(alice, [232      {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 233      {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 234    ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);235236    expect(await collection.getProperties(['electron', 'black_hole'])).to.be.empty;237  }238239  itSub('Fails to set properties that exceed the limits (NFT)', async ({helper}) =>  {240    await testFailsSetPropertiesThatExeedLimits(await helper.nft.mintCollection(alice));241  });242243  itSub.ifWithPallets('Fails to set properties that exceed the limits (ReFungible)', [Pallets.ReFungible], async ({helper}) => {244    await testFailsSetPropertiesThatExeedLimits(await helper.rft.mintCollection(alice));245  });246  247  async function testFailsSetMorePropertiesThanAllowed(collection: UniqueBaseCollection) {248    const propertiesToBeSet = [];249    for (let i = 0; i < 65; i++) {250      propertiesToBeSet.push({251        key: 'electron_' + i,252        value: Math.random() > 0.5 ? 'high' : 'low',253      });254    }255256    await expect(collection.setProperties(alice, propertiesToBeSet)).257      to.be.rejectedWith(/common\.PropertyLimitReached/);258259    const properties = await collection.getProperties();260    expect(properties).to.be.deep.equal([{261      'key': 'ERC721Metadata',262      'value': '1',263    }]);264  }265266  itSub('Fails to set more properties than it is allowed (NFT)', async ({helper}) =>  {267    await testFailsSetMorePropertiesThanAllowed(await helper.nft.mintCollection(alice));268  });269270  itSub.ifWithPallets('Fails to set more properties than it is allowed (ReFungible)', [Pallets.ReFungible], async ({helper}) => {271    await testFailsSetMorePropertiesThanAllowed(await helper.rft.mintCollection(alice));272  });273  274  async function testFailsSetPropertiesWithInvalidNames(collection: UniqueBaseCollection) {275    const invalidProperties = [276      [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],277      [{key: 'Mr/Sandman', value: 'Bring me a gene'}],278      [{key: 'déjà vu', value: 'hmm...'}],279    ];280281    for (let i = 0; i < invalidProperties.length; i++) {282      await expect(283        collection.setProperties(alice, invalidProperties[i]), 284        `on rejecting the new badly-named property #${i}`,285      ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);286    }287288    await expect(289      collection.setProperties(alice, [{key: '', value: 'nothing must not exist'}]), 290      'on rejecting an unnamed property',291    ).to.be.rejectedWith(/common\.EmptyPropertyKey/);292293    await expect(294      collection.setProperties(alice, [{key: 'CRISPR-Cas9', value: 'rewriting nature!'}]), 295      'on setting the correctly-but-still-badly-named property',296    ).to.be.fulfilled;297298    const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');299300    const properties = await collection.getProperties(keys);301    expect(properties).to.be.deep.equal([302      {key: 'CRISPR-Cas9', value: 'rewriting nature!'},303    ]);304305    for (let i = 0; i < invalidProperties.length; i++) {306      await expect(307        collection.deleteProperties(alice, invalidProperties[i].map(propertySet => propertySet.key)), 308        `on trying to delete the non-existent badly-named property #${i}`,309      ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);310    }311  }312313  itSub('Fails to set properties with invalid names (NFT)', async ({helper}) =>  {314    await testFailsSetPropertiesWithInvalidNames(await helper.nft.mintCollection(alice));315  });316317  itSub.ifWithPallets('Fails to set properties with invalid names (ReFungible)', [Pallets.ReFungible], async ({helper}) => {318    await testFailsSetPropertiesWithInvalidNames(await helper.rft.mintCollection(alice));319  });320});321322// ---------- ACCESS RIGHTS323324describe('Integration Test: Access Rights to Token Properties', () => {325  let alice: IKeyringPair;326  let bob: IKeyringPair;327328  before(async () => {329    await usingPlaygrounds(async (helper, privateKey) => {330      const donor = privateKey('//Alice');331      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);332    });333  });334  335  itSub('Reads access rights to properties of a collection', async ({helper}) =>  {336    const collection = await helper.nft.mintCollection(alice);337    const propertyRights = (await helper.callRpc('api.query.common.collectionPropertyPermissions', [collection.collectionId])).toJSON();338    expect(propertyRights).to.be.empty;339  });340  341  async function testSetsAccessRightsToProperties(collection: UniqueNFTCollection | UniqueRFTCollection) {  342    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true}}]))343      .to.be.fulfilled;344345    await collection.addAdmin(alice, {Substrate: bob.address});346347    await expect(collection.setTokenPropertyPermissions(bob, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]))348      .to.be.fulfilled;349350    const propertyRights = await collection.getPropertyPermissions(['skullduggery', 'mindgame']);351    expect(propertyRights).to.include.deep.members([352      {key: 'skullduggery', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}},353      {key: 'mindgame', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},354    ]);355  }356357  itSub('Sets access rights to properties of a collection (NFT)', async ({helper}) =>  {358    await testSetsAccessRightsToProperties(await helper.nft.mintCollection(alice));359  });360361  itSub.ifWithPallets('Sets access rights to properties of a collection (ReFungible)', [Pallets.ReFungible], async ({helper}) => {362    await testSetsAccessRightsToProperties(await helper.rft.mintCollection(alice));363  });364  365  async function testChangesAccessRightsToProperty(collection: UniqueNFTCollection | UniqueRFTCollection) {366    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]))367      .to.be.fulfilled;368369    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]))370      .to.be.fulfilled;371372    const propertyRights = await collection.getPropertyPermissions();373    expect(propertyRights).to.be.deep.equal([374      {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},375    ]);376  }377378  itSub('Changes access rights to properties of a NFT collection', async ({helper}) =>  {379    await testChangesAccessRightsToProperty(await helper.nft.mintCollection(alice));380  });381382  itSub.ifWithPallets('Changes access rights to properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {383    await testChangesAccessRightsToProperty(await helper.rft.mintCollection(alice));384  });385});386387describe('Negative Integration Test: Access Rights to Token Properties', () => {388  let alice: IKeyringPair;389  let bob: IKeyringPair;390391  before(async () => {392    await usingPlaygrounds(async (helper, privateKey) => {393      const donor = privateKey('//Alice');394      [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);395    });396  });397398  async function testPreventsFromSettingAccessRightsNotAdminOrOwner(collection: UniqueNFTCollection | UniqueRFTCollection) {399    await expect(collection.setTokenPropertyPermissions(bob, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]))400      .to.be.rejectedWith(/common\.NoPermission/);401402    const propertyRights = await collection.getPropertyPermissions(['skullduggery']);403    expect(propertyRights).to.be.empty;404  }405406  itSub('Prevents from setting access rights to properties of a NFT collection if not an onwer/admin', async ({helper}) =>  {407    await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.nft.mintCollection(alice));408  });409410  itSub.ifWithPallets('Prevents from setting access rights to properties of a ReFungible collection if not an onwer/admin', [Pallets.ReFungible], async ({helper}) => {411    await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.rft.mintCollection(alice));412  });413414  async function testPreventFromAddingTooManyPossibleProperties(collection: UniqueNFTCollection | UniqueRFTCollection) {  415    const constitution = [];416    for (let i = 0; i < 65; i++) {417      constitution.push({418        key: 'property_' + i,419        permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},420      });421    }422423    await expect(collection.setTokenPropertyPermissions(alice, constitution))424      .to.be.rejectedWith(/common\.PropertyLimitReached/);425426    const propertyRights = await collection.getPropertyPermissions();427    expect(propertyRights).to.be.empty;428  }429430  itSub('Prevents from adding too many possible properties (NFT)', async ({helper}) =>  {431    await testPreventFromAddingTooManyPossibleProperties(await helper.nft.mintCollection(alice));432  });433434  itSub.ifWithPallets('Prevents from adding too many possible properties (ReFungible)', [Pallets.ReFungible], async ({helper}) => {435    await testPreventFromAddingTooManyPossibleProperties(await helper.rft.mintCollection(alice));436  });437438  async function testPreventAccessRightsModifiedIfConstant(collection: UniqueNFTCollection | UniqueRFTCollection) {439    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]))440      .to.be.fulfilled;441442    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {collectionAdmin: true}}]))443      .to.be.rejectedWith(/common\.NoPermission/);444445    const propertyRights = await collection.getPropertyPermissions(['skullduggery']);446    expect(propertyRights).to.deep.equal([447      {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},448    ]);449  }450451  itSub('Prevents access rights to be modified if constant (NFT)', async ({helper}) =>  {452    await testPreventAccessRightsModifiedIfConstant(await helper.nft.mintCollection(alice));453  });454455  itSub.ifWithPallets('Prevents access rights to be modified if constant (ReFungible)', [Pallets.ReFungible], async ({helper}) => {456    await testPreventAccessRightsModifiedIfConstant(await helper.rft.mintCollection(alice));457  });458459  async function testPreventsAddingPropertiesWithInvalidNames(collection: UniqueNFTCollection | UniqueRFTCollection) {460    const invalidProperties = [461      [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],462      [{key: 'G#4', permission: {tokenOwner: true}}],463      [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],464    ];465466    for (let i = 0; i < invalidProperties.length; i++) {467      await expect(468        collection.setTokenPropertyPermissions(alice, invalidProperties[i]), 469        `on setting the new badly-named property #${i}`,470      ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);471    }472473    await expect(474      collection.setTokenPropertyPermissions(alice, [{key: '', permission: {}}]), 475      'on rejecting an unnamed property',476    ).to.be.rejectedWith(/common\.EmptyPropertyKey/);477478    const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string479    await expect(480      collection.setTokenPropertyPermissions(alice, [481        {key: correctKey, permission: {collectionAdmin: true}},482      ]), 483      'on setting the correctly-but-still-badly-named property',484    ).to.be.fulfilled;485486    const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');487488    const propertyRights = await collection.getPropertyPermissions(keys);489    expect(propertyRights).to.be.deep.equal([490      {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},491    ]);492  }493494  itSub('Prevents adding properties with invalid names (NFT)', async ({helper}) =>  {495    await testPreventsAddingPropertiesWithInvalidNames(await helper.nft.mintCollection(alice));496  });497498  itSub.ifWithPallets('Prevents adding properties with invalid names (ReFungible)', [Pallets.ReFungible], async ({helper}) => {499    await testPreventsAddingPropertiesWithInvalidNames(await helper.rft.mintCollection(alice));500  });501});502503// ---------- TOKEN PROPERTIES504505describe('Integration Test: Token Properties', () => {506  let alice: IKeyringPair; // collection owner507  let bob: IKeyringPair; // collection admin508  let charlie: IKeyringPair; // token owner509510  let permissions: {permission: any, signers: IKeyringPair[]}[];511512  before(async () => {513    await usingPlaygrounds(async (helper, privateKey) => {514      const donor = privateKey('//Alice');515      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);516    });517518    // todo:playgrounds probably separate these tests later519    permissions = [520      {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},521      {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},522      {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},523      {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},524      {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},525      {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},526    ];527  });528  529  async function testReadsYetEmptyProperties(token: UniqueNFToken | UniqueRFToken) {530    const properties = await token.getProperties();531    expect(properties).to.be.empty;532533    const tokenData = await token.getData();534    expect(tokenData!.properties).to.be.empty;535  }536537  itSub('Reads yet empty properties of a token (NFT)', async ({helper}) => {538    const collection = await helper.nft.mintCollection(alice);539    const token = await collection.mintToken(alice);540    await testReadsYetEmptyProperties(token);541  });542543  itSub.ifWithPallets('Reads yet empty properties of a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {544    const collection = await helper.rft.mintCollection(alice);545    const token = await collection.mintToken(alice);546    await testReadsYetEmptyProperties(token);547  });548549  async function testAssignPropertiesAccordingToPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {550    await token.collection.addAdmin(alice, {Substrate: bob.address});551    await token.transfer(alice, {Substrate: charlie.address}, pieces);552553    const propertyKeys: string[] = [];554    let i = 0;555    for (const permission of permissions) {556      i++;557      let j = 0;558      for (const signer of permission.signers) {559        j++;560        const key = i + '_' + signer.address;561        propertyKeys.push(key);562563        await expect(564          token.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 565          `on setting permission #${i} by alice`,566        ).to.be.fulfilled;567568        await expect(569          token.setProperties(signer, [{key: key, value: 'Serotonin increase'}]), 570          `on adding property #${i} by signer #${j}`,571        ).to.be.fulfilled;572      }573    }574575    const properties = await token.getProperties(propertyKeys);576    const tokenData = await token.getData();577    for (let i = 0; i < properties.length; i++) {578      expect(properties[i].value).to.be.equal('Serotonin increase');579      expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase');580    }581  }582583  itSub('Assigns properties to a token according to permissions (NFT)', async ({helper}) =>  {584    const collection = await helper.nft.mintCollection(alice);585    const token = await collection.mintToken(alice);586    await testAssignPropertiesAccordingToPermissions(token, 1n);587  });588589  itSub.ifWithPallets('Assigns properties to a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {590    const collection = await helper.rft.mintCollection(alice);591    const token = await collection.mintToken(alice, 100n);592    await testAssignPropertiesAccordingToPermissions(token, 100n);593  });594595  async function testChangesPropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {596    await token.collection.addAdmin(alice, {Substrate: bob.address});597    await token.transfer(alice, {Substrate: charlie.address}, pieces);598599    const propertyKeys: string[] = [];600    let i = 0;601    for (const permission of permissions) {602      i++;603      if (!permission.permission.mutable) continue;604      605      let j = 0;606      for (const signer of permission.signers) {607        j++;608        const key = i + '_' + signer.address;609        propertyKeys.push(key);610611        await expect(612          token.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 613          `on setting permission #${i} by alice`,614        ).to.be.fulfilled;615616        await expect(617          token.setProperties(signer, [{key, value: 'Serotonin increase'}]), 618          `on adding property #${i} by signer #${j}`,619        ).to.be.fulfilled;620621        await expect(622          token.setProperties(signer, [{key, value: 'Serotonin stable'}]), 623          `on changing property #${i} by signer #${j}`,624        ).to.be.fulfilled;625      }626    }627628    const properties = await token.getProperties(propertyKeys);629    const tokenData = await token.getData();630    for (let i = 0; i < properties.length; i++) {631      expect(properties[i].value).to.be.equal('Serotonin stable');632      expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable');633    }634  }635636  itSub('Changes properties of a token according to permissions (NFT)', async ({helper}) =>  {637    const collection = await helper.nft.mintCollection(alice);638    const token = await collection.mintToken(alice);639    await testChangesPropertiesAccordingPermission(token, 1n);640  });641642  itSub.ifWithPallets('Changes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {643    const collection = await helper.rft.mintCollection(alice);644    const token = await collection.mintToken(alice, 100n);645    await testChangesPropertiesAccordingPermission(token, 100n);646  });647648  async function testDeletePropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {649    await token.collection.addAdmin(alice, {Substrate: bob.address});650    await token.transfer(alice, {Substrate: charlie.address}, pieces);651652    const propertyKeys: string[] = [];653    let i = 0;654655    for (const permission of permissions) {656      i++;657      if (!permission.permission.mutable) continue;658      659      let j = 0;660      for (const signer of permission.signers) {661        j++;662        const key = i + '_' + signer.address;663        propertyKeys.push(key);664665        await expect(666          token.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 667          `on setting permission #${i} by alice`,668        ).to.be.fulfilled;669670        await expect(671          token.setProperties(signer, [{key, value: 'Serotonin increase'}]), 672          `on adding property #${i} by signer #${j}`,673        ).to.be.fulfilled;674675        await expect(676          token.deleteProperties(signer, [key]), 677          `on deleting property #${i} by signer #${j}`,678        ).to.be.fulfilled;679      }680    }681682    expect(await token.getProperties(propertyKeys)).to.be.empty;683    expect((await token.getData())!.properties).to.be.empty;684  }685  686  itSub('Deletes properties of a token according to permissions (NFT)', async ({helper}) =>  {687    const collection = await helper.nft.mintCollection(alice);688    const token = await collection.mintToken(alice);689    await testDeletePropertiesAccordingPermission(token, 1n);690  });691692  itSub.ifWithPallets('Deletes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {693    const collection = await helper.rft.mintCollection(alice);694    const token = await collection.mintToken(alice, 100n);695    await testDeletePropertiesAccordingPermission(token, 100n);696  });697698  itSub('Assigns properties to a nested token according to permissions', async ({helper}) =>  {699    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});700    const collectionB = await helper.nft.mintCollection(alice);701    const targetToken = await collectionA.mintToken(alice);702    const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());703704    await collectionB.addAdmin(alice, {Substrate: bob.address});705    await targetToken.transfer(alice, {Substrate: charlie.address});706707    const propertyKeys: string[] = [];708    let i = 0;709    for (const permission of permissions) {710      i++;711      let j = 0;712      for (const signer of permission.signers) {713        j++;714        const key = i + '_' + signer.address;715        propertyKeys.push(key);716        717        await expect(718          nestedToken.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 719          `on setting permission #${i} by alice`,720        ).to.be.fulfilled;721722        await expect(723          nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), 724          `on adding property #${i} by signer #${j}`,725        ).to.be.fulfilled;726      }727    }728729    const properties = await nestedToken.getProperties(propertyKeys);730    const tokenData = await nestedToken.getData();731    for (let i = 0; i < properties.length; i++) {732      expect(properties[i].value).to.be.equal('Serotonin increase');733      expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase');734    }735    expect(await targetToken.getProperties()).to.be.empty;736  });737738  itSub('Changes properties of a nested token according to permissions', async ({helper}) =>  {739    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});740    const collectionB = await helper.nft.mintCollection(alice);741    const targetToken = await collectionA.mintToken(alice);742    const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());743744    await collectionB.addAdmin(alice, {Substrate: bob.address});745    await targetToken.transfer(alice, {Substrate: charlie.address});746747    const propertyKeys: string[] = [];748    let i = 0;749    for (const permission of permissions) {750      i++;751      if (!permission.permission.mutable) continue;752      753      let j = 0;754      for (const signer of permission.signers) {755        j++;756        const key = i + '_' + signer.address;757        propertyKeys.push(key);758759        await expect(760          nestedToken.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 761          `on setting permission #${i} by alice`,762        ).to.be.fulfilled;763764        await expect(765          nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), 766          `on adding property #${i} by signer #${j}`,767        ).to.be.fulfilled;768769        await expect(770          nestedToken.setProperties(signer, [{key, value: 'Serotonin stable'}]), 771          `on changing property #${i} by signer #${j}`,772        ).to.be.fulfilled;773      }774    }775776    const properties = await nestedToken.getProperties(propertyKeys);777    const tokenData = await nestedToken.getData();778    for (let i = 0; i < properties.length; i++) {779      expect(properties[i].value).to.be.equal('Serotonin stable');780      expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable');781    }782    expect(await targetToken.getProperties()).to.be.empty;783  });784785  itSub('Deletes properties of a nested token according to permissions', async ({helper}) =>  {786    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});787    const collectionB = await helper.nft.mintCollection(alice);788    const targetToken = await collectionA.mintToken(alice);789    const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());790791    await collectionB.addAdmin(alice, {Substrate: bob.address});792    await targetToken.transfer(alice, {Substrate: charlie.address});793794    const propertyKeys: string[] = [];795    let i = 0;796    for (const permission of permissions) {797      i++;798      if (!permission.permission.mutable) continue;799      800      let j = 0;801      for (const signer of permission.signers) {802        j++;803        const key = i + '_' + signer.address;804        propertyKeys.push(key);805806        await expect(807          nestedToken.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]), 808          `on setting permission #${i} by alice`,809        ).to.be.fulfilled;810811        await expect(812          nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]), 813          `on adding property #${i} by signer #${j}`,814        ).to.be.fulfilled;815816        await expect(817          nestedToken.deleteProperties(signer, [key]), 818          `on deleting property #${i} by signer #${j}`,819        ).to.be.fulfilled;820      }821    }822823    expect(await nestedToken.getProperties(propertyKeys)).to.be.empty;824    expect((await nestedToken.getData())!.properties).to.be.empty;825    expect(await targetToken.getProperties()).to.be.empty;826  });827});828829describe('Negative Integration Test: Token Properties', () => {830  let alice: IKeyringPair; // collection owner831  let bob: IKeyringPair; // collection admin832  let charlie: IKeyringPair; // token owner833834  let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];835836  before(async () => {837    await usingPlaygrounds(async (helper, privateKey) => {838      const donor = privateKey('//Alice');839      let dave: IKeyringPair;840      [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);841842      // todo:playgrounds probably separate these tests later843      constitution = [844        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},845        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},846        {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},847        {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},848        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},849        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},850      ];851    });852  });853854  async function getConsumedSpace(api: any, collectionId: number, tokenId: number, mode: 'NFT' | 'RFT'): Promise<number> {855    return (await (mode == 'NFT' ? api.query.nonfungible : api.query.refungible).tokenProperties(collectionId, tokenId)).toJSON().consumedSpace;856  }857858  async function prepare(token: UniqueNFToken | UniqueRFToken, pieces: bigint): Promise<number> {859    await token.collection.addAdmin(alice, {Substrate: bob.address});860    await token.transfer(alice, {Substrate: charlie.address}, pieces);861862    let i = 0;863    for (const passage of constitution) {864      i++;865      const signer = passage.signers[0];866      867      await expect(868        token.collection.setTokenPropertyPermissions(alice, [{key: `${i}`, permission: passage.permission}]), 869        `on setting permission ${i} by alice`,870      ).to.be.fulfilled;871872      await expect(873        token.setProperties(signer, [{key: `${i}`, value: 'Serotonin increase'}]), 874        `on adding property ${i} by ${signer.address}`,875      ).to.be.fulfilled;876    }877878    const originalSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); 879    return originalSpace;880  }881882  async function testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {883    const originalSpace = await prepare(token, pieces);884885    let i = 0;886    for (const forbiddance of constitution) {887      i++;888      if (!forbiddance.permission.mutable) continue;889890      await expect(891        token.setProperties(forbiddance.sinner, [{key: `${i}`, value: 'Serotonin down'}]), 892        `on failing to change property ${i} by the malefactor`,893      ).to.be.rejectedWith(/common\.NoPermission/);894895      await expect(896        token.deleteProperties(forbiddance.sinner, [`${i}`]), 897        `on failing to delete property ${i} by the malefactor`,898      ).to.be.rejectedWith(/common\.NoPermission/);899    }900901    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); 902    expect(consumedSpace).to.be.equal(originalSpace);903  }904905  itSub('Forbids changing/deleting properties of a token if the user is outside of permissions (NFT)', async ({helper}) =>  {906    const collection = await helper.nft.mintCollection(alice);907    const token = await collection.mintToken(alice);908    await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, 1n);909  });910911  itSub.ifWithPallets('Forbids changing/deleting properties of a token if the user is outside of permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {912    const collection = await helper.rft.mintCollection(alice);913    const token = await collection.mintToken(alice, 100n);914    await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, 100n);915  });916917  async function testForbidsChangingDeletingPropertiesIfPropertyImmutable(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {918    const originalSpace = await prepare(token, pieces);919920    let i = 0;921    for (const permission of constitution) {922      i++;923      if (permission.permission.mutable) continue;924925      await expect(926        token.setProperties(permission.signers[0], [{key: `${i}`, value: 'Serotonin down'}]), 927        `on failing to change property ${i} by signer #0`,928      ).to.be.rejectedWith(/common\.NoPermission/);929930      await expect(931        token.deleteProperties(permission.signers[0], [i.toString()]), 932        `on failing to delete property ${i} by signer #0`,933      ).to.be.rejectedWith(/common\.NoPermission/);934    }935  936    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); 937    expect(consumedSpace).to.be.equal(originalSpace);938  }939940  itSub('Forbids changing/deleting properties of a token if the property is permanent (immutable) (NFT)', async ({helper}) =>  {941    const collection = await helper.nft.mintCollection(alice);942    const token = await collection.mintToken(alice);943    await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, 1n);944  });945946  itSub.ifWithPallets('Forbids changing/deleting properties of a token if the property is permanent (immutable) (ReFungible)', [Pallets.ReFungible], async ({helper}) => {947    const collection = await helper.rft.mintCollection(alice);948    const token = await collection.mintToken(alice, 100n);949    await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, 100n);950  });951952  async function testForbidsAddingPropertiesIfPropertyNotDeclared(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {953    const originalSpace = await prepare(token, pieces);954955    await expect(956      token.setProperties(alice, [{key: 'non-existent', value: 'I exist!'}]), 957      'on failing to add a previously non-existent property',958    ).to.be.rejectedWith(/common\.NoPermission/);959      960    await expect(961      token.collection.setTokenPropertyPermissions(alice, [{key: 'now-existent', permission: {}}]), 962      'on setting a new non-permitted property',963    ).to.be.fulfilled;964965    await expect(966      token.setProperties(alice, [{key: 'now-existent', value: 'I exist!'}]), 967      'on failing to add a property forbidden by the \'None\' permission',968    ).to.be.rejectedWith(/common\.NoPermission/);969970    expect(await token.getProperties(['non-existent', 'now-existent'])).to.be.empty;971      972    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); 973    expect(consumedSpace).to.be.equal(originalSpace);974  }975976  itSub('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (NFT)', async ({helper}) =>  {977    const collection = await helper.nft.mintCollection(alice);978    const token = await collection.mintToken(alice);979    await testForbidsAddingPropertiesIfPropertyNotDeclared(token, 1n);980  });981982  itSub.ifWithPallets('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (ReFungible)', [Pallets.ReFungible], async ({helper}) => {983    const collection = await helper.rft.mintCollection(alice);984    const token = await collection.mintToken(alice, 100n);985    await testForbidsAddingPropertiesIfPropertyNotDeclared(token, 100n);986  });987988  async function testForbidsAddingTooManyProperties(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {989    const originalSpace = await prepare(token, pieces);990991    await expect(992      token.collection.setTokenPropertyPermissions(alice, [993        {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 994        {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},995      ]), 996      'on setting new permissions for properties',997    ).to.be.fulfilled;998999    // Mute the general tx parsing error1000    {1001      console.error = () => {};1002      await expect(token.setProperties(alice, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]))1003        .to.be.rejected;1004    }10051006    await expect(token.setProperties(alice, [1007      {key: 'a_holy_book', value: 'word '.repeat(3277)}, 1008      {key: 'young_years', value: 'neverending'.repeat(1490)},1009    ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);1010  1011    expect(await token.getProperties(['a_holy_book', 'young_years'])).to.be.empty;1012    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT'); 1013    expect(consumedSpace).to.be.equal(originalSpace);1014  }10151016  itSub('Forbids adding too many properties to a token (NFT)', async ({helper}) =>  {1017    const collection = await helper.nft.mintCollection(alice);1018    const token = await collection.mintToken(alice);1019    await testForbidsAddingTooManyProperties(token, 1n);1020  });10211022  itSub.ifWithPallets('Forbids adding too many properties to a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {1023    const collection = await helper.rft.mintCollection(alice);1024    const token = await collection.mintToken(alice, 100n);1025    await testForbidsAddingTooManyProperties(token, 100n);1026  });1027});10281029describe('ReFungible token properties permissions tests', () => {1030  let alice: IKeyringPair;1031  let bob: IKeyringPair;1032  let charlie: IKeyringPair;10331034  before(async function() {1035    await usingPlaygrounds(async (helper, privateKey) => {1036      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);10371038      const donor = privateKey('//Alice');1039      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);1040    });1041  });10421043  async function prepare(helper: UniqueHelper): Promise<UniqueRFToken> {1044    const collection = await helper.rft.mintCollection(alice);1045    const token = await collection.mintToken(alice, 100n);1046    1047    await collection.addAdmin(alice, {Substrate: bob.address});1048    await collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable: true, tokenOwner: true}}]);1049    1050    return token;1051  }10521053  itSub('Forbids adding token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) =>  {1054    const token = await prepare(helper);10551056    await token.transfer(alice, {Substrate: charlie.address}, 33n);10571058    await expect(token.setProperties(alice, [1059      {key: 'fractals', value: 'multiverse'}, 1060    ])).to.be.rejectedWith(/common\.NoPermission/);1061  });10621063  itSub('Forbids mutating token property with tokenOwher==true when signer doesn\'t have all pieces', async ({helper}) =>  {1064    const token = await prepare(helper);10651066    await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, tokenOwner: true}}]))1067      .to.be.fulfilled;10681069    await expect(token.setProperties(alice, [1070      {key: 'fractals', value: 'multiverse'}, 1071    ])).to.be.fulfilled;10721073    await token.transfer(alice, {Substrate: charlie.address}, 33n);10741075    await expect(token.setProperties(alice, [1076      {key: 'fractals', value: 'want to rule the world'}, 1077    ])).to.be.rejectedWith(/common\.NoPermission/);1078  });10791080  itSub('Forbids deleting token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) =>  {1081    const token = await prepare(helper);10821083    await expect(token.setProperties(alice, [1084      {key: 'fractals', value: 'one headline - why believe it'}, 1085    ])).to.be.fulfilled;10861087    await token.transfer(alice, {Substrate: charlie.address}, 33n);10881089    await expect(token.deleteProperties(alice, ['fractals'])).1090      to.be.rejectedWith(/common\.NoPermission/);1091  });10921093  itSub('Allows token property mutation with collectionOwner==true when admin doesn\'t have all pieces', async ({helper}) =>  {1094    const token = await prepare(helper);10951096    await token.transfer(alice, {Substrate: charlie.address}, 33n);10971098    await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, collectionAdmin: true}}]))1099      .to.be.fulfilled;11001101    await expect(token.setProperties(alice, [1102      {key: 'fractals', value: 'multiverse'}, 1103    ])).to.be.fulfilled;1104  });1105});
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -1297,7 +1297,6 @@
   async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {
     collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object
     collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};
-    collectionOptions.properties = collectionOptions.properties || [{key: 'ERC721Metadata', value: '1'}];
     for (const key of ['name', 'description', 'tokenPrefix']) {
       if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);
     }