1import {expect} from 'chai';2import usingApi, {executeTransaction} from '../substrate/substrate-api';3import {4 addCollectionAdminExpectSuccess,5 createCollectionExpectSuccess,6 createItemExpectSuccess,7 getCreateCollectionResult,8 transferExpectSuccess,9} from '../util/helpers';10import {IKeyringPair} from '@polkadot/types/types';1112let alice: IKeyringPair;13let bob: IKeyringPair;14let charlie: IKeyringPair;1516describe('Composite Properties Test', () => {17 before(async () => {18 await usingApi(async (api, privateKeyWrapper) => {19 alice = privateKeyWrapper('//Alice');20 bob = privateKeyWrapper('//Bob');21 });22 });2324 it('Makes sure collectionById supplies required fields', async () => {25 await usingApi(async api => {26 const collectionId = await createCollectionExpectSuccess();2728 const collectionOption = await api.rpc.unique.collectionById(collectionId);29 expect(collectionOption.isSome).to.be.true;30 let collection = collectionOption.unwrap();31 expect(collection.tokenPropertyPermissions.toHuman()).to.be.empty;32 expect(collection.properties.toHuman()).to.be.empty;3334 const propertyPermissions = [35 {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},36 {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},37 ];38 await expect(executeTransaction(39 api, 40 alice, 41 api.tx.unique.setTokenPropertyPermissions(collectionId, propertyPermissions), 42 )).to.not.be.rejected;4344 const collectionProperties = [45 {key: 'black_hole', value: 'LIGO'},46 {key: 'electron', value: 'come bond'}, 47 ];48 await expect(executeTransaction(49 api, 50 alice, 51 api.tx.unique.setCollectionProperties(collectionId, collectionProperties), 52 )).to.not.be.rejected;5354 collection = (await api.rpc.unique.collectionById(collectionId)).unwrap();55 expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);56 expect(collection.properties.toHuman()).to.be.deep.equal(collectionProperties);57 });58 });59});60616263describe('Integration Test: Collection Properties', () => {64 before(async () => {65 await usingApi(async (api, privateKeyWrapper) => {66 alice = privateKeyWrapper('//Alice');67 bob = privateKeyWrapper('//Bob');68 });69 });7071 it('Reads properties from a collection', async () => {72 await usingApi(async api => {73 const collection = await createCollectionExpectSuccess();74 const properties = (await api.query.common.collectionProperties(collection)).toJSON();75 expect(properties.map).to.be.empty;76 expect(properties.consumedSpace).to.equal(0);77 });78 });7980 it('Sets properties for a collection', async () => {81 await usingApi(async api => {82 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));83 const {collectionId} = getCreateCollectionResult(events);8485 86 await expect(executeTransaction(87 api, 88 bob, 89 api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 90 )).to.not.be.rejected;9192 await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);9394 95 await expect(executeTransaction(96 api, 97 alice, 98 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 99 )).to.not.be.rejected;100101 const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();102 expect(properties).to.be.deep.equal([103 {key: 'electron', value: 'come bond'},104 {key: 'black_hole', value: ''},105 ]);106 });107 });108109 it('Check valid names for collection properties keys', async () => {110 await usingApi(async api => {111 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));112 const {collectionId} = getCreateCollectionResult(events);113114 115 await expect(executeTransaction(116 api, 117 bob, 118 api.tx.unique.setCollectionProperties(collectionId, [{key: 'alpha'}]), 119 )).to.not.be.rejected;120121 122 await expect(executeTransaction(123 api, 124 bob, 125 api.tx.unique.setCollectionProperties(collectionId, [{key: '123'}]), 126 )).to.not.be.rejected;127128 129 await expect(executeTransaction(130 api, 131 bob, 132 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 133 )).to.not.be.rejected;134135 136 await expect(executeTransaction(137 api, 138 bob, 139 api.tx.unique.setCollectionProperties(collectionId, [{key: 'semi-automatic'}]), 140 )).to.not.be.rejected;141142 143 await expect(executeTransaction(144 api, 145 bob, 146 api.tx.unique.setCollectionProperties(collectionId, [{key: 'build.rs'}]), 147 )).to.not.be.rejected;148149 const propertyKeys = ['alpha', '123', 'black_hole', 'semi-automatic', 'build.rs'];150 const properties = (await api.rpc.unique.collectionProperties(collectionId, propertyKeys)).toHuman();151 expect(properties).to.be.deep.equal([152 {key: 'alpha', value: ''},153 {key: '123', value: ''},154 {key: 'black_hole', value: ''},155 {key: 'semi-automatic', value: ''},156 {key: 'build.rs', value: ''},157 ]);158 });159 });160161 it('Changes properties of a collection', async () => {162 await usingApi(async api => {163 const collection = await createCollectionExpectSuccess();164165 await expect(executeTransaction(166 api, 167 alice, 168 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 169 )).to.not.be.rejected;170171 172 await expect(executeTransaction(173 api, 174 alice, 175 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 176 )).to.not.be.rejected;177178 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();179 expect(properties).to.be.deep.equal([180 {key: 'electron', value: 'bonded'},181 {key: 'black_hole', value: 'LIGO'},182 ]);183 });184 });185186 it('Deletes properties of a collection', async () => {187 await usingApi(async api => {188 const collection = await createCollectionExpectSuccess();189190 await expect(executeTransaction(191 api, 192 alice, 193 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 194 )).to.not.be.rejected;195196 await expect(executeTransaction(197 api, 198 alice, 199 api.tx.unique.deleteCollectionProperties(collection, ['electron']), 200 )).to.not.be.rejected;201202 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();203 expect(properties).to.be.deep.equal([204 {key: 'black_hole', value: 'LIGO'},205 ]);206 });207 });208});209210describe('Negative Integration Test: Collection Properties', () => {211 before(async () => {212 await usingApi(async (api, privateKeyWrapper) => {213 alice = privateKeyWrapper('//Alice');214 bob = privateKeyWrapper('//Bob');215 });216 });217 218 it('Fails to set properties in a collection if not its onwer/administrator', async () => {219 await usingApi(async api => {220 const collection = await createCollectionExpectSuccess();221222 await expect(executeTransaction(223 api, 224 bob, 225 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 226 )).to.be.rejectedWith(/common\.NoPermission/);227 228 const properties = (await api.query.common.collectionProperties(collection)).toJSON();229 expect(properties.map).to.be.empty;230 expect(properties.consumedSpace).to.equal(0);231 });232 });233 234 it('Fails to set properties that exceed the limits', async () => {235 await usingApi(async api => {236 const collection = await createCollectionExpectSuccess();237 const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 238239 240 {241 console.error = () => {};242 await expect(executeTransaction(243 api, 244 alice, 245 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 246 )).to.be.rejected;247 }248249 let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();250 expect(properties).to.be.empty;251252 await expect(executeTransaction(253 api, 254 alice, 255 api.tx.unique.setCollectionProperties(collection, [256 {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 257 {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 258 ]), 259 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);260261 properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();262 expect(properties).to.be.empty;263 });264 });265 266 it('Fails to set more properties than it is allowed', async () => {267 await usingApi(async api => {268 const collection = await createCollectionExpectSuccess();269270 const propertiesToBeSet = [];271 for (let i = 0; i < 65; i++) {272 propertiesToBeSet.push({273 key: 'electron_' + i,274 value: Math.random() > 0.5 ? 'high' : 'low',275 });276 }277278 await expect(executeTransaction(279 api, 280 alice, 281 api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 282 )).to.be.rejectedWith(/common\.PropertyLimitReached/);283284 const properties = (await api.query.common.collectionProperties(collection)).toJSON();285 expect(properties.map).to.be.empty;286 expect(properties.consumedSpace).to.equal(0);287 });288 });289290 it('Fails to set properties with invalid names', async () => {291 await usingApi(async api => {292 const collection = await createCollectionExpectSuccess();293294 const invalidProperties = [295 [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],296 [{key: 'Mr/Sandman', value: 'Bring me a gene'}],297 [{key: 'déjà vu', value: 'hmm...'}],298 ];299300 for (let i = 0; i < invalidProperties.length; i++) {301 await expect(executeTransaction(302 api, 303 alice, 304 api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 305 ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);306 }307308 await expect(executeTransaction(309 api, 310 alice, 311 api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 312 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);313314 await expect(executeTransaction(315 api, 316 alice, 317 api.tx.unique.setCollectionProperties(collection, [318 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},319 ]), 320 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;321322 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');323324 const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();325 expect(properties).to.be.deep.equal([326 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},327 ]);328329 for (let i = 0; i < invalidProperties.length; i++) {330 await expect(executeTransaction(331 api, 332 alice, 333 api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 334 ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);335 }336 });337 });338});339340341342describe('Integration Test: Access Rights to Token Properties', () => {343 before(async () => {344 await usingApi(async (api, privateKeyWrapper) => {345 alice = privateKeyWrapper('//Alice');346 bob = privateKeyWrapper('//Bob');347 });348 });349 350 it('Reads access rights to properties of a collection', async () => {351 await usingApi(async api => {352 const collection = await createCollectionExpectSuccess();353 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();354 expect(propertyRights).to.be.empty;355 });356 });357 358 it('Sets access rights to properties of a collection', async () => {359 await usingApi(async api => {360 const collection = await createCollectionExpectSuccess();361362 await expect(executeTransaction(363 api, 364 alice, 365 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 366 )).to.not.be.rejected;367368 await addCollectionAdminExpectSuccess(alice, collection, bob.address);369370 await expect(executeTransaction(371 api, 372 alice, 373 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 374 )).to.not.be.rejected;375376 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();377 expect(propertyRights).to.be.deep.equal([378 {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},379 {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},380 ]);381 });382 });383 384 it('Changes access rights to properties of a collection', async () => {385 await usingApi(async api => {386 const collection = await createCollectionExpectSuccess();387388 await expect(executeTransaction(389 api, 390 alice, 391 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 392 )).to.not.be.rejected;393394 await expect(executeTransaction(395 api, 396 alice, 397 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 398 )).to.not.be.rejected;399400 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();401 expect(propertyRights).to.be.deep.equal([402 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},403 ]);404 });405 });406});407408describe('Negative Integration Test: Access Rights to Token Properties', () => {409 before(async () => {410 await usingApi(async (api, privateKeyWrapper) => {411 alice = privateKeyWrapper('//Alice');412 bob = privateKeyWrapper('//Bob');413 });414 });415416 it('Prevents from setting access rights to properties of a collection if not an onwer/admin', async () => {417 await usingApi(async api => {418 const collection = await createCollectionExpectSuccess();419420 await expect(executeTransaction(421 api, 422 bob, 423 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 424 )).to.be.rejectedWith(/common\.NoPermission/);425426 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();427 expect(propertyRights).to.be.empty;428 });429 });430431 it('Prevents from adding too many possible properties', async () => {432 await usingApi(async api => {433 const collection = await createCollectionExpectSuccess();434435 const constitution = [];436 for (let i = 0; i < 65; i++) {437 constitution.push({438 key: 'property_' + i,439 permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},440 });441 }442443 await expect(executeTransaction(444 api, 445 alice, 446 api.tx.unique.setTokenPropertyPermissions(collection, constitution), 447 )).to.be.rejectedWith(/common\.PropertyLimitReached/);448449 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();450 expect(propertyRights).to.be.empty;451 });452 });453454 it('Prevents access rights to be modified if constant', async () => {455 await usingApi(async api => {456 const collection = await createCollectionExpectSuccess();457458 await expect(executeTransaction(459 api, 460 alice, 461 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 462 )).to.not.be.rejected;463464 await expect(executeTransaction(465 api, 466 alice, 467 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 468 )).to.be.rejectedWith(/common\.NoPermission/);469470 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();471 expect(propertyRights).to.deep.equal([472 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},473 ]);474 });475 });476477 it('Prevents adding properties with invalid names', async () => {478 await usingApi(async api => {479 const collection = await createCollectionExpectSuccess();480481 const invalidProperties = [482 [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],483 [{key: 'G#4', permission: {tokenOwner: true}}],484 [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],485 ];486487 for (let i = 0; i < invalidProperties.length; i++) {488 await expect(executeTransaction(489 api, 490 alice, 491 api.tx.unique.setTokenPropertyPermissions(collection, invalidProperties[i]), 492 ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);493 }494495 await expect(executeTransaction(496 api, 497 alice, 498 api.tx.unique.setTokenPropertyPermissions(collection, [{key: '', permission: {}}]), 499 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);500501 const correctKey = '--0x03116e387820CA05'; 502 await expect(executeTransaction(503 api, 504 alice, 505 api.tx.unique.setTokenPropertyPermissions(collection, [506 {key: correctKey, permission: {collectionAdmin: true}},507 ]), 508 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;509510 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');511512 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();513 expect(propertyRights).to.be.deep.equal([514 {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},515 ]);516 });517 });518});519520521522describe('Integration Test: Token Properties', () => {523 let collection: number;524 let token: number;525 let permissions: {permission: any, signers: IKeyringPair[]}[];526527 before(async () => {528 await usingApi(async (api, privateKeyWrapper) => {529 alice = privateKeyWrapper('//Alice');530 bob = privateKeyWrapper('//Bob');531 charlie = privateKeyWrapper('//Charlie');532533 permissions = [534 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},535 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},536 {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},537 {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},538 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},539 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},540 ];541 });542 });543544 beforeEach(async () => {545 await usingApi(async () => {546 collection = await createCollectionExpectSuccess();547 token = await createItemExpectSuccess(alice, collection, 'NFT');548 await addCollectionAdminExpectSuccess(alice, collection, bob.address);549 await transferExpectSuccess(collection, token, alice, charlie);550 });551 });552 553 it('Reads yet empty properties of a token', async () => {554 await usingApi(async api => {555 const collection = await createCollectionExpectSuccess();556 const token = await createItemExpectSuccess(alice, collection, 'NFT');557 558 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();559 expect(properties.map).to.be.empty;560 expect(properties.consumedSpace).to.be.equal(0);561562 const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;563 expect(tokenData).to.be.empty;564 });565 });566567 it('Assigns properties to a token according to permissions', async () => {568 await usingApi(async api => {569 const propertyKeys: string[] = [];570 let i = 0;571 for (const permission of permissions) {572 for (const signer of permission.signers) {573 const key = i + '_' + signer.address;574 propertyKeys.push(key);575576 await expect(executeTransaction(577 api, 578 alice, 579 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 580 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;581582 await expect(executeTransaction(583 api, 584 signer, 585 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 586 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;587 }588589 i++;590 }591592 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];593 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];594 for (let i = 0; i < properties.length; i++) {595 expect(properties[i].value).to.be.equal('Serotonin increase');596 expect(tokensData[i].value).to.be.equal('Serotonin increase');597 }598 });599 });600601 it('Changes properties of a token according to permissions', async () => {602 await usingApi(async api => {603 const propertyKeys: string[] = [];604 let i = 0;605 for (const permission of permissions) {606 if (!permission.permission.mutable) continue;607 608 for (const signer of permission.signers) {609 const key = i + '_' + signer.address;610 propertyKeys.push(key);611612 await expect(executeTransaction(613 api, 614 alice, 615 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 616 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;617618 await expect(executeTransaction(619 api, 620 signer, 621 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 622 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;623624 await expect(executeTransaction(625 api, 626 signer, 627 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 628 ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;629 }630631 i++;632 }633634 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];635 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];636 for (let i = 0; i < properties.length; i++) {637 expect(properties[i].value).to.be.equal('Serotonin stable');638 expect(tokensData[i].value).to.be.equal('Serotonin stable');639 }640 });641 });642643 it('Deletes properties of a token according to permissions', async () => {644 await usingApi(async api => {645 const propertyKeys: string[] = [];646 let i = 0;647648 for (const permission of permissions) {649 if (!permission.permission.mutable) continue;650 651 for (const signer of permission.signers) {652 const key = i + '_' + signer.address;653 propertyKeys.push(key);654655 await expect(executeTransaction(656 api, 657 alice, 658 api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 659 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;660661 await expect(executeTransaction(662 api, 663 signer, 664 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 665 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;666667 await expect(executeTransaction(668 api, 669 signer, 670 api.tx.unique.deleteTokenProperties(collection, token, [key]), 671 ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;672 }673 674 i++;675 }676677 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];678 expect(properties).to.be.empty;679 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];680 expect(tokensData).to.be.empty;681 expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);682 });683 });684});685686describe('Negative Integration Test: Token Properties', () => {687 let collection: number;688 let token: number;689 let originalSpace: number;690 let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];691692 before(async () => {693 await usingApi(async (api, privateKeyWrapper) => {694 alice = privateKeyWrapper('//Alice');695 bob = privateKeyWrapper('//Bob');696 charlie = privateKeyWrapper('//Charlie');697 const dave = privateKeyWrapper('//Dave');698699 constitution = [700 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},701 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},702 {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},703 {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},704 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},705 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},706 ];707 });708 });709710 beforeEach(async () => {711 collection = await createCollectionExpectSuccess();712 token = await createItemExpectSuccess(alice, collection, 'NFT');713 await addCollectionAdminExpectSuccess(alice, collection, bob.address);714 await transferExpectSuccess(collection, token, alice, charlie);715 716 await usingApi(async api => {717 let i = 0;718 for (const passage of constitution) {719 const signer = passage.signers[0];720 721 await expect(executeTransaction(722 api, 723 alice, 724 api.tx.unique.setTokenPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 725 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;726727 await expect(executeTransaction(728 api, 729 signer, 730 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 731 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;732733 i++;734 }735736 originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;737 });738 });739740 it('Forbids changing/deleting properties of a token if the user is outside of permissions', async () => {741 await usingApi(async api => {742 let i = -1;743 for (const forbiddance of constitution) {744 i++;745 if (!forbiddance.permission.mutable) continue;746747 await expect(executeTransaction(748 api, 749 forbiddance.sinner, 750 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 751 ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);752753 await expect(executeTransaction(754 api, 755 forbiddance.sinner, 756 api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 757 ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);758 }759760 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();761 expect(properties.consumedSpace).to.be.equal(originalSpace);762 });763 });764765 it('Forbids changing/deleting properties of a token if the property is permanent (immutable)', async () => {766 await usingApi(async api => {767 let i = -1;768 for (const permission of constitution) {769 i++;770 if (permission.permission.mutable) continue;771772 await expect(executeTransaction(773 api, 774 permission.signers[0], 775 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 776 ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);777778 await expect(executeTransaction(779 api, 780 permission.signers[0], 781 api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 782 ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);783 }784785 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();786 expect(properties.consumedSpace).to.be.equal(originalSpace);787 });788 });789790 it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission', async () => {791 await usingApi(async api => {792 await expect(executeTransaction(793 api, 794 alice, 795 api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 796 ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);797 798 await expect(executeTransaction(799 api, 800 alice, 801 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 802 ), 'on setting a new non-permitted property').to.not.be.rejected;803804 await expect(executeTransaction(805 api, 806 alice, 807 api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 808 ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);809810 expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;811 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();812 expect(properties.consumedSpace).to.be.equal(originalSpace);813 });814 });815816 it('Forbids adding too many properties to a token', async () => {817 await usingApi(async api => {818 await expect(executeTransaction(819 api, 820 alice, 821 api.tx.unique.setTokenPropertyPermissions(collection, [822 {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 823 {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},824 ]), 825 ), 'on setting a new non-permitted property').to.not.be.rejected;826827 828 {829 console.error = () => {};830 await expect(executeTransaction(831 api, 832 alice, 833 api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 834 )).to.be.rejected;835 }836837 await expect(executeTransaction(838 api, 839 alice, 840 api.tx.unique.setTokenProperties(collection, token, [841 {key: 'a_holy_book', value: 'word '.repeat(3277)}, 842 {key: 'young_years', value: 'neverending'.repeat(1490)},843 ]), 844 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);845846 expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young_years'])).toJSON()).to.be.empty;847 const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();848 expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);849 });850 });851});