1import {expect} from 'chai';2import usingApi, {executeTransaction} from '../substrate/substrate-api';3import {4 addCollectionAdminExpectSuccess,5 CollectionMode,6 createCollectionExpectSuccess,7 setCollectionPermissionsExpectSuccess,8 createItemExpectSuccess,9 getCreateCollectionResult,10 transferExpectSuccess,11} from '../util/helpers';12import {IKeyringPair} from '@polkadot/types/types';13import {tokenIdToAddress} from '../eth/util/helpers';1415let alice: IKeyringPair;16let bob: IKeyringPair;17let charlie: IKeyringPair;1819describe('Composite Properties Test', () => {20 before(async () => {21 await usingApi(async (api, privateKeyWrapper) => {22 alice = privateKeyWrapper('//Alice');23 bob = privateKeyWrapper('//Bob');24 });25 });2627 async function testMakeSureSuppliesRequired(mode: CollectionMode) {28 await usingApi(async api => {29 const collectionId = await createCollectionExpectSuccess({mode: mode});3031 const collectionOption = await api.rpc.unique.collectionById(collectionId);32 expect(collectionOption.isSome).to.be.true;33 let collection = collectionOption.unwrap();34 expect(collection.tokenPropertyPermissions.toHuman()).to.be.empty;35 expect(collection.properties.toHuman()).to.be.empty;3637 const propertyPermissions = [38 {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},39 {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},40 ];41 await expect(executeTransaction(42 api, 43 alice, 44 api.tx.unique.setTokenPropertyPermissions(collectionId, propertyPermissions), 45 )).to.not.be.rejected;4647 const collectionProperties = [48 {key: 'black_hole', value: 'LIGO'},49 {key: 'electron', value: 'come bond'}, 50 ];51 await expect(executeTransaction(52 api, 53 alice, 54 api.tx.unique.setCollectionProperties(collectionId, collectionProperties), 55 )).to.not.be.rejected;5657 collection = (await api.rpc.unique.collectionById(collectionId)).unwrap();58 expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);59 expect(collection.properties.toHuman()).to.be.deep.equal(collectionProperties);60 });61 }6263 it('Makes sure collectionById supplies required fields for NFT', async () => {64 await testMakeSureSuppliesRequired({type: 'NFT'});65 });6667 it('Makes sure collectionById supplies required fields for ReFungible', async () => {68 await testMakeSureSuppliesRequired({type: 'ReFungible'});69 });70});71727374describe('Integration Test: Collection Properties', () => {75 before(async () => {76 await usingApi(async (api, privateKeyWrapper) => {77 alice = privateKeyWrapper('//Alice');78 bob = privateKeyWrapper('//Bob');79 });80 });8182 it('Reads properties from a collection', async () => {83 await usingApi(async api => {84 const collection = await createCollectionExpectSuccess();85 const properties = (await api.query.common.collectionProperties(collection)).toJSON();86 expect(properties.map).to.be.empty;87 expect(properties.consumedSpace).to.equal(0);88 });89 });909192 async function testSetsPropertiesForCollection(mode: string) {93 await usingApi(async api => {94 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: mode}));95 const {collectionId} = getCreateCollectionResult(events);9697 98 await expect(executeTransaction(99 api, 100 bob, 101 api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 102 )).to.not.be.rejected;103104 await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);105106 107 await expect(executeTransaction(108 api, 109 alice, 110 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 111 )).to.not.be.rejected;112113 const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();114 expect(properties).to.be.deep.equal([115 {key: 'electron', value: 'come bond'},116 {key: 'black_hole', value: ''},117 ]);118 });119 }120 it('Sets properties for a NFT collection', async () => {121 await testSetsPropertiesForCollection('NFT');122 });123 it('Sets properties for a ReFungible collection', async () => {124 await testSetsPropertiesForCollection('ReFungible');125 });126127 async function testCheckValidNames(mode: string) {128 await usingApi(async api => {129 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: mode}));130 const {collectionId} = getCreateCollectionResult(events);131 132 133 await expect(executeTransaction(134 api, 135 bob, 136 api.tx.unique.setCollectionProperties(collectionId, [{key: 'alpha'}]), 137 )).to.not.be.rejected;138 139 140 await expect(executeTransaction(141 api, 142 bob, 143 api.tx.unique.setCollectionProperties(collectionId, [{key: '123'}]), 144 )).to.not.be.rejected;145 146 147 await expect(executeTransaction(148 api, 149 bob, 150 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 151 )).to.not.be.rejected;152 153 154 await expect(executeTransaction(155 api, 156 bob, 157 api.tx.unique.setCollectionProperties(collectionId, [{key: 'semi-automatic'}]), 158 )).to.not.be.rejected;159 160 161 await expect(executeTransaction(162 api, 163 bob, 164 api.tx.unique.setCollectionProperties(collectionId, [{key: 'build.rs'}]), 165 )).to.not.be.rejected;166 167 const propertyKeys = ['alpha', '123', 'black_hole', 'semi-automatic', 'build.rs'];168 const properties = (await api.rpc.unique.collectionProperties(collectionId, propertyKeys)).toHuman();169 expect(properties).to.be.deep.equal([170 {key: 'alpha', value: ''},171 {key: '123', value: ''},172 {key: 'black_hole', value: ''},173 {key: 'semi-automatic', value: ''},174 {key: 'build.rs', value: ''},175 ]);176 });177 }178 it('Check valid names for NFT collection properties keys', async () => {179 await testCheckValidNames('NFT');180 });181 it('Check valid names for ReFungible collection properties keys', async () => {182 await testCheckValidNames('ReFungible');183 });184185 async function testChangesProperties(mode: CollectionMode) {186 await usingApi(async api => {187 const collection = await createCollectionExpectSuccess({mode: mode});188 189 await expect(executeTransaction(190 api, 191 alice, 192 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 193 )).to.not.be.rejected;194 195 196 await expect(executeTransaction(197 api, 198 alice, 199 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 200 )).to.not.be.rejected;201 202 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();203 expect(properties).to.be.deep.equal([204 {key: 'electron', value: 'bonded'},205 {key: 'black_hole', value: 'LIGO'},206 ]);207 });208 }209 it('Changes properties of a NFT collection', async () => {210 await testChangesProperties({type: 'NFT'});211 });212 it('Changes properties of a ReFungible collection', async () => {213 await testChangesProperties({type: 'ReFungible'});214 });215216 async function testDeleteProperties(mode: CollectionMode) {217 await usingApi(async api => {218 const collection = await createCollectionExpectSuccess({mode: mode});219 220 await expect(executeTransaction(221 api, 222 alice, 223 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 224 )).to.not.be.rejected;225 226 await expect(executeTransaction(227 api, 228 alice, 229 api.tx.unique.deleteCollectionProperties(collection, ['electron']), 230 )).to.not.be.rejected;231 232 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();233 expect(properties).to.be.deep.equal([234 {key: 'black_hole', value: 'LIGO'},235 ]);236 }); 237 }238 it('Deletes properties of a NFT collection', async () => {239 await testDeleteProperties({type: 'NFT'});240 });241 it('Deletes properties of a ReFungible collection', async () => {242 await testDeleteProperties({type: 'ReFungible'});243 });244});245246describe('Negative Integration Test: Collection Properties', () => {247 before(async () => {248 await usingApi(async (api, privateKeyWrapper) => {249 alice = privateKeyWrapper('//Alice');250 bob = privateKeyWrapper('//Bob');251 });252 });253 254 async function testFailsSetPropertiesIfNotOwnerOrAdmin(mode: CollectionMode) {255 await usingApi(async api => {256 const collection = await createCollectionExpectSuccess({mode: mode});257 258 await expect(executeTransaction(259 api, 260 bob, 261 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 262 )).to.be.rejectedWith(/common\.NoPermission/);263 264 const properties = (await api.query.common.collectionProperties(collection)).toJSON();265 expect(properties.map).to.be.empty;266 expect(properties.consumedSpace).to.equal(0);267 }); 268 }269 it('Fails to set properties in a NFT collection if not its onwer/administrator', async () => {270 await testFailsSetPropertiesIfNotOwnerOrAdmin({type: 'NFT'});271 });272 it('Fails to set properties in a ReFungible collection if not its onwer/administrator', async () => {273 await testFailsSetPropertiesIfNotOwnerOrAdmin({type: 'ReFungible'});274 });275 276 async function testFailsSetPropertiesThatExeedLimits(mode: CollectionMode) {277 await usingApi(async api => {278 const collection = await createCollectionExpectSuccess({mode: mode});279 const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 280 281 282 {283 console.error = () => {};284 await expect(executeTransaction(285 api, 286 alice, 287 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 288 )).to.be.rejected;289 }290 291 let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();292 expect(properties).to.be.empty;293 294 await expect(executeTransaction(295 api, 296 alice, 297 api.tx.unique.setCollectionProperties(collection, [298 {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 299 {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 300 ]), 301 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);302 303 properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();304 expect(properties).to.be.empty;305 }); 306 }307 it('Fails to set properties that exceed the limits (NFT)', async () => {308 await testFailsSetPropertiesThatExeedLimits({type: 'NFT'});309 });310 it('Fails to set properties that exceed the limits (ReFungible)', async () => {311 await testFailsSetPropertiesThatExeedLimits({type: 'ReFungible'});312 });313 314 async function testFailsSetMorePropertiesThanAllowed(mode: CollectionMode) {315 await usingApi(async api => {316 const collection = await createCollectionExpectSuccess({mode: mode});317 318 const propertiesToBeSet = [];319 for (let i = 0; i < 65; i++) {320 propertiesToBeSet.push({321 key: 'electron_' + i,322 value: Math.random() > 0.5 ? 'high' : 'low',323 });324 }325 326 await expect(executeTransaction(327 api, 328 alice, 329 api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 330 )).to.be.rejectedWith(/common\.PropertyLimitReached/);331 332 const properties = (await api.query.common.collectionProperties(collection)).toJSON();333 expect(properties.map).to.be.empty;334 expect(properties.consumedSpace).to.equal(0);335 }); 336 }337 it('Fails to set more properties than it is allowed (NFT)', async () => {338 await testFailsSetMorePropertiesThanAllowed({type: 'NFT'});339 });340 it('Fails to set more properties than it is allowed (ReFungible)', async () => {341 await testFailsSetMorePropertiesThanAllowed({type: 'ReFungible'});342 });343 344 async function testFailsSetPropertiesWithInvalidNames(mode: CollectionMode) {345 await usingApi(async api => {346 const collection = await createCollectionExpectSuccess({mode: mode});347 348 const invalidProperties = [349 [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],350 [{key: 'Mr/Sandman', value: 'Bring me a gene'}],351 [{key: 'déjà vu', value: 'hmm...'}],352 ];353 354 for (let i = 0; i < invalidProperties.length; i++) {355 await expect(executeTransaction(356 api, 357 alice, 358 api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 359 ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);360 }361 362 await expect(executeTransaction(363 api, 364 alice, 365 api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 366 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);367 368 await expect(executeTransaction(369 api, 370 alice, 371 api.tx.unique.setCollectionProperties(collection, [372 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},373 ]), 374 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;375 376 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');377 378 const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();379 expect(properties).to.be.deep.equal([380 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},381 ]);382 383 for (let i = 0; i < invalidProperties.length; i++) {384 await expect(executeTransaction(385 api, 386 alice, 387 api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 388 ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);389 }390 });391 }392 it('Fails to set properties with invalid names (NFT)', async () => {393 await testFailsSetPropertiesWithInvalidNames({type: 'NFT'});394 });395 it('Fails to set properties with invalid names (ReFungible)', async () => {396 await testFailsSetPropertiesWithInvalidNames({type: 'ReFungible'});397 });398});399400401402describe('Integration Test: Access Rights to Token Properties', () => {403 before(async () => {404 await usingApi(async (api, privateKeyWrapper) => {405 alice = privateKeyWrapper('//Alice');406 bob = privateKeyWrapper('//Bob');407 });408 });409 410 it('Reads access rights to properties of a collection', async () => {411 await usingApi(async api => {412 const collection = await createCollectionExpectSuccess();413 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();414 expect(propertyRights).to.be.empty;415 });416 });417 418 async function testSetsAccessRightsToProperties(mode: CollectionMode) {419 await usingApi(async api => {420 const collection = await createCollectionExpectSuccess({mode: mode});421 422 await expect(executeTransaction(423 api, 424 alice, 425 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 426 )).to.not.be.rejected;427 428 await addCollectionAdminExpectSuccess(alice, collection, bob.address);429 430 await expect(executeTransaction(431 api, 432 alice, 433 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 434 )).to.not.be.rejected;435 436 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();437 expect(propertyRights).to.be.deep.equal([438 {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},439 {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},440 ]);441 }); 442 }443 it('Sets access rights to properties of a collection (NFT)', async () => {444 await testSetsAccessRightsToProperties({type: 'NFT'});445 });446 it('Sets access rights to properties of a collection (ReFungible)', async () => {447 await testSetsAccessRightsToProperties({type: 'ReFungible'});448 });449 450 async function testChangesAccessRightsToProperty(mode: CollectionMode) {451 await usingApi(async api => {452 const collection = await createCollectionExpectSuccess({mode: mode});453 454 await expect(executeTransaction(455 api, 456 alice, 457 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 458 )).to.not.be.rejected;459 460 await expect(executeTransaction(461 api, 462 alice, 463 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 464 )).to.not.be.rejected;465 466 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();467 expect(propertyRights).to.be.deep.equal([468 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},469 ]);470 });471 }472 it('Changes access rights to properties of a NFT collection', async () => {473 await testChangesAccessRightsToProperty({type: 'NFT'});474 });475 it('Changes access rights to properties of a ReFungible collection', async () => {476 await testChangesAccessRightsToProperty({type: 'ReFungible'});477 });478});479480describe('Negative Integration Test: Access Rights to Token Properties', () => {481 before(async () => {482 await usingApi(async (api, privateKeyWrapper) => {483 alice = privateKeyWrapper('//Alice');484 bob = privateKeyWrapper('//Bob');485 });486 });487488 async function testPreventsFromSettingAccessRightsNotAdminOrOwner(mode: CollectionMode) {489 await usingApi(async api => {490 const collection = await createCollectionExpectSuccess({mode: mode});491 492 await expect(executeTransaction(493 api, 494 bob, 495 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 496 )).to.be.rejectedWith(/common\.NoPermission/);497 498 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();499 expect(propertyRights).to.be.empty;500 });501 }502 it('Prevents from setting access rights to properties of a NFT collection if not an onwer/admin', async () => {503 await testPreventsFromSettingAccessRightsNotAdminOrOwner({type: 'NFT'});504 });505 it('Prevents from setting access rights to properties of a ReFungible collection if not an onwer/admin', async () => {506 await testPreventsFromSettingAccessRightsNotAdminOrOwner({type: 'ReFungible'});507 });508509 async function testPreventFromAddingTooManyPossibleProperties(mode: CollectionMode) {510 await usingApi(async api => {511 const collection = await createCollectionExpectSuccess({mode: mode});512 513 const constitution = [];514 for (let i = 0; i < 65; i++) {515 constitution.push({516 key: 'property_' + i,517 permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},518 });519 }520 521 await expect(executeTransaction(522 api, 523 alice, 524 api.tx.unique.setTokenPropertyPermissions(collection, constitution), 525 )).to.be.rejectedWith(/common\.PropertyLimitReached/);526 527 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();528 expect(propertyRights).to.be.empty;529 }); 530 }531 it('Prevents from adding too many possible properties (NFT)', async () => {532 await testPreventFromAddingTooManyPossibleProperties({type: 'NFT'});533 });534 it('Prevents from adding too many possible properties (ReFungible)', async () => {535 await testPreventFromAddingTooManyPossibleProperties({type: 'ReFungible'});536 });537538 async function testPreventAccessRightsModifiedIfConstant(mode: CollectionMode) {539 await usingApi(async api => {540 const collection = await createCollectionExpectSuccess({mode: mode});541 542 await expect(executeTransaction(543 api, 544 alice, 545 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 546 )).to.not.be.rejected;547 548 await expect(executeTransaction(549 api, 550 alice, 551 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 552 )).to.be.rejectedWith(/common\.NoPermission/);553 554 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();555 expect(propertyRights).to.deep.equal([556 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},557 ]);558 }); 559 }560 it('Prevents access rights to be modified if constant (NFT)', async () => {561 await testPreventAccessRightsModifiedIfConstant({type: 'NFT'});562 });563 it('Prevents access rights to be modified if constant (ReFungible)', async () => {564 await testPreventAccessRightsModifiedIfConstant({type: 'ReFungible'});565 });566567 async function testPreventsAddingPropertiesWithInvalidNames(mode: CollectionMode) {568 await usingApi(async api => {569 const collection = await createCollectionExpectSuccess({mode: mode});570 571 const invalidProperties = [572 [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],573 [{key: 'G#4', permission: {tokenOwner: true}}],574 [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],575 ];576 577 for (let i = 0; i < invalidProperties.length; i++) {578 await expect(executeTransaction(579 api, 580 alice, 581 api.tx.unique.setTokenPropertyPermissions(collection, invalidProperties[i]), 582 ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);583 }584 585 await expect(executeTransaction(586 api, 587 alice, 588 api.tx.unique.setTokenPropertyPermissions(collection, [{key: '', permission: {}}]), 589 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);590 591 const correctKey = '--0x03116e387820CA05'; 592 await expect(executeTransaction(593 api, 594 alice, 595 api.tx.unique.setTokenPropertyPermissions(collection, [596 {key: correctKey, permission: {collectionAdmin: true}},597 ]), 598 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;599 600 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');601 602 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();603 expect(propertyRights).to.be.deep.equal([604 {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},605 ]);606 });607 }608 it('Prevents adding properties with invalid names (NFT)', async () => {609 await testPreventsAddingPropertiesWithInvalidNames({type: 'NFT'});610 });611 it('Prevents adding properties with invalid names (ReFungible)', async () => {612 await testPreventsAddingPropertiesWithInvalidNames({type: 'ReFungible'});613 });614});615616617618describe('Integration Test: Token Properties', () => {619 let permissions: {permission: any, signers: IKeyringPair[]}[];620621 before(async () => {622 await usingApi(async (api, privateKeyWrapper) => {623 alice = privateKeyWrapper('//Alice'); 624 bob = privateKeyWrapper('//Bob'); 625 charlie = privateKeyWrapper('//Charlie'); 626627 permissions = [628 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},629 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},630 {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},631 {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},632 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},633 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},634 ];635 });636 });637 638 async function testReadsYetEmptyProperties(mode: CollectionMode) {639 await usingApi(async api => {640 const collection = await createCollectionExpectSuccess({mode: mode});641 const token = await createItemExpectSuccess(alice, collection, mode.type);642 643 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();644 expect(properties.map).to.be.empty;645 expect(properties.consumedSpace).to.be.equal(0);646 647 const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;648 expect(tokenData).to.be.empty;649 });650 }651 it('Reads yet empty properties of a token (NFT)', async () => {652 await testReadsYetEmptyProperties({type: 'NFT'});653 });654 it('Reads yet empty properties of a token (ReFungible)', async () => {655 await testReadsYetEmptyProperties({type: 'ReFungible'});656 });657658 async function testAssignPropertiesAccordingToPermissions(mode: CollectionMode, pieces: number) {659 await usingApi(async api => {660 const collection = await createCollectionExpectSuccess({mode: mode});661 const token = await createItemExpectSuccess(alice, collection, mode.type);662 await addCollectionAdminExpectSuccess(alice, collection, bob.address);663 await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);664665 const propertyKeys: string[] = [];666 let i = 0;667 for (const permission of permissions) {668 for (const signer of permission.signers) {669 const key = i + '_' + signer.address;670 propertyKeys.push(key);671672 await expect(executeTransaction(673 api, 674 alice, 675 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 676 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;677678 await expect(executeTransaction(679 api, 680 signer, 681 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 682 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;683 }684685 i++;686 }687688 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];689 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];690 for (let i = 0; i < properties.length; i++) {691 expect(properties[i].value).to.be.equal('Serotonin increase');692 expect(tokensData[i].value).to.be.equal('Serotonin increase');693 }694 });695 }696 it('Assigns properties to a token according to permissions (NFT)', async () => {697 await testAssignPropertiesAccordingToPermissions({type: 'NFT'}, 1);698 });699 it('Assigns properties to a token according to permissions (ReFungible)', async () => {700 await testAssignPropertiesAccordingToPermissions({type: 'ReFungible'}, 100);701 });702703 async function testChangesPropertiesAccordingPermission(mode: CollectionMode, pieces: number) {704 await usingApi(async api => {705 const collection = await createCollectionExpectSuccess({mode: mode});706 const token = await createItemExpectSuccess(alice, collection, mode.type);707 await addCollectionAdminExpectSuccess(alice, collection, bob.address);708 await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);709710 const propertyKeys: string[] = [];711 let i = 0;712 for (const permission of permissions) {713 if (!permission.permission.mutable) continue;714 715 for (const signer of permission.signers) {716 const key = i + '_' + signer.address;717 propertyKeys.push(key);718 719 await expect(executeTransaction(720 api, 721 alice, 722 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 723 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;724 725 await expect(executeTransaction(726 api, 727 signer, 728 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 729 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;730 731 await expect(executeTransaction(732 api, 733 signer, 734 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 735 ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;736 }737 738 i++;739 }740 741 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];742 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];743 for (let i = 0; i < properties.length; i++) {744 expect(properties[i].value).to.be.equal('Serotonin stable');745 expect(tokensData[i].value).to.be.equal('Serotonin stable');746 }747 });748 }749 it('Changes properties of a token according to permissions (NFT)', async () => {750 await testChangesPropertiesAccordingPermission({type: 'NFT'}, 1);751 });752 it('Changes properties of a token according to permissions (ReFungible)', async () => {753 await testChangesPropertiesAccordingPermission({type: 'ReFungible'}, 100);754 });755756 async function testDeletePropertiesAccordingPermission(mode: CollectionMode, pieces: number) {757 await usingApi(async api => {758 const collection = await createCollectionExpectSuccess({mode: mode});759 const token = await createItemExpectSuccess(alice, collection, mode.type);760 await addCollectionAdminExpectSuccess(alice, collection, bob.address);761 await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);762763 const propertyKeys: string[] = [];764 let i = 0;765 766 for (const permission of permissions) {767 if (!permission.permission.mutable) continue;768 769 for (const signer of permission.signers) {770 const key = i + '_' + signer.address;771 propertyKeys.push(key);772 773 await expect(executeTransaction(774 api, 775 alice, 776 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 777 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;778 779 await expect(executeTransaction(780 api, 781 signer, 782 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 783 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;784 785 await expect(executeTransaction(786 api, 787 signer, 788 api.tx.unique.deleteTokenProperties(collection, token, [key]), 789 ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;790 }791 792 i++;793 }794 795 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];796 expect(properties).to.be.empty;797 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];798 expect(tokensData).to.be.empty;799 expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);800 });801 }802 it('Deletes properties of a token according to permissions (NFT)', async () => {803 await testDeletePropertiesAccordingPermission({type: 'NFT'}, 1);804 });805 it('Deletes properties of a token according to permissions (ReFungible)', async () => {806 await testDeletePropertiesAccordingPermission({type: 'ReFungible'}, 100);807 });808809 it('Assigns properties to a nested token according to permissions', async () => {810 await usingApi(async api => {811 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});812 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});813 const token = await createItemExpectSuccess(alice, collection, 'NFT');814 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)});815 await addCollectionAdminExpectSuccess(alice, collection, bob.address);816 await transferExpectSuccess(collection, token, alice, charlie);817818 const propertyKeys: string[] = [];819 let i = 0;820 for (const permission of permissions) {821 for (const signer of permission.signers) {822 const key = i + '_' + signer.address;823 propertyKeys.push(key);824825 await expect(executeTransaction(826 api, 827 alice, 828 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 829 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;830831 await expect(executeTransaction(832 api, 833 signer, 834 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]), 835 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;836 }837838 i++;839 }840841 const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toHuman() as any[];842 const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toHuman().properties as any[];843 for (let i = 0; i < properties.length; i++) {844 expect(properties[i].value).to.be.equal('Serotonin increase');845 expect(tokensData[i].value).to.be.equal('Serotonin increase');846 }847 });848 });849850 it('Changes properties of a nested token according to permissions', async () => {851 await usingApi(async api => {852 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});853 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});854 const token = await createItemExpectSuccess(alice, collection, 'NFT');855 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)});856 await addCollectionAdminExpectSuccess(alice, collection, bob.address);857 await transferExpectSuccess(collection, token, alice, charlie);858859 const propertyKeys: string[] = [];860 let i = 0;861 for (const permission of permissions) {862 if (!permission.permission.mutable) continue;863 864 for (const signer of permission.signers) {865 const key = i + '_' + signer.address;866 propertyKeys.push(key);867868 await expect(executeTransaction(869 api, 870 alice, 871 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 872 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;873874 await expect(executeTransaction(875 api, 876 signer, 877 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]), 878 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;879880 await expect(executeTransaction(881 api, 882 signer, 883 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin stable'}]), 884 ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;885 }886887 i++;888 }889890 const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toHuman() as any[];891 const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toHuman().properties as any[];892 for (let i = 0; i < properties.length; i++) {893 expect(properties[i].value).to.be.equal('Serotonin stable');894 expect(tokensData[i].value).to.be.equal('Serotonin stable');895 }896 });897 });898899 it('Deletes properties of a nested token according to permissions', async () => {900 await usingApi(async api => {901 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});902 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});903 const token = await createItemExpectSuccess(alice, collection, 'NFT');904 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)});905 await addCollectionAdminExpectSuccess(alice, collection, bob.address);906 await transferExpectSuccess(collection, token, alice, charlie);907908 const propertyKeys: string[] = [];909 let i = 0;910911 for (const permission of permissions) {912 if (!permission.permission.mutable) continue;913 914 for (const signer of permission.signers) {915 const key = i + '_' + signer.address;916 propertyKeys.push(key);917918 await expect(executeTransaction(919 api, 920 alice, 921 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 922 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;923924 await expect(executeTransaction(925 api, 926 signer, 927 api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]), 928 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;929930 await expect(executeTransaction(931 api, 932 signer, 933 api.tx.unique.deleteTokenProperties(collection, nestedToken, [key]), 934 ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;935 }936 937 i++;938 }939940 const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toJSON() as any[];941 expect(properties).to.be.empty;942 const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toJSON().properties as any[];943 expect(tokensData).to.be.empty;944 expect((await api.query.nonfungible.tokenProperties(collection, nestedToken)).toJSON().consumedSpace).to.be.equal(0);945 });946 });947});948949describe('Negative Integration Test: Token Properties', () => {950 let collection: number;951 let token: number;952 let originalSpace: number;953 let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];954955 before(async () => {956 await usingApi(async (api, privateKeyWrapper) => {957 alice = privateKeyWrapper('//Alice');958 bob = privateKeyWrapper('//Bob');959 charlie = privateKeyWrapper('//Charlie');960 const dave = privateKeyWrapper('//Dave');961962 constitution = [963 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},964 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},965 {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},966 {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},967 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},968 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},969 ];970 });971 });972973 async function prepare(mode: CollectionMode, pieces: number) {974 collection = await createCollectionExpectSuccess({mode: mode});975 token = await createItemExpectSuccess(alice, collection, mode.type);976 await addCollectionAdminExpectSuccess(alice, collection, bob.address);977 await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);978 979 await usingApi(async api => {980 let i = 0;981 for (const passage of constitution) {982 const signer = passage.signers[0];983 984 await expect(executeTransaction(985 api, 986 alice, 987 api.tx.unique.setTokenPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 988 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;989 990 await expect(executeTransaction(991 api, 992 signer, 993 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 994 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;995 996 i++;997 }998 999 originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;1000 }); 1001 }10021003 async function testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(mode: CollectionMode, pieces: number) {1004 await prepare(mode, pieces);1005 1006 await usingApi(async api => {1007 let i = -1;1008 for (const forbiddance of constitution) {1009 i++;1010 if (!forbiddance.permission.mutable) continue;1011 1012 await expect(executeTransaction(1013 api, 1014 forbiddance.sinner, 1015 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 1016 ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);1017 1018 await expect(executeTransaction(1019 api, 1020 forbiddance.sinner, 1021 api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 1022 ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);1023 }1024 1025 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();1026 expect(properties.consumedSpace).to.be.equal(originalSpace);1027 });1028 }1029 it('Forbids changing/deleting properties of a token if the user is outside of permissions (NFT)', async () => {1030 await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions({type: 'NFT'}, 1);1031 });1032 it('Forbids changing/deleting properties of a token if the user is outside of permissions (ReFungible)', async () => {1033 await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions({type: 'ReFungible'}, 100);1034 });10351036 async function testForbidsChangingDeletingPropertiesIfPropertyImmutable(mode: CollectionMode, pieces: number) {1037 await prepare(mode, pieces);1038 1039 await usingApi(async api => {1040 let i = -1;1041 for (const permission of constitution) {1042 i++;1043 if (permission.permission.mutable) continue;1044 1045 await expect(executeTransaction(1046 api, 1047 permission.signers[0], 1048 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 1049 ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);1050 1051 await expect(executeTransaction(1052 api, 1053 permission.signers[0], 1054 api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 1055 ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);1056 }1057 1058 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();1059 expect(properties.consumedSpace).to.be.equal(originalSpace);1060 }); 1061 }1062 it('Forbids changing/deleting properties of a token if the property is permanent (immutable) (NFT)', async () => {1063 await testForbidsChangingDeletingPropertiesIfPropertyImmutable({type: 'NFT'}, 1);1064 });1065 it('Forbids changing/deleting properties of a token if the property is permanent (immutable) (ReFungible)', async () => {1066 await testForbidsChangingDeletingPropertiesIfPropertyImmutable({type: 'ReFungible'}, 100);1067 });10681069 async function testForbidsAddingPropertiesIfPropertyNotDeclared(mode: CollectionMode, pieces: number) {1070 await prepare(mode, pieces);10711072 await usingApi(async api => {1073 await expect(executeTransaction(1074 api, 1075 alice, 1076 api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 1077 ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);1078 1079 await expect(executeTransaction(1080 api, 1081 alice, 1082 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 1083 ), 'on setting a new non-permitted property').to.not.be.rejected;1084 1085 await expect(executeTransaction(1086 api, 1087 alice, 1088 api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 1089 ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);1090 1091 expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;1092 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();1093 expect(properties.consumedSpace).to.be.equal(originalSpace);1094 });1095 }1096 it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (NFT)', async () => {1097 await testForbidsAddingPropertiesIfPropertyNotDeclared({type: 'NFT'}, 1);1098 });1099 it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (ReFungible)', async () => {1100 await testForbidsAddingPropertiesIfPropertyNotDeclared({type: 'ReFungible'}, 100);1101 });11021103 async function testForbidsAddingTooManyProperties(mode: CollectionMode, pieces: number) {1104 await prepare(mode, pieces);11051106 await usingApi(async api => {1107 await expect(executeTransaction(1108 api, 1109 alice, 1110 api.tx.unique.setTokenPropertyPermissions(collection, [1111 {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 1112 {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},1113 ]), 1114 ), 'on setting a new non-permitted property').to.not.be.rejected;1115 1116 1117 {1118 console.error = () => {};1119 await expect(executeTransaction(1120 api, 1121 alice, 1122 api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 1123 )).to.be.rejected;1124 }1125 1126 await expect(executeTransaction(1127 api, 1128 alice, 1129 api.tx.unique.setTokenProperties(collection, token, [1130 {key: 'a_holy_book', value: 'word '.repeat(3277)}, 1131 {key: 'young_years', value: 'neverending'.repeat(1490)},1132 ]), 1133 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);1134 1135 expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young_years'])).toJSON()).to.be.empty;1136 const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();1137 expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);1138 });1139 }1140 it('Forbids adding too many properties to a token (NFT)', async () => {1141 await testForbidsAddingTooManyProperties({type: 'NFT'}, 1);1142 });1143 it('Forbids adding too many properties to a token (ReFungible)', async () => {1144 await testForbidsAddingTooManyProperties({type: 'ReFungible'}, 100);1145 });1146});11471148describe('ReFungible token properties permissions tests', () => {1149 let collection: number;1150 let token: number;11511152 before(async () => {1153 await usingApi(async (api, privateKeyWrapper) => {1154 alice = privateKeyWrapper('//Alice');1155 bob = privateKeyWrapper('//Bob');1156 charlie = privateKeyWrapper('//Charlie');1157 });1158 });11591160 beforeEach(async () => {1161 await usingApi(async api => {1162 collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});1163 token = await createItemExpectSuccess(alice, collection, 'ReFungible');1164 await addCollectionAdminExpectSuccess(alice, collection, bob.address);11651166 await expect(executeTransaction(1167 api, 1168 alice, 1169 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'key', permission: {mutable:true, tokenOwner: true}}]), 1170 )).to.not.be.rejected;1171 });1172 });11731174 it('Forbids add token property with tokenOwher==true but signer have\'t all pieces', async () => {1175 await usingApi(async api => {1176 await transferExpectSuccess(collection, token, alice, charlie, 33, 'ReFungible');1177 1178 await expect(executeTransaction(1179 api, 1180 alice, 1181 api.tx.unique.setTokenProperties(collection, token, [1182 {key: 'key', value: 'word'}, 1183 ]), 1184 )).to.be.rejectedWith(/common\.NoPermission/);1185 });1186 });11871188 it('Forbids mutate token property with tokenOwher==true but signer have\'t all pieces', async () => {1189 await usingApi(async api => {1190 await expect(executeTransaction(1191 api, 1192 alice, 1193 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'key', permission: {mutable:true, tokenOwner: true}}]), 1194 )).to.not.be.rejected;1195 1196 await expect(executeTransaction(1197 api, 1198 alice, 1199 api.tx.unique.setTokenProperties(collection, token, [1200 {key: 'key', value: 'word'}, 1201 ]), 1202 )).to.be.not.rejected;12031204 await transferExpectSuccess(collection, token, alice, charlie, 33, 'ReFungible');1205 1206 await expect(executeTransaction(1207 api, 1208 alice, 1209 api.tx.unique.setTokenProperties(collection, token, [1210 {key: 'key', value: 'bad word'}, 1211 ]), 1212 )).to.be.rejectedWith(/common\.NoPermission/);1213 });1214 });12151216 it('Forbids delete token property with tokenOwher==true but signer have\'t all pieces', async () => {1217 await usingApi(async api => {1218 await expect(executeTransaction(1219 api, 1220 alice, 1221 api.tx.unique.setTokenProperties(collection, token, [1222 {key: 'key', value: 'word'}, 1223 ]), 1224 )).to.be.not.rejected;12251226 await transferExpectSuccess(collection, token, alice, charlie, 33, 'ReFungible');1227 1228 await expect(executeTransaction(1229 api, 1230 alice, 1231 api.tx.unique.deleteTokenProperties(collection, token, [1232 'key',1233 ]), 1234 )).to.be.rejectedWith(/common\.NoPermission/);1235 });1236 });1237});