difftreelog
test(structure) extra nesting + properties refactoring
in: master
7 files changed
tests/src/createItem.test.tsdiffbeforeafterboth--- a/tests/src/createItem.test.ts
+++ b/tests/src/createItem.test.ts
@@ -144,7 +144,6 @@
it('User doesnt have editing rights', async () => {
await usingApi(async api => {
- const createMode = 'NFT';
const newCollectionID = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'key1', mutable: true, collectionAdmin: false, tokenOwner: false}]});
const token = await createItemExpectSuccess(alice, newCollectionID, 'NFT');
tests/src/createMultipleItems.test.tsdiffbeforeafterboth--- a/tests/src/createMultipleItems.test.ts
+++ b/tests/src/createMultipleItems.test.ts
@@ -146,7 +146,6 @@
const itemsListIndexBefore = await getLastTokenId(api, collectionId);
expect(itemsListIndexBefore).to.be.equal(0);
const alice = privateKey('//Alice');
- const bob = privateKey('//Bob');
const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
{Nft: {const_data: '0x32', variable_data: '0x32'}},
{Nft: {const_data: '0x33', variable_data: '0x33'}}];
@@ -182,7 +181,6 @@
const itemsListIndexBefore = await getLastTokenId(api, collectionId);
expect(itemsListIndexBefore).to.be.equal(0);
const alice = privateKey('//Alice');
- const bob = privateKey('//Bob');
const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
{Nft: {const_data: '0x32', variable_data: '0x32'}},
{Nft: {const_data: '0x33', variable_data: '0x33'}}];
@@ -218,7 +216,6 @@
const itemsListIndexBefore = await getLastTokenId(api, collectionId);
expect(itemsListIndexBefore).to.be.equal(0);
const alice = privateKey('//Alice');
- const bob = privateKey('//Bob');
const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
{Nft: {const_data: '0x32', variable_data: '0x32'}},
{Nft: {const_data: '0x33', variable_data: '0x33'}}];
tests/src/nesting/migration-check.test.tsdiffbeforeafterboth--- a/tests/src/nesting/migration-check.test.ts
+++ b/tests/src/nesting/migration-check.test.ts
@@ -8,8 +8,8 @@
// Used for polkadot-launch signalling
import find from 'find-process';
-// todo skip
-describe('Migration testing for pallet-common', () => {
+// todo un-skip for migrations
+describe.skip('Migration testing for pallet-common', () => {
let alice: IKeyringPair;
before(async() => {
@@ -63,16 +63,33 @@
});
// And wait for the parachain upgrade
- while (newVersion == oldVersion! && connectionFailCounter < 2) {
- try {
- await usingApi(async api => {
- await waitNewBlocks(api);
- newVersion = (api.consts.system.version.toJSON() as any).specVersion;
- });
- } catch (_) {
- connectionFailCounter++;
- console.log(`Still waiting for the parachain upgrade from ${oldVersion!}...`);
- await new Promise(resolve => setTimeout(resolve, 12000));
+ {
+ // Catch warnings like 'RPC methods not decorated' and keep the 'waiting' message in front
+ const stdlog = console.warn.bind(console);
+ let warnCount = 0;
+ console.warn = function(...args){
+ if (arguments.length <= 2 || !args[2].includes('RPC methods not decorated')) {
+ warnCount++;
+ stdlog.apply(console, args as any);
+ }
+ };
+
+ let oldWarnCount = 0;
+ while (newVersion == oldVersion! && connectionFailCounter < 2) {
+ try {
+ await usingApi(async api => {
+ await waitNewBlocks(api);
+ newVersion = (api.consts.system.version.toJSON() as any).specVersion;
+ if (warnCount > oldWarnCount) {
+ console.log(`Still waiting for the parachain upgrade from ${oldVersion!}...`);
+ oldWarnCount = warnCount;
+ }
+ await new Promise(resolve => setTimeout(resolve, 6000));
+ });
+ } catch (_) {
+ connectionFailCounter++;
+ await new Promise(resolve => setTimeout(resolve, 12000));
+ }
}
}
tests/src/nesting/nest.test.tsdiffbeforeafterboth--- a/tests/src/nesting/nest.test.ts
+++ b/tests/src/nesting/nest.test.ts
@@ -9,8 +9,7 @@
enableAllowListExpectSuccess,
enablePublicMintingExpectSuccess,
getTokenOwner,
- getTopmostTokenOwner,
- normalizeAccountId,
+ getTopmostTokenOwner,
setCollectionLimitsExpectSuccess,
transferExpectFailure,
transferExpectSuccess,
@@ -23,7 +22,7 @@
describe('Integration Test: Nesting', () => {
before(async () => {
- await usingApi(async api => {
+ await usingApi(async () => {
alice = privateKey('//Alice');
bob = privateKey('//Bob');
});
@@ -196,7 +195,7 @@
describe('Negative Test: Nesting', async() => {
before(async () => {
- await usingApi(async api => {
+ await usingApi(async () => {
alice = privateKey('//Alice');
bob = privateKey('//Bob');
});
@@ -212,14 +211,11 @@
const nestedToken1 = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
const nestedToken2 = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, nestedToken1)});
// The nesting depth is limited by 2
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, nestedToken2)},
+ await expect(executeTransaction(api, alice, api.tx.unique.createItem(
+ collection,
+ {Ethereum: tokenIdToAddress(collection, nestedToken2)},
{nft: {const_data: [], variable_data: []}} as any,
- ),
- ), 'while creating nested token').to.be.rejectedWith(/^structure\.DepthLimit$/); // OuroborosDetected?
+ )), 'while creating nested token').to.be.rejectedWith(/^structure\.DepthLimit$/);
expect(await getTopmostTokenOwner(api, collection, nestedToken2)).to.be.deep.equal({Substrate: alice.address});
});
@@ -234,24 +230,16 @@
const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
// Try to create a nested token
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
+ await expect(executeTransaction(api, alice, api.tx.unique.createItem(
+ collection,
+ {Ethereum: tokenIdToAddress(collection, targetToken)},
{nft: {const_data: [], variable_data: []}} as any,
- ),
- ), 'while creating nested token').to.be.rejectedWith(/^common\.NestingIsDisabled$/);
+ )), 'while creating nested token').to.be.rejectedWith(/^common\.NestingIsDisabled$/);
// Create a token to be nested
const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
// Try to nest
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.transfer(
- normalizeAccountId({Ethereum: tokenIdToAddress(collection, targetToken)}), collection, newToken, 1,
- ),
- ), 'while nesting new token').to.be.rejectedWith(/common\.NestingIsDisabled/); // todo to.be.rejected for all
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.NestingIsDisabled/);
expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
});
@@ -270,23 +258,15 @@
const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');
// Try to create a nested token in the wrong collection
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
+ await expect(executeTransaction(api, alice, api.tx.unique.createItem(
+ collection,
+ {Ethereum: tokenIdToAddress(collection, targetToken)},
{nft: {const_data: [], variable_data: []}} as any,
- ),
- ), 'while creating nested token').to.be.rejectedWith(/structure\.DepthLimit/); // todo NestingIsDisabled?
+ )), 'while creating nested token').to.be.rejectedWith(/common\.OnlyOwnerAllowedToNest/);
// Try to create and nest a token in the wrong collection
const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.transfer(
- normalizeAccountId({Ethereum: tokenIdToAddress(collection, targetToken)}), collection, newToken, 1,
- ),
- ), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
});
});
@@ -304,23 +284,15 @@
const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');
// Try to create a nested token in the wrong collection
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
+ await expect(executeTransaction(api, alice, api.tx.unique.createItem(
+ collection,
+ {Ethereum: tokenIdToAddress(collection, targetToken)},
{nft: {const_data: [], variable_data: []}} as any,
- ),
- ), 'while creating nested token').to.be.rejectedWith(/structure\.DepthLimit/);
+ )), 'while creating nested token').to.be.rejectedWith(/common\.OnlyOwnerAllowedToNest/);
// Try to create and nest a token in the wrong collection
const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.transfer(
- normalizeAccountId({Ethereum: tokenIdToAddress(collection, targetToken)}), collection, newToken, 1,
- ),
- ), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
});
});
@@ -334,23 +306,15 @@
const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
// Try to create a nested token in the wrong collection
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
+ await expect(executeTransaction(api, alice, api.tx.unique.createItem(
+ collection,
+ {Ethereum: tokenIdToAddress(collection, targetToken)},
{nft: {const_data: [], variable_data: []}} as any,
- ),
- ), 'while creating nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+ )), 'while creating nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
// Try to create and nest a token in the wrong collection
const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.transfer(
- normalizeAccountId({Ethereum: tokenIdToAddress(collection, targetToken)}), collection, newToken, 1,
- ),
- ), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
});
});
@@ -367,24 +331,21 @@
const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
// Try to create a nested token
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.createItem(
- collectionFT,
- targetAddress,
- {Fungible: {Value: 10}},
- )
- ), 'while creating nested token').to.be.rejectedWith(/^common\.NestingIsDisabled$/);
-
+ await expect(executeTransaction(api, alice, api.tx.unique.createItem(
+ collectionFT,
+ targetAddress,
+ {Fungible: {Value: 10}},
+ )), 'while creating nested token').to.be.rejectedWith(/^common\.NestingIsDisabled$/);
+
// Create a token to be nested
const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
// Try to nest
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.transfer(
- normalizeAccountId({Ethereum: tokenIdToAddress(collectionFT, targetToken)}), collectionFT, newToken, 1,
- ),
- ), 'while nesting new token').to.be.rejectedWith(/fungible\.FungibleDisallowsNesting/);
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.NestingIsDisabled/);
+
+ // Create another token to be nested
+ const newToken2 = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
+ // Try to nest inside a fungible token
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionFT, newToken)}, collectionFT, newToken2, 1)), 'while nesting new token inside fungible').to.be.rejectedWith(/fungible\.FungibleDisallowsNesting/);
});
});
@@ -404,31 +365,21 @@
const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
// Try to create a nested token in the wrong collection
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.createItem(
- collectionFT,
- targetAddress,
- {Fungible: {Value: 10}},
- )
- ), 'while creating nested token').to.be.rejectedWith(/structure\.DepthLimit/);
+ await expect(executeTransaction(api, alice, api.tx.unique.createItem(
+ collectionFT,
+ targetAddress,
+ {Fungible: {Value: 10}},
+ )), 'while creating nested token').to.be.rejectedWith(/common\.OnlyOwnerAllowedToNest/);
// Try to create and nest a token in the wrong collection
const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- await expect(executeTransaction(
- api, alice,
- api.tx.unique.transfer(
- normalizeAccountId({Ethereum: tokenIdToAddress(collectionFT, targetToken)}), collectionFT, newToken, 1,
- ),
- ), 'while nesting new token').to.be.rejectedWith(/fungible\.FungibleDisallowsNesting/);
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.OnlyOwnerAllowedToNest/);
});
});
it('Fungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {
await usingApi(async api => {
const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionLimitsExpectSuccess(alice, collectionNFT, {nestingRule: {OwnerRestricted:[collectionNFT]}}); // todo clear redundant restrictions?
-
await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);
await enableAllowListExpectSuccess(alice, collectionNFT);
await enablePublicMintingExpectSuccess(alice, collectionNFT);
@@ -438,17 +389,18 @@
const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await setCollectionLimitsExpectSuccess(alice, collectionNFT, {nestingRule: {OwnerRestricted:[collectionFT]}});
// Try to create a nested token in the wrong collection
await expect(executeTransaction(api, alice, api.tx.unique.createItem(
collectionFT,
targetAddress,
{Fungible: {Value: 10}},
- ))).to.be.rejected;
+ )), 'while creating nested token').to.be.rejectedWith(/common\.OnlyOwnerAllowedToNest/);
// Try to create and nest a token in the wrong collection
const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- await transferExpectFailure(collectionFT, newToken, alice, targetAddress, 1);
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.OnlyOwnerAllowedToNest/);
});
});
@@ -468,11 +420,11 @@
collectionFT,
targetAddress,
{Fungible: {Value: 10}},
- ))).to.be.rejected;
+ )), 'while creating a nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
// Try to create and nest a token in the wrong collection
const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- await transferExpectFailure(collectionFT, newToken, alice, targetAddress, 1);
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
});
});
@@ -492,12 +444,19 @@
collectionRFT,
targetAddress,
{ReFungible: {const_data: [], pieces: 100}},
- ))).to.be.rejectedWith(/^common\.NestingIsDisabled$/);
+ )), 'while creating a nested token').to.be.rejectedWith(/^common\.NestingIsDisabled$/);
// Create a token to be nested
const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
// Try to nest
await transferExpectFailure(collectionRFT, newToken, alice, targetAddress, 100);
+ // Try to nest
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.NestingIsDisabled/);
+
+ // Create another token to be nested
+ const newToken2 = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
+ // Try to nest inside a fungible token
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionRFT, newToken)}, collectionRFT, newToken2, 1)), 'while nesting new token inside refungible').to.be.rejectedWith(/refungible\.RefungibleDisallowsNesting/);
});
});
@@ -521,19 +480,17 @@
collectionRFT,
targetAddress,
{ReFungible: {const_data: [], pieces: 100}},
- ))).to.be.rejected;
+ )), 'while creating a nested token').to.be.rejectedWith(/common\.OnlyOwnerAllowedToNest/);
// Try to create and nest a token in the wrong collection
const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await transferExpectFailure(collectionRFT, newToken, alice, targetAddress, 100);
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.OnlyOwnerAllowedToNest/);
});
});
it('ReFungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {
await usingApi(async api => {
const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionLimitsExpectSuccess(alice, collectionNFT, {nestingRule: {OwnerRestricted:[collectionNFT]}});
-
await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);
await enableAllowListExpectSuccess(alice, collectionNFT);
await enablePublicMintingExpectSuccess(alice, collectionNFT);
@@ -543,17 +500,18 @@
const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ await setCollectionLimitsExpectSuccess(alice, collectionNFT, {nestingRule: {OwnerRestricted:[collectionRFT]}});
// Try to create a nested token in the wrong collection
await expect(executeTransaction(api, alice, api.tx.unique.createItem(
collectionRFT,
targetAddress,
{ReFungible: {const_data: [], pieces: 100}},
- ))).to.be.rejected;
+ )), 'while creating a nested token').to.be.rejectedWith(/common\.OnlyOwnerAllowedToNest/);
// Try to create and nest a token in the wrong collection
const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await transferExpectFailure(collectionRFT, newToken, alice, targetAddress, 100);
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.OnlyOwnerAllowedToNest/);
});
});
@@ -573,11 +531,11 @@
collectionRFT,
targetAddress,
{ReFungible: {const_data: [], pieces: 100}},
- ))).to.be.rejected;
+ )), 'while creating a nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
// Try to create and nest a token in the wrong collection
const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await transferExpectFailure(collectionRFT, newToken, alice, targetAddress, 100);
+ await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
});
});
});
tests/src/nesting/properties.test.tsdiffbeforeafterboth1import {expect} from 'chai';2import privateKey from '../substrate/privateKey';3import usingApi, {executeTransaction} from '../substrate/substrate-api';4import {5 addCollectionAdminExpectSuccess,6 createCollectionExpectSuccess,7 createItemExpectSuccess,8 getCreateCollectionResult,9 transferExpectSuccess,10} from '../util/helpers';11import {IKeyringPair} from '@polkadot/types/types';1213let alice: IKeyringPair;14let bob: IKeyringPair;15let charlie: IKeyringPair;1617// ---------- COLLECTION PROPERTIES1819describe('Integration Test: Collection Properties', () => {20 before(async () => {21 await usingApi(async api => {22 alice = privateKey('//Alice');23 bob = privateKey('//Bob');24 });25 });2627 it('Reads properties from a collection', async () => {28 await usingApi(async api => {29 const collection = await createCollectionExpectSuccess();30 const properties = (await api.query.common.collectionProperties(collection)).toJSON();31 expect(properties.map).to.be.empty;32 expect(properties.consumedSpace).to.equal(0);33 });34 });3536 it('Sets properties for a collection', async () => {37 await usingApi(async api => {38 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));39 const {collectionId} = getCreateCollectionResult(events);4041 // As owner42 await expect(executeTransaction(43 api, 44 bob, 45 api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 46 )).to.not.be.rejected;4748 await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);4950 // As administrator51 await expect(executeTransaction(52 api, 53 alice, 54 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 55 )).to.not.be.rejected;5657 const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();58 expect(properties).to.be.deep.equal([59 {key: 'electron', value: 'come bond'},60 {key: 'black_hole', value: ''},61 ]);62 });63 });6465 it('Changes properties of a collection', async () => {66 await usingApi(async api => {67 const collection = await createCollectionExpectSuccess();6869 await expect(executeTransaction(70 api, 71 alice, 72 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 73 )).to.not.be.rejected;7475 // Mutate the properties76 await expect(executeTransaction(77 api, 78 alice, 79 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 80 )).to.not.be.rejected;8182 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();83 expect(properties).to.be.deep.equal([84 {key: 'electron', value: 'bonded'},85 {key: 'black_hole', value: 'LIGO'},86 ]);87 });88 });8990 it('Deletes properties of a collection', async () => {91 await usingApi(async api => {92 const collection = await createCollectionExpectSuccess();9394 await expect(executeTransaction(95 api, 96 alice, 97 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 98 )).to.not.be.rejected;99100 await expect(executeTransaction(101 api, 102 alice, 103 api.tx.unique.deleteCollectionProperties(collection, ['electron']), 104 )).to.not.be.rejected;105106 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();107 expect(properties).to.be.deep.equal([108 {key: 'black_hole', value: 'LIGO'},109 ]);110 });111 });112});113114describe('Negative Integration Test: Collection Properties', () => {115 before(async () => {116 await usingApi(async api => {117 alice = privateKey('//Alice');118 bob = privateKey('//Bob');119 });120 });121 122 it('Fails to set properties in a collection if not its onwer/administrator', async () => {123 await usingApi(async api => {124 const collection = await createCollectionExpectSuccess();125126 await expect(executeTransaction(127 api, 128 bob, 129 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 130 )).to.be.rejectedWith(/common\.NoPermission/);131 132 const properties = (await api.query.common.collectionProperties(collection)).toJSON();133 expect(properties.map).to.be.empty;134 expect(properties.consumedSpace).to.equal(0);135 });136 });137 138 it('Fails to set properties that exceed the limits', async () => {139 await usingApi(async api => {140 const collection = await createCollectionExpectSuccess();141 const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 142143 // Mute the general tx parsing error, too many bytes to process144 {145 console.error = () => {};146 await expect(executeTransaction(147 api, 148 alice, 149 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 150 )).to.be.rejected;151 }152153 let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();154 expect(properties).to.be.empty;155156 await expect(executeTransaction(157 api, 158 alice, 159 api.tx.unique.setCollectionProperties(collection, [160 {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 161 {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 162 ]), 163 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);164165 properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();166 expect(properties).to.be.empty;167 });168 });169 170 it('Fails to set more properties than it is allowed', async () => {171 await usingApi(async api => {172 const collection = await createCollectionExpectSuccess();173174 const propertiesToBeSet = [];175 for (let i = 0; i < 65; i++) {176 propertiesToBeSet.push({177 key: 'electron_' + i,178 value: Math.random() > 0.5 ? 'high' : 'low',179 });180 }181182 await expect(executeTransaction(183 api, 184 alice, 185 api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 186 )).to.be.rejectedWith(/common\.PropertyLimitReached/);187188 const properties = (await api.query.common.collectionProperties(collection)).toJSON();189 expect(properties.map).to.be.empty;190 expect(properties.consumedSpace).to.equal(0);191 });192 });193194 it('Fails to set properties with invalid names', async () => {195 await usingApi(async api => {196 const collection = await createCollectionExpectSuccess();197198 const invalidProperties = [199 [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],200 [{key: 'Mr.Sandman', value: 'Bring me a gene'}],201 [{key: 'déjà vu', value: 'hmm...'}],202 ];203204 for (let i = 0; i < invalidProperties.length; i++) {205 await expect(executeTransaction(206 api, 207 alice, 208 api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 209 ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);210 }211212 await expect(executeTransaction(213 api, 214 alice, 215 api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 216 ), `on rejecting an unnamed property`).to.be.rejectedWith(/common\.EmptyPropertyKey/);217218 await expect(executeTransaction(219 api, 220 alice, 221 api.tx.unique.setCollectionProperties(collection, [222 {key: 'CRISPR-Cas9', value: 'rewriting nature!'}223 ]), 224 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;225226 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');227228 const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();229 expect(properties).to.be.deep.equal([230 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},231 ]);232233 for (let i = 0; i < invalidProperties.length; i++) {234 await expect(executeTransaction(235 api, 236 alice, 237 api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 238 ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);239 }240 });241 });242});243244// ---------- ACCESS RIGHTS245246describe('Integration Test: Access Rights to Token Properties', () => {247 before(async () => {248 await usingApi(async api => {249 alice = privateKey('//Alice');250 bob = privateKey('//Bob');251 });252 });253 254 it('Reads access rights to properties of a collection', async () => {255 await usingApi(async api => {256 const collection = await createCollectionExpectSuccess();257 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();258 expect(propertyRights).to.be.empty;259 });260 });261 262 it('Sets access rights to properties of a collection', async () => {263 await usingApi(async api => {264 const collection = await createCollectionExpectSuccess();265266 await expect(executeTransaction(267 api, 268 alice, 269 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 270 )).to.not.be.rejected;271272 await addCollectionAdminExpectSuccess(alice, collection, bob.address);273274 await expect(executeTransaction(275 api, 276 alice, 277 api.tx.unique.setPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 278 )).to.not.be.rejected;279280 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();281 expect(propertyRights).to.be.deep.equal([282 {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},283 {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},284 ]);285 });286 });287 288 it('Changes access rights to properties of a collection', async () => {289 await usingApi(async api => {290 const collection = await createCollectionExpectSuccess();291292 await expect(executeTransaction(293 api, 294 alice, 295 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 296 )).to.not.be.rejected;297298 await expect(executeTransaction(299 api, 300 alice, 301 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 302 )).to.not.be.rejected;303304 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();305 expect(propertyRights).to.be.deep.equal([306 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},307 ]);308 });309 });310});311312describe('Negative Integration Test: Access Rights to Token Properties', () => {313 before(async () => {314 await usingApi(async api => {315 alice = privateKey('//Alice');316 bob = privateKey('//Bob');317 });318 });319320 it('Prevents from setting access rights to properties of a collection if not an onwer/admin', async () => {321 await usingApi(async api => {322 const collection = await createCollectionExpectSuccess();323324 await expect(executeTransaction(325 api, 326 bob, 327 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 328 )).to.be.rejectedWith(/common\.NoPermission/);329330 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();331 expect(propertyRights).to.be.empty;332 });333 });334335 it('Prevents from adding too many possible properties', async () => {336 await usingApi(async api => {337 const collection = await createCollectionExpectSuccess();338339 const constitution = [];340 for (let i = 0; i < 65; i++) {341 constitution.push({342 key: 'property_' + i,343 permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},344 });345 }346347 await expect(executeTransaction(348 api, 349 alice, 350 api.tx.unique.setPropertyPermissions(collection, constitution), 351 )).to.be.rejectedWith(/common\.PropertyLimitReached/);352353 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();354 expect(propertyRights).to.be.empty;355 });356 });357358 it('Prevents access rights to be modified if constant', async () => {359 await usingApi(async api => {360 const collection = await createCollectionExpectSuccess();361362 await expect(executeTransaction(363 api, 364 alice, 365 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 366 )).to.not.be.rejected;367368 await expect(executeTransaction(369 api, 370 alice, 371 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 372 )).to.be.rejectedWith(/common\.NoPermission/);373374 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();375 expect(propertyRights).to.deep.equal([376 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},377 ]);378 });379 });380381 it('Prevents adding properties with invalid names', async () => {382 await usingApi(async api => {383 const collection = await createCollectionExpectSuccess();384385 const invalidProperties = [386 [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],387 [{key: 'G#4', permission: {tokenOwner: true}}],388 [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],389 ];390391 for (let i = 0; i < invalidProperties.length; i++) {392 await expect(executeTransaction(393 api, 394 alice, 395 api.tx.unique.setPropertyPermissions(collection, invalidProperties[i]), 396 ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);397 }398399 await expect(executeTransaction(400 api, 401 alice, 402 api.tx.unique.setPropertyPermissions(collection, [{key: '', permission: {}}]), 403 ), `on rejecting an unnamed property`).to.be.rejectedWith(/common\.EmptyPropertyKey/);404405 const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string406 await expect(executeTransaction(407 api, 408 alice, 409 api.tx.unique.setPropertyPermissions(collection, [410 {key: correctKey, permission: {collectionAdmin: true}},411 ]), 412 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;413414 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');415416 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();417 expect(propertyRights).to.be.deep.equal([418 {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},419 ]);420 });421 });422});423424// ---------- TOKEN PROPERTIES425426describe('Integration Test: Token Properties', () => {427 let collection: number;428 let token: number;429 let permissions: {permission: any, signers: IKeyringPair[]}[];430431 before(async () => {432 await usingApi(async api => {433 alice = privateKey('//Alice');434 bob = privateKey('//Bob');435 charlie = privateKey('//Charlie');436437 permissions = [438 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},439 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},440 {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},441 {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},442 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},443 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},444 ];445 });446 });447448 beforeEach(async () => {449 collection = await createCollectionExpectSuccess();450 token = await createItemExpectSuccess(alice, collection, 'NFT');451 await addCollectionAdminExpectSuccess(alice, collection, bob.address);452 await transferExpectSuccess(collection, token, alice, charlie);453 });454 455 it('Reads yet empty properties of a token', async () => {456 await usingApi(async api => {457 const collection = await createCollectionExpectSuccess();458 const token = await createItemExpectSuccess(alice, collection, 'NFT');459 460 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();461 expect(properties.map).to.be.empty;462 expect(properties.consumedSpace).to.be.equal(0);463464 const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;465 expect(tokenData).to.be.empty;466 });467 });468469 it('Assigns properties to a token according to permissions', async () => {470 await usingApi(async api => {471 const propertyKeys: string[] = [];472 let i = 0;473 for (const permission of permissions) {474 for (const signer of permission.signers) {475 const key = i + '_' + signer.address;476 propertyKeys.push(key);477478 await expect(executeTransaction(479 api, 480 alice, 481 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 482 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;483484 await expect(executeTransaction(485 api, 486 signer, 487 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 488 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;489 }490491 i++;492 }493494 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];495 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];496 for (let i = 0; i < properties.length; i++) {497 expect(properties[i].value).to.be.equal('Serotonin increase');498 expect(tokensData[i].value).to.be.equal('Serotonin increase');499 }500 });501 });502503 it('Changes properties of a token according to permissions', async () => {504 await usingApi(async api => {505 const propertyKeys: string[] = [];506 let i = 0;507 for (const permission of permissions) {508 if (!permission.permission.mutable) continue;509 510 for (const signer of permission.signers) {511 const key = i + '_' + signer.address;512 propertyKeys.push(key);513514 await expect(executeTransaction(515 api, 516 alice, 517 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 518 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;519520 await expect(executeTransaction(521 api, 522 signer, 523 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 524 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;525526 await expect(executeTransaction(527 api, 528 signer, 529 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 530 ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;531 }532533 i++;534 }535536 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];537 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];538 for (let i = 0; i < properties.length; i++) {539 expect(properties[i].value).to.be.equal('Serotonin stable');540 expect(tokensData[i].value).to.be.equal('Serotonin stable');541 }542 });543 });544545 it('Deletes properties of a token according to permissions', async () => {546 await usingApi(async api => {547 const propertyKeys: string[] = [];548 let i = 0;549550 for (const permission of permissions) {551 if (!permission.permission.mutable) continue;552 553 for (const signer of permission.signers) {554 const key = i + '_' + signer.address;555 propertyKeys.push(key);556557 await expect(executeTransaction(558 api, 559 alice, 560 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 561 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;562563 await expect(executeTransaction(564 api, 565 signer, 566 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 567 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;568569 await expect(executeTransaction(570 api, 571 signer, 572 api.tx.unique.deleteTokenProperties(collection, token, [key]), 573 ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;574 }575 576 i++;577 }578579 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];580 expect(properties).to.be.empty;581 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];582 expect(tokensData).to.be.empty;583 expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);584 });585 });586});587588describe('Negative Integration Test: Token Properties', () => {589 let collection: number;590 let token: number;591 let originalSpace: number;592 let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];593594 before(async () => {595 await usingApi(async api => {596 alice = privateKey('//Alice');597 bob = privateKey('//Bob');598 charlie = privateKey('//Charlie');599 const dave = privateKey('//Dave');600601 constitution = [602 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},603 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},604 {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},605 {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},606 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},607 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},608 ];609 });610 });611612 beforeEach(async () => {613 collection = await createCollectionExpectSuccess();614 token = await createItemExpectSuccess(alice, collection, 'NFT');615 await addCollectionAdminExpectSuccess(alice, collection, bob.address);616 await transferExpectSuccess(collection, token, alice, charlie);617 618 await usingApi(async api => {619 let i = 0;620 for (const passage of constitution) {621 const signer = passage.signers[0];622 623 await expect(executeTransaction(624 api, 625 alice, 626 api.tx.unique.setPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 627 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;628629 await expect(executeTransaction(630 api, 631 signer, 632 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 633 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;634635 i++;636 }637638 originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;639 });640 });641642 it('Forbids changing/deleting properties of a token if the user is outside of permissions', async () => {643 await usingApi(async api => {644 let i = -1;645 for (const forbiddance of constitution) {646 i++;647 if (!forbiddance.permission.mutable) continue;648649 await expect(executeTransaction(650 api, 651 forbiddance.sinner, 652 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 653 ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);654655 await expect(executeTransaction(656 api, 657 forbiddance.sinner, 658 api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 659 ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);660 }661662 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();663 expect(properties.consumedSpace).to.be.equal(originalSpace);664 });665 });666667 it('Forbids changing/deleting properties of a token if the property is permanent (constant)', async () => {668 await usingApi(async api => {669 let i = -1;670 for (const permission of constitution) {671 i++;672 if (permission.permission.mutable) continue;673674 await expect(executeTransaction(675 api, 676 permission.signers[0], 677 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 678 ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);679680 await expect(executeTransaction(681 api, 682 permission.signers[0], 683 api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 684 ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);685 }686687 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();688 expect(properties.consumedSpace).to.be.equal(originalSpace);689 });690 });691692 it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission', async () => {693 await usingApi(async api => {694 await expect(executeTransaction(695 api, 696 alice, 697 api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 698 ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);699 700 await expect(executeTransaction(701 api, 702 alice, 703 api.tx.unique.setPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 704 ), 'on setting a new non-permitted property').to.not.be.rejected;705706 await expect(executeTransaction(707 api, 708 alice, 709 api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 710 ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);711712 expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;713 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();714 expect(properties.consumedSpace).to.be.equal(originalSpace);715 });716 });717718 it('Forbids adding too many properties to a token', async () => {719 await usingApi(async api => {720 await expect(executeTransaction(721 api, 722 alice, 723 api.tx.unique.setPropertyPermissions(collection, [724 {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 725 {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},726 ]), 727 ), 'on setting a new non-permitted property').to.not.be.rejected;728729 // Mute the general tx parsing error730 {731 console.error = () => {};732 await expect(executeTransaction(733 api, 734 alice, 735 api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 736 )).to.be.rejected;737 }738739 await expect(executeTransaction(740 api, 741 alice, 742 api.tx.unique.setTokenProperties(collection, token, [743 {key: 'a_holy_book', value: 'word '.repeat(3277)}, 744 {key: 'young_years', value: 'neverending'.repeat(1490)},745 ]), 746 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);747748 expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young years'])).toJSON()).to.be.empty;749 const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();750 expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);751 });752 });753});1import {expect} from 'chai';2import privateKey from '../substrate/privateKey';3import usingApi, {executeTransaction} from '../substrate/substrate-api';4import {5 addCollectionAdminExpectSuccess,6 createCollectionExpectSuccess,7 createItemExpectSuccess,8 getCreateCollectionResult,9 transferExpectSuccess,10} from '../util/helpers';11import {IKeyringPair} from '@polkadot/types/types';1213let alice: IKeyringPair;14let bob: IKeyringPair;15let charlie: IKeyringPair;1617// ---------- COLLECTION PROPERTIES1819describe('Integration Test: Collection Properties', () => {20 before(async () => {21 await usingApi(async () => {22 alice = privateKey('//Alice');23 bob = privateKey('//Bob');24 });25 });2627 it('Reads properties from a collection', async () => {28 await usingApi(async api => {29 const collection = await createCollectionExpectSuccess();30 const properties = (await api.query.common.collectionProperties(collection)).toJSON();31 expect(properties.map).to.be.empty;32 expect(properties.consumedSpace).to.equal(0);33 });34 });3536 it('Sets properties for a collection', async () => {37 await usingApi(async api => {38 const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));39 const {collectionId} = getCreateCollectionResult(events);4041 // As owner42 await expect(executeTransaction(43 api, 44 bob, 45 api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 46 )).to.not.be.rejected;4748 await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);4950 // As administrator51 await expect(executeTransaction(52 api, 53 alice, 54 api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 55 )).to.not.be.rejected;5657 const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();58 expect(properties).to.be.deep.equal([59 {key: 'electron', value: 'come bond'},60 {key: 'black_hole', value: ''},61 ]);62 });63 });6465 it('Changes properties of a collection', async () => {66 await usingApi(async api => {67 const collection = await createCollectionExpectSuccess();6869 await expect(executeTransaction(70 api, 71 alice, 72 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 73 )).to.not.be.rejected;7475 // Mutate the properties76 await expect(executeTransaction(77 api, 78 alice, 79 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 80 )).to.not.be.rejected;8182 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();83 expect(properties).to.be.deep.equal([84 {key: 'electron', value: 'bonded'},85 {key: 'black_hole', value: 'LIGO'},86 ]);87 });88 });8990 it('Deletes properties of a collection', async () => {91 await usingApi(async api => {92 const collection = await createCollectionExpectSuccess();9394 await expect(executeTransaction(95 api, 96 alice, 97 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 98 )).to.not.be.rejected;99100 await expect(executeTransaction(101 api, 102 alice, 103 api.tx.unique.deleteCollectionProperties(collection, ['electron']), 104 )).to.not.be.rejected;105106 const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();107 expect(properties).to.be.deep.equal([108 {key: 'black_hole', value: 'LIGO'},109 ]);110 });111 });112});113114describe('Negative Integration Test: Collection Properties', () => {115 before(async () => {116 await usingApi(async () => {117 alice = privateKey('//Alice');118 bob = privateKey('//Bob');119 });120 });121 122 it('Fails to set properties in a collection if not its onwer/administrator', async () => {123 await usingApi(async api => {124 const collection = await createCollectionExpectSuccess();125126 await expect(executeTransaction(127 api, 128 bob, 129 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 130 )).to.be.rejectedWith(/common\.NoPermission/);131 132 const properties = (await api.query.common.collectionProperties(collection)).toJSON();133 expect(properties.map).to.be.empty;134 expect(properties.consumedSpace).to.equal(0);135 });136 });137 138 it('Fails to set properties that exceed the limits', async () => {139 await usingApi(async api => {140 const collection = await createCollectionExpectSuccess();141 const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 142143 // Mute the general tx parsing error, too many bytes to process144 {145 console.error = () => {};146 await expect(executeTransaction(147 api, 148 alice, 149 api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 150 )).to.be.rejected;151 }152153 let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();154 expect(properties).to.be.empty;155156 await expect(executeTransaction(157 api, 158 alice, 159 api.tx.unique.setCollectionProperties(collection, [160 {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 161 {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 162 ]), 163 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);164165 properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();166 expect(properties).to.be.empty;167 });168 });169 170 it('Fails to set more properties than it is allowed', async () => {171 await usingApi(async api => {172 const collection = await createCollectionExpectSuccess();173174 const propertiesToBeSet = [];175 for (let i = 0; i < 65; i++) {176 propertiesToBeSet.push({177 key: 'electron_' + i,178 value: Math.random() > 0.5 ? 'high' : 'low',179 });180 }181182 await expect(executeTransaction(183 api, 184 alice, 185 api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 186 )).to.be.rejectedWith(/common\.PropertyLimitReached/);187188 const properties = (await api.query.common.collectionProperties(collection)).toJSON();189 expect(properties.map).to.be.empty;190 expect(properties.consumedSpace).to.equal(0);191 });192 });193194 it('Fails to set properties with invalid names', async () => {195 await usingApi(async api => {196 const collection = await createCollectionExpectSuccess();197198 const invalidProperties = [199 [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],200 [{key: 'Mr.Sandman', value: 'Bring me a gene'}],201 [{key: 'déjà vu', value: 'hmm...'}],202 ];203204 for (let i = 0; i < invalidProperties.length; i++) {205 await expect(executeTransaction(206 api, 207 alice, 208 api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 209 ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);210 }211212 await expect(executeTransaction(213 api, 214 alice, 215 api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 216 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);217218 await expect(executeTransaction(219 api, 220 alice, 221 api.tx.unique.setCollectionProperties(collection, [222 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},223 ]), 224 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;225226 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');227228 const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();229 expect(properties).to.be.deep.equal([230 {key: 'CRISPR-Cas9', value: 'rewriting nature!'},231 ]);232233 for (let i = 0; i < invalidProperties.length; i++) {234 await expect(executeTransaction(235 api, 236 alice, 237 api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 238 ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);239 }240 });241 });242});243244// ---------- ACCESS RIGHTS245246describe('Integration Test: Access Rights to Token Properties', () => {247 before(async () => {248 await usingApi(async () => {249 alice = privateKey('//Alice');250 bob = privateKey('//Bob');251 });252 });253 254 it('Reads access rights to properties of a collection', async () => {255 await usingApi(async api => {256 const collection = await createCollectionExpectSuccess();257 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();258 expect(propertyRights).to.be.empty;259 });260 });261 262 it('Sets access rights to properties of a collection', async () => {263 await usingApi(async api => {264 const collection = await createCollectionExpectSuccess();265266 await expect(executeTransaction(267 api, 268 alice, 269 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 270 )).to.not.be.rejected;271272 await addCollectionAdminExpectSuccess(alice, collection, bob.address);273274 await expect(executeTransaction(275 api, 276 alice, 277 api.tx.unique.setPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 278 )).to.not.be.rejected;279280 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();281 expect(propertyRights).to.be.deep.equal([282 {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},283 {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},284 ]);285 });286 });287 288 it('Changes access rights to properties of a collection', async () => {289 await usingApi(async api => {290 const collection = await createCollectionExpectSuccess();291292 await expect(executeTransaction(293 api, 294 alice, 295 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 296 )).to.not.be.rejected;297298 await expect(executeTransaction(299 api, 300 alice, 301 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 302 )).to.not.be.rejected;303304 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();305 expect(propertyRights).to.be.deep.equal([306 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},307 ]);308 });309 });310});311312describe('Negative Integration Test: Access Rights to Token Properties', () => {313 before(async () => {314 await usingApi(async () => {315 alice = privateKey('//Alice');316 bob = privateKey('//Bob');317 });318 });319320 it('Prevents from setting access rights to properties of a collection if not an onwer/admin', async () => {321 await usingApi(async api => {322 const collection = await createCollectionExpectSuccess();323324 await expect(executeTransaction(325 api, 326 bob, 327 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 328 )).to.be.rejectedWith(/common\.NoPermission/);329330 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();331 expect(propertyRights).to.be.empty;332 });333 });334335 it('Prevents from adding too many possible properties', async () => {336 await usingApi(async api => {337 const collection = await createCollectionExpectSuccess();338339 const constitution = [];340 for (let i = 0; i < 65; i++) {341 constitution.push({342 key: 'property_' + i,343 permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},344 });345 }346347 await expect(executeTransaction(348 api, 349 alice, 350 api.tx.unique.setPropertyPermissions(collection, constitution), 351 )).to.be.rejectedWith(/common\.PropertyLimitReached/);352353 const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();354 expect(propertyRights).to.be.empty;355 });356 });357358 it('Prevents access rights to be modified if constant', async () => {359 await usingApi(async api => {360 const collection = await createCollectionExpectSuccess();361362 await expect(executeTransaction(363 api, 364 alice, 365 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 366 )).to.not.be.rejected;367368 await expect(executeTransaction(369 api, 370 alice, 371 api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 372 )).to.be.rejectedWith(/common\.NoPermission/);373374 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();375 expect(propertyRights).to.deep.equal([376 {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},377 ]);378 });379 });380381 it('Prevents adding properties with invalid names', async () => {382 await usingApi(async api => {383 const collection = await createCollectionExpectSuccess();384385 const invalidProperties = [386 [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],387 [{key: 'G#4', permission: {tokenOwner: true}}],388 [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],389 ];390391 for (let i = 0; i < invalidProperties.length; i++) {392 await expect(executeTransaction(393 api, 394 alice, 395 api.tx.unique.setPropertyPermissions(collection, invalidProperties[i]), 396 ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);397 }398399 await expect(executeTransaction(400 api, 401 alice, 402 api.tx.unique.setPropertyPermissions(collection, [{key: '', permission: {}}]), 403 ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);404405 const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string406 await expect(executeTransaction(407 api, 408 alice, 409 api.tx.unique.setPropertyPermissions(collection, [410 {key: correctKey, permission: {collectionAdmin: true}},411 ]), 412 ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;413414 const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');415416 const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();417 expect(propertyRights).to.be.deep.equal([418 {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},419 ]);420 });421 });422});423424// ---------- TOKEN PROPERTIES425426describe('Integration Test: Token Properties', () => {427 let collection: number;428 let token: number;429 let permissions: {permission: any, signers: IKeyringPair[]}[];430431 before(async () => {432 await usingApi(async () => {433 alice = privateKey('//Alice');434 bob = privateKey('//Bob');435 charlie = privateKey('//Charlie');436437 permissions = [438 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},439 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},440 {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},441 {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},442 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},443 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},444 ];445 });446 });447448 beforeEach(async () => {449 await usingApi(async () => {450 collection = await createCollectionExpectSuccess();451 token = await createItemExpectSuccess(alice, collection, 'NFT');452 await addCollectionAdminExpectSuccess(alice, collection, bob.address);453 await transferExpectSuccess(collection, token, alice, charlie);454 });455 });456 457 it('Reads yet empty properties of a token', async () => {458 await usingApi(async api => {459 const collection = await createCollectionExpectSuccess();460 const token = await createItemExpectSuccess(alice, collection, 'NFT');461 462 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();463 expect(properties.map).to.be.empty;464 expect(properties.consumedSpace).to.be.equal(0);465466 const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;467 expect(tokenData).to.be.empty;468 });469 });470471 it('Assigns properties to a token according to permissions', async () => {472 await usingApi(async api => {473 const propertyKeys: string[] = [];474 let i = 0;475 for (const permission of permissions) {476 for (const signer of permission.signers) {477 const key = i + '_' + signer.address;478 propertyKeys.push(key);479480 await expect(executeTransaction(481 api, 482 alice, 483 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 484 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;485486 await expect(executeTransaction(487 api, 488 signer, 489 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 490 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;491 }492493 i++;494 }495496 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];497 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];498 for (let i = 0; i < properties.length; i++) {499 expect(properties[i].value).to.be.equal('Serotonin increase');500 expect(tokensData[i].value).to.be.equal('Serotonin increase');501 }502 });503 });504505 it('Changes properties of a token according to permissions', async () => {506 await usingApi(async api => {507 const propertyKeys: string[] = [];508 let i = 0;509 for (const permission of permissions) {510 if (!permission.permission.mutable) continue;511 512 for (const signer of permission.signers) {513 const key = i + '_' + signer.address;514 propertyKeys.push(key);515516 await expect(executeTransaction(517 api, 518 alice, 519 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 520 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;521522 await expect(executeTransaction(523 api, 524 signer, 525 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 526 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;527528 await expect(executeTransaction(529 api, 530 signer, 531 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 532 ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;533 }534535 i++;536 }537538 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];539 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];540 for (let i = 0; i < properties.length; i++) {541 expect(properties[i].value).to.be.equal('Serotonin stable');542 expect(tokensData[i].value).to.be.equal('Serotonin stable');543 }544 });545 });546547 it('Deletes properties of a token according to permissions', async () => {548 await usingApi(async api => {549 const propertyKeys: string[] = [];550 let i = 0;551552 for (const permission of permissions) {553 if (!permission.permission.mutable) continue;554 555 for (const signer of permission.signers) {556 const key = i + '_' + signer.address;557 propertyKeys.push(key);558559 await expect(executeTransaction(560 api, 561 alice, 562 api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 563 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;564565 await expect(executeTransaction(566 api, 567 signer, 568 api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 569 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;570571 await expect(executeTransaction(572 api, 573 signer, 574 api.tx.unique.deleteTokenProperties(collection, token, [key]), 575 ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;576 }577 578 i++;579 }580581 const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];582 expect(properties).to.be.empty;583 const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];584 expect(tokensData).to.be.empty;585 expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);586 });587 });588});589590describe('Negative Integration Test: Token Properties', () => {591 let collection: number;592 let token: number;593 let originalSpace: number;594 let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];595596 before(async () => {597 await usingApi(async () => {598 alice = privateKey('//Alice');599 bob = privateKey('//Bob');600 charlie = privateKey('//Charlie');601 const dave = privateKey('//Dave');602603 constitution = [604 {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},605 {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},606 {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},607 {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},608 {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},609 {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},610 ];611 });612 });613614 beforeEach(async () => {615 collection = await createCollectionExpectSuccess();616 token = await createItemExpectSuccess(alice, collection, 'NFT');617 await addCollectionAdminExpectSuccess(alice, collection, bob.address);618 await transferExpectSuccess(collection, token, alice, charlie);619 620 await usingApi(async api => {621 let i = 0;622 for (const passage of constitution) {623 const signer = passage.signers[0];624 625 await expect(executeTransaction(626 api, 627 alice, 628 api.tx.unique.setPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 629 ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;630631 await expect(executeTransaction(632 api, 633 signer, 634 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 635 ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;636637 i++;638 }639640 originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;641 });642 });643644 it('Forbids changing/deleting properties of a token if the user is outside of permissions', async () => {645 await usingApi(async api => {646 let i = -1;647 for (const forbiddance of constitution) {648 i++;649 if (!forbiddance.permission.mutable) continue;650651 await expect(executeTransaction(652 api, 653 forbiddance.sinner, 654 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 655 ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);656657 await expect(executeTransaction(658 api, 659 forbiddance.sinner, 660 api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 661 ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);662 }663664 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();665 expect(properties.consumedSpace).to.be.equal(originalSpace);666 });667 });668669 it('Forbids changing/deleting properties of a token if the property is permanent (constant)', async () => {670 await usingApi(async api => {671 let i = -1;672 for (const permission of constitution) {673 i++;674 if (permission.permission.mutable) continue;675676 await expect(executeTransaction(677 api, 678 permission.signers[0], 679 api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 680 ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);681682 await expect(executeTransaction(683 api, 684 permission.signers[0], 685 api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 686 ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);687 }688689 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();690 expect(properties.consumedSpace).to.be.equal(originalSpace);691 });692 });693694 it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission', async () => {695 await usingApi(async api => {696 await expect(executeTransaction(697 api, 698 alice, 699 api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 700 ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);701 702 await expect(executeTransaction(703 api, 704 alice, 705 api.tx.unique.setPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 706 ), 'on setting a new non-permitted property').to.not.be.rejected;707708 await expect(executeTransaction(709 api, 710 alice, 711 api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 712 ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);713714 expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;715 const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();716 expect(properties.consumedSpace).to.be.equal(originalSpace);717 });718 });719720 it('Forbids adding too many properties to a token', async () => {721 await usingApi(async api => {722 await expect(executeTransaction(723 api, 724 alice, 725 api.tx.unique.setPropertyPermissions(collection, [726 {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 727 {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},728 ]), 729 ), 'on setting a new non-permitted property').to.not.be.rejected;730731 // Mute the general tx parsing error732 {733 console.error = () => {};734 await expect(executeTransaction(735 api, 736 alice, 737 api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 738 )).to.be.rejected;739 }740741 await expect(executeTransaction(742 api, 743 alice, 744 api.tx.unique.setTokenProperties(collection, token, [745 {key: 'a_holy_book', value: 'word '.repeat(3277)}, 746 {key: 'young_years', value: 'neverending'.repeat(1490)},747 ]), 748 )).to.be.rejectedWith(/common\.NoSpaceForProperty/);749750 expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young years'])).toJSON()).to.be.empty;751 const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();752 expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);753 });754 });755});tests/src/nesting/unnest.test.tsdiffbeforeafterboth--- a/tests/src/nesting/unnest.test.ts
+++ b/tests/src/nesting/unnest.test.ts
@@ -4,11 +4,9 @@
import usingApi, {executeTransaction} from '../substrate/substrate-api';
import {
createCollectionExpectSuccess,
- createItemExpectFailure,
createItemExpectSuccess,
getBalance,
getTokenOwner,
- getTopmostTokenOwner,
normalizeAccountId,
setCollectionLimitsExpectSuccess,
transferExpectSuccess,
@@ -21,7 +19,7 @@
describe('Integration Test: Unnesting', () => {
before(async () => {
- await usingApi(async api => {
+ await usingApi(async () => {
alice = privateKey('//Alice');
bob = privateKey('//Bob');
});
@@ -112,7 +110,7 @@
describe('Negative Test: Unnesting', () => {
before(async () => {
- await usingApi(async api => {
+ await usingApi(async () => {
alice = privateKey('//Alice');
bob = privateKey('//Bob');
});
@@ -133,7 +131,7 @@
api,
bob,
api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(bob), collection, nestedToken, 1),
- ), 'while unnesting').to.be.rejectedWith(/^structure\.DepthLimit$/); // todo ApprovedValueTooLow?
+ ), 'while unnesting').to.be.rejectedWith(/^common\.ApprovedValueTooLow$/);
expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
// Try to burn
tests/src/util/helpers.tsdiffbeforeafterboth--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -427,7 +427,6 @@
export async function createCollectionWithPropsExpectFailure(params: Partial<CreateCollectionParams> = {}) {
const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};
- const collectionId = 0;
await usingApi(async (api) => {
// Get number of collections before the transaction
const collectionCountBefore = await getCreatedCollectionCount(api);