difftreelog
tests(nesting): refactored to use playgrounds
in: master
10 files changed
tests/src/eth/util/playgrounds/index.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/index.ts
+++ b/tests/src/eth/util/playgrounds/index.ts
@@ -12,7 +12,7 @@
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
-import { requirePalletsOrSkip } from '../../../util/playgrounds';
+import {requirePalletsOrSkip} from '../../../util/playgrounds';
chai.use(chaiAsPromised);
export const expect = chai.expect;
tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -240,7 +240,7 @@
}
fromTokenId(collectionId: number, tokenId: number): string {
- return this.helper.util.getNestingTokenAddress(collectionId, tokenId);
+ return this.helper.util.getNestingTokenAddressRaw({collectionId, tokenId});
}
normalizeAddress(address: string): string {
tests/src/nesting/graphs.test.tsdiffbeforeafterboth--- a/tests/src/nesting/graphs.test.ts
+++ b/tests/src/nesting/graphs.test.ts
@@ -1,9 +1,22 @@
-import {ApiPromise} from '@polkadot/api';
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
import {IKeyringPair} from '@polkadot/types/types';
-import {expect} from 'chai';
-import {tokenIdToCross} from '../eth/util/helpers';
-import usingApi, {executeTransaction} from '../substrate/substrate-api';
-import {getCreateCollectionResult, transferExpectSuccess, setCollectionLimitsExpectSuccess} from '../util/helpers';
+import {expect, itSub, usingPlaygrounds} from '../util/playgrounds';
+import {UniqueHelper, UniqueNFTToken} from '../util/playgrounds/unique';
/**
* ```dot
@@ -12,46 +25,47 @@
* 8 -> 5
* ```
*/
-async function buildComplexObjectGraph(api: ApiPromise, sender: IKeyringPair): Promise<number> {
- const events = await executeTransaction(api, sender, api.tx.unique.createCollectionEx({mode: 'NFT', permissions: {nesting: {tokenOwner: true}}}));
- const {collectionId} = getCreateCollectionResult(events);
+async function buildComplexObjectGraph(helper: UniqueHelper, sender: IKeyringPair): Promise<UniqueNFTToken[]> {
+ const collection = await helper.nft.mintCollection(sender, {permissions: {nesting: {tokenOwner: true}}});
+ const tokens = await collection.mintMultipleTokens(sender, Array(8).fill({owner: {Substrate: sender.address}}));
- await executeTransaction(api, sender, api.tx.unique.createMultipleItemsEx(collectionId, {NFT: Array(8).fill({owner: {Substrate: sender.address}})}));
-
- await transferExpectSuccess(collectionId, 8, sender, tokenIdToCross(collectionId, 5));
-
- await transferExpectSuccess(collectionId, 7, sender, tokenIdToCross(collectionId, 6));
- await transferExpectSuccess(collectionId, 6, sender, tokenIdToCross(collectionId, 5));
- await transferExpectSuccess(collectionId, 5, sender, tokenIdToCross(collectionId, 2));
+ await tokens[7].nest(sender, tokens[4]);
+ await tokens[6].nest(sender, tokens[5]);
+ await tokens[5].nest(sender, tokens[4]);
+ await tokens[4].nest(sender, tokens[1]);
+ await tokens[3].nest(sender, tokens[2]);
+ await tokens[2].nest(sender, tokens[1]);
+ await tokens[1].nest(sender, tokens[0]);
- await transferExpectSuccess(collectionId, 4, sender, tokenIdToCross(collectionId, 3));
- await transferExpectSuccess(collectionId, 3, sender, tokenIdToCross(collectionId, 2));
- await transferExpectSuccess(collectionId, 2, sender, tokenIdToCross(collectionId, 1));
-
- return collectionId;
+ return tokens;
}
describe('Graphs', () => {
- it('Ouroboros can\'t be created in a complex graph', async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- const alice = privateKeyWrapper('//Alice');
- const collection = await buildComplexObjectGraph(api, alice);
- const tokenTwoParent = tokenIdToCross(collection, 1);
+ let alice: IKeyringPair;
- // to self
- await expect(
- executeTransaction(api, alice, api.tx.unique.transfer(tokenIdToCross(collection, 1), collection, 1, 1)),
- 'first transaction',
- ).to.be.rejectedWith(/structure\.OuroborosDetected/);
- // to nested part of graph
- await expect(
- executeTransaction(api, alice, api.tx.unique.transfer(tokenIdToCross(collection, 5), collection, 1, 1)),
- 'second transaction',
- ).to.be.rejectedWith(/structure\.OuroborosDetected/);
- await expect(
- executeTransaction(api, alice, api.tx.unique.transferFrom(tokenTwoParent, tokenIdToCross(collection, 8), collection, 2, 1)),
- 'third transaction',
- ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([10n], donor);
});
});
+
+ itSub('Ouroboros can\'t be created in a complex graph', async ({helper}) => {
+ const tokens = await buildComplexObjectGraph(helper, alice);
+
+ // to self
+ await expect(
+ tokens[0].nest(alice, tokens[0]),
+ 'first transaction',
+ ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+ // to nested part of graph
+ await expect(
+ tokens[0].nest(alice, tokens[4]),
+ 'second transaction',
+ ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+ await expect(
+ tokens[1].transferFrom(alice, tokens[0].nestingAddress(), tokens[7].nestingAddress()),
+ 'third transaction',
+ ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+ });
});
tests/src/nesting/migration-check.test.tsdiffbeforeafterboth--- a/tests/src/nesting/migration-check.test.ts
+++ b/tests/src/nesting/migration-check.test.ts
@@ -8,7 +8,8 @@
import find from 'find-process';
// todo un-skip for migrations
-describe.skip('Migration testing', () => {
+// todo:playgrounds skipped, this one is outdated. Probably to be deleted/replaced.
+describe.skip('Migration testing: Properties', () => {
let alice: IKeyringPair;
before(async() => {
tests/src/nesting/nest.test.tsdiffbeforeafterboth--- a/tests/src/nesting/nest.test.ts
+++ b/tests/src/nesting/nest.test.ts
@@ -1,840 +1,667 @@
-import {expect} from 'chai';
-import {tokenIdToAddress} from '../eth/util/helpers';
-import usingApi, {executeTransaction} from '../substrate/substrate-api';
-import {
- addCollectionAdminExpectSuccess,
- addToAllowListExpectSuccess,
- createCollectionExpectSuccess,
- createItemExpectSuccess,
- enableAllowListExpectSuccess,
- enablePublicMintingExpectSuccess,
- getTokenChildren,
- getTokenOwner,
- getTopmostTokenOwner,
- normalizeAccountId,
- setCollectionPermissionsExpectSuccess,
- transferExpectFailure,
- transferExpectSuccess,
- transferFromExpectSuccess,
- setCollectionLimitsExpectSuccess,
- requirePallets,
- Pallets,
-} from '../util/helpers';
-import {IKeyringPair} from '@polkadot/types/types';
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
-let alice: IKeyringPair;
-let bob: IKeyringPair;
-let charlie: IKeyringPair;
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, Pallets, usingPlaygrounds} from '../util/playgrounds';
+
describe('Integration Test: Composite nesting tests', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
before(async () => {
- await usingApi(async (_, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
});
});
- it('Performs the full suite: bundles a token, transfers, and unnests', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('Performs the full suite: bundles a token, transfers, and unnests', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Create an immediately nested token
+ const nestedToken = await collection.mintToken(alice, targetToken.nestingAddress());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
+
+ // Create a token to be nested
+ const newToken = await collection.mintToken(alice);
- // Create a token to be nested
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
-
- // Nest
- await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Nest
+ await newToken.nest(alice, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
- // Move bundle to different user
- await transferExpectSuccess(collection, targetToken, alice, {Substrate: bob.address});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: bob.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Move bundle to different user
+ await targetToken.transfer(alice, {Substrate: bob.address});
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
- // Unnest
- await transferFromExpectSuccess(collection, newToken, bob, {Ethereum: tokenIdToAddress(collection, targetToken)}, {Substrate: bob.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});
- });
+ // Unnest
+ await newToken.unnest(bob, targetToken, {Substrate: bob.address});
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: bob.address});
});
-
- it('Transfers an already bundled token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const tokenA = await createItemExpectSuccess(alice, collection, 'NFT');
- const tokenB = await createItemExpectSuccess(alice, collection, 'NFT');
-
- // Create a nested token
- const tokenC = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, tokenA)});
- expect(await getTopmostTokenOwner(api, collection, tokenC)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, tokenC)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, tokenA).toLowerCase()});
+ itSub('Transfers an already bundled token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const tokenA = await collection.mintToken(alice);
+ const tokenB = await collection.mintToken(alice);
- // Transfer the nested token to another token
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.transferFrom(
- normalizeAccountId({Ethereum: tokenIdToAddress(collection, tokenA)}),
- normalizeAccountId({Ethereum: tokenIdToAddress(collection, tokenB)}),
- collection,
- tokenC,
- 1,
- ),
- )).to.not.be.rejected;
- expect(await getTopmostTokenOwner(api, collection, tokenC)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, tokenC)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, tokenB).toLowerCase()});
- });
+ // Create a nested token
+ const tokenC = await collection.mintToken(alice, tokenA.nestingAddress());
+ expect(await tokenC.getOwner()).to.be.deep.equal(tokenA.nestingAddress());
+
+ // Transfer the nested token to another token
+ await expect(tokenC.transferFrom(alice, tokenA.nestingAddress(), tokenB.nestingAddress())).to.be.fulfilled;
+ expect(await tokenC.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+ expect(await tokenC.getOwner()).to.be.deep.equal(tokenB.nestingAddress());
});
- it('Checks token children', async () => {
- await usingApi(async api => {
- const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionLimitsExpectSuccess(alice, collectionA, {ownerCanTransfer: true});
- await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {tokenOwner: true}});
- const collectionB = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ itSub('Checks token children', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const collectionB = await helper.ft.mintCollection(alice);
+
+ const targetToken = await collectionA.mintToken(alice);
+ expect((await targetToken.getChildren()).length).to.be.equal(0, 'Children length check at creation');
- const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionA, targetToken)};
- let children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(0, 'Children length check at creation');
+ // Create a nested NFT token
+ const tokenA = await collectionA.mintToken(alice, targetToken.nestingAddress());
+ expect(await targetToken.getChildren()).to.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ ], 'Children contents check at nesting #1').and.be.length(1, 'Children length check at nesting #1');
- // Create a nested NFT token
- const tokenA = await createItemExpectSuccess(alice, collectionA, 'NFT', targetAddress);
- children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(1, 'Children length check at nesting #1');
- expect(children).to.have.deep.members([
- {token: tokenA, collection: collectionA},
- ], 'Children contents check at nesting #1');
+ // Create then nest
+ const tokenB = await collectionA.mintToken(alice);
+ await tokenB.nest(alice, targetToken);
+ expect(await targetToken.getChildren()).to.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ {tokenId: tokenB.tokenId, collectionId: collectionA.collectionId},
+ ], 'Children contents check at nesting #2').and.be.length(2, 'Children length check at nesting #2');
- // Create then nest
- const tokenB = await createItemExpectSuccess(alice, collectionA, 'NFT');
- await transferExpectSuccess(collectionA, tokenB, alice, targetAddress);
- children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(2, 'Children length check at nesting #2');
- expect(children).to.have.deep.members([
- {token: tokenA, collection: collectionA},
- {token: tokenB, collection: collectionA},
- ], 'Children contents check at nesting #2');
+ // Move token B to a different user outside the nesting tree
+ await tokenB.unnest(alice, targetToken, {Substrate: bob.address});
+ expect(await targetToken.getChildren()).to.be.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ ], 'Children contents check at nesting #3 (unnesting)').and.be.length(1, 'Children length check at nesting #3 (unnesting)');
- // Move token B to a different user outside the nesting tree
- await transferFromExpectSuccess(collectionA, tokenB, alice, targetAddress, bob);
- children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(1, 'Children length check at unnesting');
- expect(children).to.be.have.deep.members([
- {token: tokenA, collection: collectionA},
- ], 'Children contents check at unnesting');
+ // Create a fungible token in another collection and then nest
+ await collectionB.mint(alice, 10n);
+ await collectionB.transfer(alice, targetToken.nestingAddress(), 2n);
+ expect(await targetToken.getChildren()).to.be.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ {tokenId: 0, collectionId: collectionB.collectionId},
+ ], 'Children contents check at nesting #4 (from another collection)')
+ .and.be.length(2, 'Children length check at nesting #4 (from another collection)');
+
+ // Move part of the fungible token inside token A deeper in the nesting tree
+ await collectionB.transferFrom(alice, targetToken.nestingAddress(), tokenA.nestingAddress(), 1n);
+ expect(await targetToken.getChildren()).to.be.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ {tokenId: 0, collectionId: collectionB.collectionId},
+ ], 'Children contents check at nesting #5 (deeper)').and.be.length(2, 'Children length check at nesting #5 (deeper)');
+ expect(await tokenA.getChildren()).to.be.have.deep.members([
+ {tokenId: 0, collectionId: collectionB.collectionId},
+ ], 'Children contents check at nesting #5.5 (deeper)').and.be.length(1, 'Children length check at nesting #5.5 (deeper)');
- // Create a fungible token in another collection and then nest
- const tokenC = await createItemExpectSuccess(alice, collectionB, 'Fungible');
- await transferExpectSuccess(collectionB, tokenC, alice, targetAddress, 1, 'Fungible');
- children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(2, 'Children length check at nesting #3 (from another collection)');
- expect(children).to.be.have.deep.members([
- {token: tokenA, collection: collectionA},
- {token: tokenC, collection: collectionB},
- ], 'Children contents check at nesting #3 (from another collection)');
-
- // Move the fungible token inside token A deeper in the nesting tree
- await transferFromExpectSuccess(collectionB, tokenC, alice, targetAddress, {Ethereum: tokenIdToAddress(collectionA, tokenA)}, 1, 'Fungible');
- children = await getTokenChildren(api, collectionA, targetToken);
- expect(children.length).to.be.equal(1, 'Children length check at deeper nesting');
- expect(children).to.be.have.deep.members([
- {token: tokenA, collection: collectionA},
- ], 'Children contents check at deeper nesting');
- });
+ // Move the remaining part of the fungible token inside token A deeper in the nesting tree
+ await collectionB.transferFrom(alice, targetToken.nestingAddress(), tokenA.nestingAddress(), 1n);
+ expect(await targetToken.getChildren()).to.be.have.deep.members([
+ {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+ ], 'Children contents check at nesting #6 (deeper)').and.be.length(1, 'Children length check at nesting #6 (deeper)');
+ expect(await tokenA.getChildren()).to.be.have.deep.members([
+ {tokenId: 0, collectionId: collectionB.collectionId},
+ ], 'Children contents check at nesting #6.5 (deeper)').and.be.length(1, 'Children length check at nesting #6.5 (deeper)');
});
});
-describe('Integration Test: Various token type nesting', async () => {
+describe('Integration Test: Various token type nesting', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+
before(async () => {
- await usingApi(async (_, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
- charlie = privateKeyWrapper('//Charlie');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob, charlie] = await helper.arrange.createAccounts([50n, 10n, 10n], donor);
});
});
- it('Admin (NFT): allows an Admin to nest a token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);
+ itSub('Admin (NFT): allows an Admin to nest a token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true}}});
+ await collection.addAdmin(alice, {Substrate: bob.address});
+ const targetToken = await collection.mintToken(alice, {Substrate: charlie.address});
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(bob, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Create an immediately nested token
+ const nestedToken = await collection.mintToken(bob, targetToken.nestingAddress());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
- // Create a token to be nested and nest
- const newToken = await createItemExpectSuccess(bob, collection, 'NFT');
- await transferExpectSuccess(collection, newToken, bob, {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
- });
+ // Create a token to be nested and nest
+ const newToken = await collection.mintToken(bob);
+ await newToken.nest(bob, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
});
- it('Admin (NFT): Admin and Token Owner can operate together', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, collectionAdmin: true}});
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);
+ itSub('Admin (NFT): Admin and Token Owner can operate together', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true, tokenOwner: true}}});
+ await collection.addAdmin(alice, {Substrate: bob.address});
+ const targetToken = await collection.mintToken(alice, {Substrate: charlie.address});
- // Create a nested token by an administrator
- const nestedToken = await createItemExpectSuccess(bob, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Create an immediately nested token by an administrator
+ const nestedToken = await collection.mintToken(bob, targetToken.nestingAddress());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
- // Create a token and allow the owner to nest too
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);
- await transferExpectSuccess(collection, newToken, charlie, {Ethereum: tokenIdToAddress(collection, nestedToken)});
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, nestedToken).toLowerCase()});
- });
+ // Create a token to be nested and nest
+ const newToken = await collection.mintToken(alice, {Substrate: charlie.address});
+ await newToken.nest(charlie, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
});
- it('Admin (NFT): allows an Admin to nest a token (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await addCollectionAdminExpectSuccess(alice, collectionA, bob.address);
- const collectionB = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await addCollectionAdminExpectSuccess(alice, collectionB, bob.address);
- await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {collectionAdmin: true, restricted:[collectionA, collectionB]}});
- const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT', charlie.address);
+ itSub('Admin (NFT): allows an Admin to nest a token (Restricted nesting)', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice);
+ await collectionA.addAdmin(alice, {Substrate: bob.address});
+ const collectionB = await helper.nft.mintCollection(alice);
+ await collectionB.addAdmin(alice, {Substrate: bob.address});
+ await collectionA.setPermissions(alice, {nesting: {collectionAdmin: true, restricted:[collectionB.collectionId]}});
+ const targetToken = await collectionA.mintToken(alice, {Substrate: charlie.address});
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(bob, collectionB, 'NFT', {Ethereum: tokenIdToAddress(collectionA, targetToken)});
- expect(await getTopmostTokenOwner(api, collectionB, nestedToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collectionB, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collectionA, targetToken).toLowerCase()});
+ // Create an immediately nested token
+ const nestedToken = await collectionB.mintToken(bob, targetToken.nestingAddress());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
- // Create a token to be nested and nest
- const newToken = await createItemExpectSuccess(bob, collectionB, 'NFT');
- await transferExpectSuccess(collectionB, newToken, bob, {Ethereum: tokenIdToAddress(collectionA, targetToken)});
- expect(await getTopmostTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Substrate: charlie.address});
- expect(await getTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collectionA, targetToken).toLowerCase()});
- });
+ // Create a token to be nested and nest
+ const newToken = await collectionB.mintToken(bob);
+ await newToken.nest(bob, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
});
// ---------- Non-Fungible ----------
- it('NFT: allows an Owner to nest/unnest their token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}}});
+ await collection.addToAllowList(alice, {Substrate: charlie.address});
+ const targetToken = await collection.mintToken(charlie);
+ await collection.addToAllowList(alice, targetToken.nestingAddress());
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Create an immediately nested token
+ const nestedToken = await collection.mintToken(charlie, targetToken.nestingAddress());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
- // Create a token to be nested and nest
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
- });
+ // Create a token to be nested and nest
+ const newToken = await collection.mintToken(charlie);
+ await newToken.nest(charlie, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
});
- it('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[collection]}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice);
+ const collectionB = await helper.nft.mintCollection(alice);
+ //await collectionB.addAdmin(alice, {Substrate: bob.address});
+ const targetToken = await collectionA.mintToken(alice, {Substrate: charlie.address});
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ await collectionA.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted:[collectionB.collectionId]}});
+ await collectionA.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionA.addToAllowList(alice, targetToken.nestingAddress());
+
+ await collectionB.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ await collectionB.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionB.addToAllowList(alice, targetToken.nestingAddress());
- // Create a token to be nested and nest
- const newToken = await createItemExpectSuccess(alice, collection, 'NFT');
- await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});
- expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
- });
+ // Create an immediately nested token
+ const nestedToken = await collectionB.mintToken(charlie, targetToken.nestingAddress());
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
+
+ // Create a token to be nested and nest
+ const newToken = await collectionB.mintToken(charlie);
+ await newToken.nest(charlie, targetToken);
+ expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+ expect(await newToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
});
// ---------- Fungible ----------
- it('Fungible: allows an Owner to nest/unnest their token', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ itSub('Fungible: allows an Owner to nest/unnest their token', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}}});
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await collectionNFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAddress());
- // Create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionFT,
- targetAddress,
- {Fungible: {Value: 10}},
- ))).to.not.be.rejected;
+ await collectionFT.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ await collectionFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionFT.addToAllowList(alice, targetToken.nestingAddress());
- // Nest a new token
- const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- await transferExpectSuccess(collectionFT, newToken, alice, targetAddress, 1, 'Fungible');
- });
+ // Create an immediately nested token
+ await collectionFT.mint(charlie, 5n, targetToken.nestingAddress());
+ expect(await collectionFT.getBalance(targetToken.nestingAddress())).to.be.equal(5n);
+
+ // Create a token to be nested and nest
+ await collectionFT.mint(charlie, 5n);
+ await collectionFT.transfer(charlie, targetToken.nestingAddress(), 2n);
+ expect(await collectionFT.getBalance(targetToken.nestingAddress())).to.be.equal(7n);
});
- it('Fungible: allows an Owner to nest/unnest their token (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ itSub('Fungible: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted:[collectionFT.collectionId]}});
+ await collectionNFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAddress());
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted: [collectionFT]}});
+ await collectionFT.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ await collectionFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionFT.addToAllowList(alice, targetToken.nestingAddress());
- // Create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionFT,
- targetAddress,
- {Fungible: {Value: 10}},
- ))).to.not.be.rejected;
+ // Create an immediately nested token
+ await collectionFT.mint(charlie, 5n, targetToken.nestingAddress());
+ expect(await collectionFT.getBalance(targetToken.nestingAddress())).to.be.equal(5n);
- // Nest a new token
- const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
- await transferExpectSuccess(collectionFT, newToken, alice, targetAddress, 1, 'Fungible');
- });
+ // Create a token to be nested and nest
+ await collectionFT.mint(charlie, 5n);
+ await collectionFT.transfer(charlie, targetToken.nestingAddress(), 2n);
+ expect(await collectionFT.getBalance(targetToken.nestingAddress())).to.be.equal(7n);
});
// ---------- Re-Fungible ----------
- it('ReFungible: allows an Owner to nest/unnest their token', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ itSub.ifWithPallets('ReFungible: allows an Owner to nest/unnest their token', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}}});
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ await collectionNFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAddress());
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ await collectionRFT.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ await collectionRFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionRFT.addToAllowList(alice, targetToken.nestingAddress());
- // Create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- ))).to.not.be.rejected;
+ // Create an immediately nested token
+ const nestedToken = await collectionRFT.mintToken(charlie, 5n, targetToken.nestingAddress());
+ expect(await nestedToken.getBalance(targetToken.nestingAddress())).to.be.equal(5n);
- // Nest a new token
- const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await transferExpectSuccess(collectionRFT, newToken, alice, targetAddress, 100, 'ReFungible');
- });
+ // Create a token to be nested and nest
+ const newToken = await collectionRFT.mintToken(charlie, 5n);
+ await newToken.transfer(charlie, targetToken.nestingAddress(), 2n);
+ expect(await newToken.getBalance(targetToken.nestingAddress())).to.be.equal(2n);
});
- it('ReFungible: allows an Owner to nest/unnest their token (Restricted nesting)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ itSub.ifWithPallets('ReFungible: allows an Owner to nest/unnest their token (Restricted nesting)', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted:[collectionRFT.collectionId]}});
+ await collectionNFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAddress());
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ await collectionRFT.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ await collectionRFT.addToAllowList(alice, {Substrate: charlie.address});
+ await collectionRFT.addToAllowList(alice, targetToken.nestingAddress());
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionRFT]}});
+ // Create an immediately nested token
+ const nestedToken = await collectionRFT.mintToken(charlie, 5n, targetToken.nestingAddress());
+ expect(await nestedToken.getBalance(targetToken.nestingAddress())).to.be.equal(5n);
- // Create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- ))).to.not.be.rejected;
-
- // Nest a new token
- const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await transferExpectSuccess(collectionRFT, newToken, alice, targetAddress, 100, 'ReFungible');
- });
+ // Create a token to be nested and nest
+ const newToken = await collectionRFT.mintToken(charlie, 5n);
+ await newToken.transfer(charlie, targetToken.nestingAddress(), 2n);
+ expect(await newToken.getBalance(targetToken.nestingAddress())).to.be.equal(2n);
});
});
-describe('Negative Test: Nesting', async() => {
+describe('Negative Test: Nesting', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
before(async () => {
- await usingApi(async (_, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob] = await helper.arrange.createAccounts([100n, 50n], donor);
});
});
- it('Disallows excessive token nesting', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
-
- const maxNestingLevel = 5;
- let prevToken = targetToken;
+ itSub('Disallows excessive token nesting', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ let token = await collection.mintToken(alice);
- // Create a nested-token matryoshka
- for (let i = 0; i < maxNestingLevel; i++) {
- const nestedToken = await createItemExpectSuccess(
- alice,
- collection,
- 'NFT',
- {Ethereum: tokenIdToAddress(collection, prevToken)},
- );
+ const maxNestingLevel = 5;
- prevToken = nestedToken;
- }
-
- // The nesting depth is limited by `maxNestingLevel`
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, prevToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/^structure\.DepthLimit$/);
+ // Create a nested-token matryoshka
+ for (let i = 0; i < maxNestingLevel; i++) {
+ token = await collection.mintToken(alice, token.nestingAddress());
+ }
- expect(await getTopmostTokenOwner(api, collection, prevToken)).to.be.deep.equal({Substrate: alice.address});
- });
+ // The nesting depth is limited by `maxNestingLevel`
+ await expect(collection.mintToken(alice, token.nestingAddress()))
+ .to.be.rejectedWith(/structure\.DepthLimit/);
+ expect(await token.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+ expect(await token.getChildren()).to.be.length(0);
});
// ---------- Admin ------------
- it('Admin (NFT): disallows an Admin to operate nesting when only TokenOwner is allowed', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('Admin (NFT): disallows an Admin to operate nesting when only TokenOwner is allowed', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ await collection.addAdmin(alice, {Substrate: bob.address});
+ const targetToken = await collection.mintToken(alice);
- // Try to create a nested token as collection admin when it's disallowed
- await expect(executeTransaction(api, bob, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Try to create an immediately nested token as collection admin when it's disallowed
+ await expect(collection.mintToken(bob, targetToken.nestingAddress()))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(bob, collection, 'NFT');
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1),
- ), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});
- });
+ // Try to create a token to be nested and nest
+ const newToken = await collection.mintToken(bob);
+ await expect(newToken.nest(bob, targetToken))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: bob.address});
});
- it('Admin (NFT): disallows a Token Owner to operate nesting when only Admin is allowed', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});
- await addToAllowListExpectSuccess(alice, collection, bob.address);
- await enableAllowListExpectSuccess(alice, collection);
- await enablePublicMintingExpectSuccess(alice, collection);
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('Admin (NFT): disallows a Token Owner to operate nesting when only Admin is allowed', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true}}});
+ const targetToken = await collection.mintToken(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, targetToken.nestingAddress());
+
+ // Try to create a nested token as token owner when it's disallowed
+ await expect(collection.mintToken(bob, targetToken.nestingAddress()))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // Try to create a nested token as collection admin when it's disallowed
- await expect(executeTransaction(api, bob, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
+ // Try to create a token to be nested and nest
+ const newToken = await collection.mintToken(bob);
+ await expect(newToken.nest(bob, targetToken))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(bob, collection, 'NFT');
- await expect(executeTransaction(
- api,
- bob,
- 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: bob.address});
- });
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: bob.address});
});
- it('Admin (NFT): disallows an Admin to nest and unnest someone else\'s token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});
+ itSub('Admin (NFT): disallows an Admin to unnest someone else\'s token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {limits: {ownerCanTransfer: true}, permissions: {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true}}});
+ //await collection.addAdmin(alice, {Substrate: bob.address});
+ const targetToken = await collection.mintToken(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, targetToken.nestingAddress());
- await addToAllowListExpectSuccess(alice, collection, bob.address);
- await enableAllowListExpectSuccess(alice, collection);
- await enablePublicMintingExpectSuccess(alice, collection);
-
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()};
+ // Try to nest somebody else's token
+ const newToken = await collection.mintToken(bob);
+ await expect(newToken.nest(alice, targetToken))
+ .to.be.rejectedWith(/common\.NoPermission/);
- // Try to nest somebody else's token
- const newToken = await createItemExpectSuccess(bob, collection, 'NFT');
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.transferFrom(targetAddress, {Substrate: bob.address}, collection, newToken, 1),
- ), 'while nesting another\'s token token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
- expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});
+ // Try to unnest a token belonging to someone else as collection admin
+ const nestedToken = await collection.mintToken(alice, targetToken.nestingAddress());
+ await expect(nestedToken.unnest(alice, targetToken, {Substrate: bob.address}))
+ .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
- // Nest a token as admin and try to unnest it, now belonging to someone else
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.transferFrom(targetAddress, normalizeAccountId(alice), collection, nestedToken, 1),
- ), 'while unnesting another\'s token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal(targetAddress);
- expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: bob.address});
- });
+ expect(await targetToken.getChildren()).to.be.length(1);
+ expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
});
- it('Admin (NFT): disallows an Admin to nest a token from an unlisted collection (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- const collectionB = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {collectionAdmin: true, restricted:[collectionA]}});
+ itSub('Admin (NFT): disallows an Admin to nest a token from an unlisted collection (Restricted nesting)', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice);
+ const collectionB = await helper.nft.mintCollection(alice);
+ await collectionA.setPermissions(alice, {nesting: {collectionAdmin: true, restricted: [collectionA.collectionId]}});
+ const targetToken = await collectionA.mintToken(alice);
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT');
+ // Try to create a nested token from another collection
+ await expect(collectionB.mintToken(alice, targetToken.nestingAddress()))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collectionB, 'NFT');
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionA, targetToken)}, collectionB, newToken, 1),
- ), 'while nesting a foreign token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- expect(await getTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Substrate: alice.address});
- });
+ // Create a token in another collection yet to be nested and try to nest
+ const newToken = await collectionB.mintToken(alice);
+ await expect(newToken.nest(alice, targetToken))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
});
// ---------- Non-Fungible ----------
- it('NFT: disallows to nest token if nesting is disabled', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {
+ // Collection is implicitly not allowed nesting at creation
+ const collection = await helper.nft.mintCollection(alice);
+ const targetToken = await collection.mintToken(alice);
- // Try to create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
+ // Try to create a nested token as token owner when it's disallowed
+ await expect(collection.mintToken(alice, targetToken.nestingAddress()))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // 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({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- 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});
- });
+ // Try to create a token to be nested and nest
+ const newToken = await collection.mintToken(alice);
+ await expect(newToken.nest(alice, targetToken))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
});
- it('NFT: disallows a non-Owner to nest someone else\'s token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
+ itSub('NFT: disallows a non-Owner to nest someone else\'s token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const targetToken = await collection.mintToken(alice);
- await addToAllowListExpectSuccess(alice, collection, bob.address);
- await enableAllowListExpectSuccess(alice, collection);
- await enablePublicMintingExpectSuccess(alice, collection);
-
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');
+ await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}});
+ await collection.addToAllowList(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, targetToken.nestingAddress());
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Try to create a token to be nested and nest
+ const newToken = await collection.mintToken(alice);
+ await expect(newToken.nest(bob, targetToken)).to.be.rejectedWith(/common\.NoPermission/);
- // 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({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});
- });
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
});
- it('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[collection]}});
+ itSub('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const targetToken = await collection.mintToken(alice);
- await addToAllowListExpectSuccess(alice, collection, bob.address);
- await enableAllowListExpectSuccess(alice, collection);
- await enablePublicMintingExpectSuccess(alice, collection);
+ await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}});
+ await collection.addToAllowList(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, targetToken.nestingAddress());
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');
+ const collectionB = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true}});
+ await collectionB.addToAllowList(alice, {Substrate: bob.address});
+ await collectionB.addToAllowList(alice, targetToken.nestingAddress());
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Try to create a token to be nested and nest
+ const newToken = await collectionB.mintToken(alice);
+ await expect(newToken.nest(bob, targetToken)).to.be.rejectedWith(/common\.NoPermission/);
- // 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({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});
- });
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
});
- it('NFT: disallows to nest token in an unlisted collection', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[]}});
+ itSub('NFT: disallows to nest token in an unlisted collection', async ({helper}) => {
+ // Create collection with restricted nesting -- even self is not allowed
+ const collection = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: []}}});
+ const targetToken = await collection.mintToken(alice, {Substrate: bob.address});
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ await collection.addToAllowList(alice, {Substrate: bob.address});
+ await collection.addToAllowList(alice, targetToken.nestingAddress());
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collection,
- {Ethereum: tokenIdToAddress(collection, targetToken)},
- {nft: {}} as any,
- )), '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({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});
- });
+ // Try to mint in own collection after allowlisting the accounts
+ await expect(collection.mintToken(bob, targetToken.nestingAddress()))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
});
// ---------- Fungible ----------
-
- it('Fungible: disallows to nest token if nesting is disabled', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
-
- 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\.UserIsNotAllowedToNest$/);
+ itSub('Fungible: disallows to nest token if nesting is disabled', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- // 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(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Try to create an immediately nested token
+ await expect(collectionFT.mint(alice, 5n, targetToken.nestingAddress()))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // 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/);
- });
+ // Try to create a token to be nested and nest
+ await collectionFT.mint(alice, 5n);
+ await expect(collectionFT.transfer(alice, targetToken.nestingAddress(), 2n))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(5n);
});
- it('Fungible: disallows a non-Owner to nest someone else\'s token', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});
+ itSub('Fungible: disallows a non-Owner to unnest someone else\'s token', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true, tokenOwner: true}}});
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: bob.address});
- await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);
- await enableAllowListExpectSuccess(alice, collectionNFT);
- await enablePublicMintingExpectSuccess(alice, collectionNFT);
+ // Nest some tokens as Alice into Bob's token
+ await collectionFT.mint(alice, 5n, targetToken.nestingAddress());
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
-
- 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(/common\.UserIsNotAllowedToNest/);
-
- // 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(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- });
+ // Try to pull it out
+ await expect(collectionFT.transferFrom(alice, targetToken.nestingAddress(), {Substrate: bob.address}, 1n))
+ .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await collectionFT.getBalance(targetToken.nestingAddress())).to.be.equal(5n);
});
- 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 addToAllowListExpectSuccess(alice, collectionNFT, bob.address);
- await enableAllowListExpectSuccess(alice, collectionNFT);
- await enablePublicMintingExpectSuccess(alice, collectionNFT);
-
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ itSub('Fungible: disallows a non-Owner to unnest someone else\'s token (Restricted nesting)', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice, {Substrate: bob.address});
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionFT]}});
+ await collectionNFT.setPermissions(alice, {nesting: {collectionAdmin: true, tokenOwner: true, restricted: [collectionFT.collectionId]}});
- // 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(/common\.UserIsNotAllowedToNest/);
+ // Nest some tokens as Alice into Bob's token
+ await collectionFT.mint(alice, 5n, targetToken.nestingAddress());
- // 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(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- });
+ // Try to pull it out as Alice still
+ await expect(collectionFT.transferFrom(alice, targetToken.nestingAddress(), {Substrate: bob.address}, 1n))
+ .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await collectionFT.getBalance(targetToken.nestingAddress())).to.be.equal(5n);
});
- it('Fungible: disallows to nest token in an unlisted collection', async () => {
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[]}});
-
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ itSub('Fungible: disallows to nest token in an unlisted collection', async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true, tokenOwner: true, restricted: []}}});
+ const collectionFT = await helper.ft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ // Try to mint an immediately nested token
+ await expect(collectionFT.mint(alice, 5n, targetToken.nestingAddress()))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- // 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 a nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+ // Mint a token and try to nest it
+ await collectionFT.mint(alice, 5n);
+ await expect(collectionFT.transfer(alice, targetToken.nestingAddress(), 1n))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- // 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(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- });
+ expect(await collectionFT.getBalance(targetToken.nestingAddress())).to.be.equal(0n);
+ expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(5n);
});
// ---------- Re-Fungible ----------
-
- it('ReFungible: disallows to nest token if nesting is disabled', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {}});
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
-
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
-
- // Create a nested token
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- )), 'while creating a nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
+ itSub.ifWithPallets('ReFungible: disallows to nest token if nesting is disabled', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- // 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\.UserIsNotAllowedToNest/);
+ // Try to create an immediately nested token
+ await expect(collectionRFT.mintToken(alice, 5n, targetToken.nestingAddress()))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- // 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/);
- });
+ // Try to create a token to be nested and nest
+ const token = await collectionRFT.mintToken(alice, 5n);
+ await expect(token.transfer(alice, targetToken.nestingAddress(), 2n))
+ .to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ expect(await token.getBalance({Substrate: alice.address})).to.be.equal(5n);
});
-
- it('ReFungible: disallows a non-Owner to nest someone else\'s token', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});
+ itSub.ifWithPallets('ReFungible: disallows a non-Owner to nest someone else\'s token', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);
- await enableAllowListExpectSuccess(alice, collectionNFT);
- await enablePublicMintingExpectSuccess(alice, collectionNFT);
+ await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}});
+ await collectionNFT.addToAllowList(alice, {Substrate: bob.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAddress());
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ // Try to create a token to be nested and nest
+ const newToken = await collectionRFT.mintToken(alice);
+ await expect(newToken.transfer(bob, targetToken.nestingAddress())).to.be.rejectedWith(/common\.TokenValueTooLow/);
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getBalance({Substrate: alice.address})).to.be.equal(1n);
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- )), 'while creating a nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Nest some tokens as Alice into Bob's token
+ await newToken.transfer(alice, targetToken.nestingAddress());
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- });
+ // Try to pull it out
+ await expect(newToken.transferFrom(bob, targetToken.nestingAddress(), {Substrate: alice.address}, 1n))
+ .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await newToken.getBalance(targetToken.nestingAddress())).to.be.equal(1n);
});
- it('ReFungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ itSub.ifWithPallets('ReFungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice);
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);
- await enableAllowListExpectSuccess(alice, collectionNFT);
- await enablePublicMintingExpectSuccess(alice, collectionNFT);
+ await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: [collectionRFT.collectionId]}});
+ await collectionNFT.addToAllowList(alice, {Substrate: bob.address});
+ await collectionNFT.addToAllowList(alice, targetToken.nestingAddress());
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
+ // Try to create a token to be nested and nest
+ const newToken = await collectionRFT.mintToken(alice);
+ await expect(newToken.transfer(bob, targetToken.nestingAddress())).to.be.rejectedWith(/common\.TokenValueTooLow/);
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionRFT]}});
+ expect(await targetToken.getChildren()).to.be.length(0);
+ expect(await newToken.getBalance({Substrate: alice.address})).to.be.equal(1n);
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- )), 'while creating a nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+ // Nest some tokens as Alice into Bob's token
+ await newToken.transfer(alice, targetToken.nestingAddress());
- // Try to create and nest a token in the wrong collection
- const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
- });
+ // Try to pull it out
+ await expect(newToken.transferFrom(bob, targetToken.nestingAddress(), {Substrate: alice.address}, 1n))
+ .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await newToken.getBalance(targetToken.nestingAddress())).to.be.equal(1n);
});
- it('ReFungible: disallows to nest token to an unlisted collection', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ itSub.ifWithPallets('ReFungible: disallows to nest token to an unlisted collection', [Pallets.ReFungible], async ({helper}) => {
+ const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true, restricted: []}}});
+ const collectionRFT = await helper.rft.mintCollection(alice);
+ const targetToken = await collectionNFT.mintToken(alice);
- await usingApi(async api => {
- const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[]}});
+ // Try to create an immediately nested token
+ await expect(collectionRFT.mintToken(alice, 5n, targetToken.nestingAddress()))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- // Create a token to attempt to be nested into
- const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};
-
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
-
- // Try to create a nested token in the wrong collection
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(
- collectionRFT,
- targetAddress,
- {ReFungible: {pieces: 100}},
- )), '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 expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
- });
+ // Try to create a token to be nested and nest
+ const token = await collectionRFT.mintToken(alice, 5n);
+ await expect(token.transfer(alice, targetToken.nestingAddress(), 2n))
+ .to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+ expect(await token.getBalance({Substrate: alice.address})).to.be.equal(5n);
});
});
tests/src/nesting/properties.test.tsdiffbeforeafterboth--- a/tests/src/nesting/properties.test.ts
+++ b/tests/src/nesting/properties.test.ts
@@ -1,5 +1,20 @@
-import {expect} from 'chai';
-import usingApi, {executeTransaction} from '../substrate/substrate-api';
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+/*import usingApi, {executeTransaction} from '../substrate/substrate-api';
import {
addCollectionAdminExpectSuccess,
CollectionMode,
@@ -8,997 +23,776 @@
createItemExpectSuccess,
getCreateCollectionResult,
transferExpectSuccess,
- requirePallets,
- Pallets,
-} from '../util/helpers';
+} from '../util/helpers';*/
import {IKeyringPair} from '@polkadot/types/types';
-import {tokenIdToAddress} from '../eth/util/helpers';
+import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../util/playgrounds';
+import {UniqueCollectionBase, UniqueHelper, UniqueNFTCollection, UniqueNFTToken, UniqueRFTCollection, UniqueRFTToken} from '../util/playgrounds/unique';
-let alice: IKeyringPair;
-let bob: IKeyringPair;
-let charlie: IKeyringPair;
+// ---------- COLLECTION PROPERTIES
+
+describe('Integration Test: Collection Properties', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
-describe('Composite Properties Test', () => {
before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
});
});
- async function testMakeSureSuppliesRequired(mode: CollectionMode) {
- await usingApi(async api => {
- const collectionId = await createCollectionExpectSuccess({mode: mode});
+ itSub('Properties are initially empty', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ expect(await collection.getProperties()).to.be.empty;
+ });
- const collectionOption = await api.rpc.unique.collectionById(collectionId);
- expect(collectionOption.isSome).to.be.true;
- let collection = collectionOption.unwrap();
- expect(collection.tokenPropertyPermissions.toHuman()).to.be.empty;
- expect(collection.properties.toHuman()).to.be.empty;
+ async function testSetsPropertiesForCollection(collection: UniqueCollectionBase) {
+ // As owner
+ await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}])).to.be.fulfilled;
- const propertyPermissions = [
- {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},
- {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},
- ];
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collectionId, propertyPermissions),
- )).to.not.be.rejected;
+ await collection.addAdmin(alice, {Substrate: bob.address});
- const collectionProperties = [
- {key: 'black_hole', value: 'LIGO'},
- {key: 'electron', value: 'come bond'},
- ];
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collectionId, collectionProperties),
- )).to.not.be.rejected;
+ // As administrator
+ await expect(collection.setProperties(bob, [{key: 'black_hole'}])).to.be.fulfilled;
- collection = (await api.rpc.unique.collectionById(collectionId)).unwrap();
- expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);
- expect(collection.properties.toHuman()).to.be.deep.equal(collectionProperties);
- });
+ const properties = await collection.getProperties();
+ expect(properties).to.include.deep.members([
+ {key: 'electron', value: 'come bond'},
+ {key: 'black_hole', value: ''},
+ ]);
}
- it('Makes sure collectionById supplies required fields for NFT', async () => {
- await testMakeSureSuppliesRequired({type: 'NFT'});
+ itSub('Sets properties for a NFT collection', async ({helper}) => {
+ await testSetsPropertiesForCollection(await helper.nft.mintCollection(alice));
});
- it('Makes sure collectionById supplies required fields for ReFungible', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
-
- await testMakeSureSuppliesRequired({type: 'ReFungible'});
+ itSub.ifWithPallets('Sets properties for a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
+ await testSetsPropertiesForCollection(await helper.rft.mintCollection(alice));
});
-});
-// ---------- COLLECTION PROPERTIES
+ async function testCheckValidNames(collection: UniqueCollectionBase) {
+ // alpha symbols
+ await expect(collection.setProperties(alice, [{key: 'answer'}])).to.be.fulfilled;
-describe('Integration Test: Collection Properties', () => {
- before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
- });
- });
+ // numeric symbols
+ await expect(collection.setProperties(alice, [{key: '451'}])).to.be.fulfilled;
- it('Reads properties from a collection', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess();
- const properties = (await api.query.common.collectionProperties(collection)).toJSON();
- expect(properties.map).to.be.empty;
- expect(properties.consumedSpace).to.equal(0);
- });
- });
+ // underscore symbol
+ await expect(collection.setProperties(alice, [{key: 'black_hole'}])).to.be.fulfilled;
+ // dash symbol
+ await expect(collection.setProperties(alice, [{key: '-'}])).to.be.fulfilled;
- async function testSetsPropertiesForCollection(mode: string) {
- await usingApi(async api => {
- const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: mode}));
- const {collectionId} = getCreateCollectionResult(events);
+ // dot symbol
+ await expect(collection.setProperties(alice, [{key: 'once.in.a.long.long.while...', value: 'you get a little lost'}])).to.be.fulfilled;
- // As owner
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]),
- )).to.not.be.rejected;
+ const properties = await collection.getProperties();
+ expect(properties).to.include.deep.members([
+ {key: 'answer', value: ''},
+ {key: '451', value: ''},
+ {key: 'black_hole', value: ''},
+ {key: '-', value: ''},
+ {key: 'once.in.a.long.long.while...', value: 'you get a little lost'},
+ ]);
+ }
- await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);
-
- // As administrator
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]),
- )).to.not.be.rejected;
-
- const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();
- expect(properties).to.be.deep.equal([
- {key: 'electron', value: 'come bond'},
- {key: 'black_hole', value: ''},
- ]);
- });
- }
- it('Sets properties for a NFT collection', async () => {
- await testSetsPropertiesForCollection('NFT');
+ itSub('Check valid names for NFT collection properties keys', async ({helper}) => {
+ await testCheckValidNames(await helper.nft.mintCollection(alice));
});
- it('Sets properties for a ReFungible collection', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testSetsPropertiesForCollection('ReFungible');
+ itSub.ifWithPallets('Check valid names for ReFungible collection properties keys', [Pallets.ReFungible], async ({helper}) => {
+ await testCheckValidNames(await helper.rft.mintCollection(alice));
});
- async function testCheckValidNames(mode: string) {
- await usingApi(async api => {
- const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: mode}));
- const {collectionId} = getCreateCollectionResult(events);
-
- // alpha symbols
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.setCollectionProperties(collectionId, [{key: 'alpha'}]),
- )).to.not.be.rejected;
-
- // numeric symbols
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.setCollectionProperties(collectionId, [{key: '123'}]),
- )).to.not.be.rejected;
-
- // underscore symbol
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]),
- )).to.not.be.rejected;
-
- // dash symbol
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.setCollectionProperties(collectionId, [{key: 'semi-automatic'}]),
- )).to.not.be.rejected;
-
- // underscore symbol
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.setCollectionProperties(collectionId, [{key: 'build.rs'}]),
- )).to.not.be.rejected;
-
- const propertyKeys = ['alpha', '123', 'black_hole', 'semi-automatic', 'build.rs'];
- const properties = (await api.rpc.unique.collectionProperties(collectionId, propertyKeys)).toHuman();
- expect(properties).to.be.deep.equal([
- {key: 'alpha', value: ''},
- {key: '123', value: ''},
- {key: 'black_hole', value: ''},
- {key: 'semi-automatic', value: ''},
- {key: 'build.rs', value: ''},
- ]);
- });
+ async function testChangesProperties(collection: UniqueCollectionBase) {
+ await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: ''}])).to.be.fulfilled;
+
+ // Mutate the properties
+ await expect(collection.setProperties(alice, [{key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;
+
+ const properties = await collection.getProperties();
+ expect(properties).to.include.deep.members([
+ {key: 'electron', value: 'come bond'},
+ {key: 'black_hole', value: 'LIGO'},
+ ]);
}
- it('Check valid names for NFT collection properties keys', async () => {
- await testCheckValidNames('NFT');
+
+ itSub('Changes properties of a NFT collection', async ({helper}) => {
+ await testChangesProperties(await helper.nft.mintCollection(alice));
});
- it('Check valid names for ReFungible collection properties keys', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testCheckValidNames('ReFungible');
+ itSub.ifWithPallets('Changes properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
+ await testChangesProperties(await helper.rft.mintCollection(alice));
});
- async function testChangesProperties(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]),
- )).to.not.be.rejected;
-
- // Mutate the properties
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]),
- )).to.not.be.rejected;
-
- const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();
- expect(properties).to.be.deep.equal([
- {key: 'electron', value: 'bonded'},
- {key: 'black_hole', value: 'LIGO'},
- ]);
- });
- }
- it('Changes properties of a NFT collection', async () => {
- await testChangesProperties({type: 'NFT'});
- });
- it('Changes properties of a ReFungible collection', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ async function testDeleteProperties(collection: UniqueCollectionBase) {
+ await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;
- await testChangesProperties({type: 'ReFungible'});
- });
+ await expect(collection.deleteProperties(alice, ['electron'])).to.be.fulfilled;
- async function testDeleteProperties(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]),
- )).to.not.be.rejected;
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.deleteCollectionProperties(collection, ['electron']),
- )).to.not.be.rejected;
-
- const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();
- expect(properties).to.be.deep.equal([
- {key: 'black_hole', value: 'LIGO'},
- ]);
- });
+ const properties = await collection.getProperties(['black_hole', 'electron']);
+ expect(properties).to.be.deep.equal([
+ {key: 'black_hole', value: 'LIGO'},
+ ]);
}
- it('Deletes properties of a NFT collection', async () => {
- await testDeleteProperties({type: 'NFT'});
+
+ itSub('Deletes properties of a NFT collection', async ({helper}) => {
+ await testDeleteProperties(await helper.nft.mintCollection(alice));
});
- it('Deletes properties of a ReFungible collection', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testDeleteProperties({type: 'ReFungible'});
+ itSub.ifWithPallets('Deletes properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
+ await testDeleteProperties(await helper.rft.mintCollection(alice));
});
});
describe('Negative Integration Test: Collection Properties', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
});
});
- async function testFailsSetPropertiesIfNotOwnerOrAdmin(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]),
- )).to.be.rejectedWith(/common\.NoPermission/);
-
- const properties = (await api.query.common.collectionProperties(collection)).toJSON();
- expect(properties.map).to.be.empty;
- expect(properties.consumedSpace).to.equal(0);
- });
+ async function testFailsSetPropertiesIfNotOwnerOrAdmin(collection: UniqueCollectionBase) {
+ await expect(collection.setProperties(bob, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]))
+ .to.be.rejectedWith(/common\.NoPermission/);
+
+ expect(await collection.getProperties()).to.be.empty;
}
- it('Fails to set properties in a NFT collection if not its onwer/administrator', async () => {
- await testFailsSetPropertiesIfNotOwnerOrAdmin({type: 'NFT'});
+
+ itSub('Fails to set properties in a NFT collection if not its onwer/administrator', async ({helper}) => {
+ await testFailsSetPropertiesIfNotOwnerOrAdmin(await helper.nft.mintCollection(alice));
});
- it('Fails to set properties in a ReFungible collection if not its onwer/administrator', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testFailsSetPropertiesIfNotOwnerOrAdmin({type: 'ReFungible'});
+ itSub.ifWithPallets('Fails to set properties in a ReFungible collection if not its onwer/administrator', [Pallets.ReFungible], async ({helper}) => {
+ await testFailsSetPropertiesIfNotOwnerOrAdmin(await helper.rft.mintCollection(alice));
});
- async function testFailsSetPropertiesThatExeedLimits(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
- const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number;
+ async function testFailsSetPropertiesThatExeedLimits(collection: UniqueCollectionBase) {
+ const spaceLimit = (await (collection.helper!.api! as any).query.common.collectionProperties(collection.collectionId)).spaceLimit.toNumber();
- // Mute the general tx parsing error, too many bytes to process
- {
- console.error = () => {};
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]),
- )).to.be.rejected;
- }
-
- let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();
- expect(properties).to.be.empty;
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collection, [
- {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))},
- {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))},
- ]),
- )).to.be.rejectedWith(/common\.NoSpaceForProperty/);
-
- properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();
- expect(properties).to.be.empty;
- });
+ // Mute the general tx parsing error, too many bytes to process
+ {
+ console.error = () => {};
+ await expect(collection.setProperties(alice, [
+ {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))},
+ ])).to.be.rejected;
+ }
+
+ expect(await collection.getProperties(['electron'])).to.be.empty;
+
+ await expect(collection.setProperties(alice, [
+ {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))},
+ {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))},
+ ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);
+
+ expect(await collection.getProperties(['electron', 'black_hole'])).to.be.empty;
}
- it('Fails to set properties that exceed the limits (NFT)', async () => {
- await testFailsSetPropertiesThatExeedLimits({type: 'NFT'});
+
+ itSub('Fails to set properties that exceed the limits (NFT)', async ({helper}) => {
+ await testFailsSetPropertiesThatExeedLimits(await helper.nft.mintCollection(alice));
});
- it('Fails to set properties that exceed the limits (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testFailsSetPropertiesThatExeedLimits({type: 'ReFungible'});
+ itSub.ifWithPallets('Fails to set properties that exceed the limits (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ await testFailsSetPropertiesThatExeedLimits(await helper.rft.mintCollection(alice));
});
- async function testFailsSetMorePropertiesThanAllowed(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- const propertiesToBeSet = [];
- for (let i = 0; i < 65; i++) {
- propertiesToBeSet.push({
- key: 'electron_' + i,
- value: Math.random() > 0.5 ? 'high' : 'low',
- });
- }
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collection, propertiesToBeSet),
- )).to.be.rejectedWith(/common\.PropertyLimitReached/);
-
- const properties = (await api.query.common.collectionProperties(collection)).toJSON();
- expect(properties.map).to.be.empty;
- expect(properties.consumedSpace).to.equal(0);
- });
+ async function testFailsSetMorePropertiesThanAllowed(collection: UniqueCollectionBase) {
+ const propertiesToBeSet = [];
+ for (let i = 0; i < 65; i++) {
+ propertiesToBeSet.push({
+ key: 'electron_' + i,
+ value: Math.random() > 0.5 ? 'high' : 'low',
+ });
+ }
+
+ await expect(collection.setProperties(alice, propertiesToBeSet)).
+ to.be.rejectedWith(/common\.PropertyLimitReached/);
+
+ expect(await collection.getProperties()).to.be.empty;
}
- it('Fails to set more properties than it is allowed (NFT)', async () => {
- await testFailsSetMorePropertiesThanAllowed({type: 'NFT'});
+
+ itSub('Fails to set more properties than it is allowed (NFT)', async ({helper}) => {
+ await testFailsSetMorePropertiesThanAllowed(await helper.nft.mintCollection(alice));
});
- it('Fails to set more properties than it is allowed (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testFailsSetMorePropertiesThanAllowed({type: 'ReFungible'});
+ itSub.ifWithPallets('Fails to set more properties than it is allowed (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ await testFailsSetMorePropertiesThanAllowed(await helper.rft.mintCollection(alice));
});
- async function testFailsSetPropertiesWithInvalidNames(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- const invalidProperties = [
- [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],
- [{key: 'Mr/Sandman', value: 'Bring me a gene'}],
- [{key: 'déjà vu', value: 'hmm...'}],
- ];
-
- for (let i = 0; i < invalidProperties.length; i++) {
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collection, invalidProperties[i]),
- ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
- }
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]),
- ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collection, [
- {key: 'CRISPR-Cas9', value: 'rewriting nature!'},
- ]),
- ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;
-
- const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');
-
- const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();
- expect(properties).to.be.deep.equal([
- {key: 'CRISPR-Cas9', value: 'rewriting nature!'},
- ]);
-
- for (let i = 0; i < invalidProperties.length; i++) {
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)),
- ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
- }
- });
+ async function testFailsSetPropertiesWithInvalidNames(collection: UniqueCollectionBase) {
+ const invalidProperties = [
+ [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],
+ [{key: 'Mr/Sandman', value: 'Bring me a gene'}],
+ [{key: 'déjà vu', value: 'hmm...'}],
+ ];
+
+ for (let i = 0; i < invalidProperties.length; i++) {
+ await expect(
+ collection.setProperties(alice, invalidProperties[i]),
+ `on rejecting the new badly-named property #${i}`,
+ ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
+ }
+
+ await expect(
+ collection.setProperties(alice, [{key: '', value: 'nothing must not exist'}]),
+ 'on rejecting an unnamed property',
+ ).to.be.rejectedWith(/common\.EmptyPropertyKey/);
+
+ await expect(
+ collection.setProperties(alice, [{key: 'CRISPR-Cas9', value: 'rewriting nature!'}]),
+ 'on setting the correctly-but-still-badly-named property',
+ ).to.be.fulfilled;
+
+ const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');
+
+ const properties = await collection.getProperties(keys);
+ expect(properties).to.be.deep.equal([
+ {key: 'CRISPR-Cas9', value: 'rewriting nature!'},
+ ]);
+
+ for (let i = 0; i < invalidProperties.length; i++) {
+ await expect(
+ collection.deleteProperties(alice, invalidProperties[i].map(propertySet => propertySet.key)),
+ `on trying to delete the non-existent badly-named property #${i}`,
+ ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
+ }
}
- it('Fails to set properties with invalid names (NFT)', async () => {
- await testFailsSetPropertiesWithInvalidNames({type: 'NFT'});
+
+ itSub('Fails to set properties with invalid names (NFT)', async ({helper}) => {
+ await testFailsSetPropertiesWithInvalidNames(await helper.nft.mintCollection(alice));
});
- it('Fails to set properties with invalid names (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testFailsSetPropertiesWithInvalidNames({type: 'ReFungible'});
+ itSub.ifWithPallets('Fails to set properties with invalid names (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ await testFailsSetPropertiesWithInvalidNames(await helper.rft.mintCollection(alice));
});
});
// ---------- ACCESS RIGHTS
describe('Integration Test: Access Rights to Token Properties', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
});
});
- it('Reads access rights to properties of a collection', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess();
- const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();
- expect(propertyRights).to.be.empty;
- });
+ itSub('Reads access rights to properties of a collection', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const propertyRights = (await helper.api!.query.common.collectionPropertyPermissions(collection.collectionId)).toJSON();
+ expect(propertyRights).to.be.empty;
});
- async function testSetsAccessRightsToProperties(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]),
- )).to.not.be.rejected;
-
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]),
- )).to.not.be.rejected;
-
- const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();
- expect(propertyRights).to.be.deep.equal([
- {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},
- {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},
- ]);
- });
+ async function testSetsAccessRightsToProperties(collection: UniqueNFTCollection | UniqueRFTCollection) {
+ await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true}}]))
+ .to.be.fulfilled;
+
+ await collection.addAdmin(alice, {Substrate: bob.address});
+
+ await expect(collection.setTokenPropertyPermissions(bob, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]))
+ .to.be.fulfilled;
+
+ const propertyRights = await collection.getPropertyPermissions(['skullduggery', 'mindgame']);
+ expect(propertyRights).to.include.deep.members([
+ {key: 'skullduggery', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}},
+ {key: 'mindgame', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},
+ ]);
}
- it('Sets access rights to properties of a collection (NFT)', async () => {
- await testSetsAccessRightsToProperties({type: 'NFT'});
+
+ itSub('Sets access rights to properties of a collection (NFT)', async ({helper}) => {
+ await testSetsAccessRightsToProperties(await helper.nft.mintCollection(alice));
});
- it('Sets access rights to properties of a collection (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testSetsAccessRightsToProperties({type: 'ReFungible'});
+ itSub.ifWithPallets('Sets access rights to properties of a collection (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ await testSetsAccessRightsToProperties(await helper.rft.mintCollection(alice));
});
- async function testChangesAccessRightsToProperty(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]),
- )).to.not.be.rejected;
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]),
- )).to.not.be.rejected;
-
- const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();
- expect(propertyRights).to.be.deep.equal([
- {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},
- ]);
- });
+ async function testChangesAccessRightsToProperty(collection: UniqueNFTCollection | UniqueRFTCollection) {
+ await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]))
+ .to.be.fulfilled;
+
+ await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]))
+ .to.be.fulfilled;
+
+ const propertyRights = await collection.getPropertyPermissions();
+ expect(propertyRights).to.be.deep.equal([
+ {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},
+ ]);
}
- it('Changes access rights to properties of a NFT collection', async () => {
- await testChangesAccessRightsToProperty({type: 'NFT'});
+
+ itSub('Changes access rights to properties of a NFT collection', async ({helper}) => {
+ await testChangesAccessRightsToProperty(await helper.nft.mintCollection(alice));
});
- it('Changes access rights to properties of a ReFungible collection', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testChangesAccessRightsToProperty({type: 'ReFungible'});
+ itSub.ifWithPallets('Changes access rights to properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
+ await testChangesAccessRightsToProperty(await helper.rft.mintCollection(alice));
});
});
describe('Negative Integration Test: Access Rights to Token Properties', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
});
});
- async function testPreventsFromSettingAccessRightsNotAdminOrOwner(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]),
- )).to.be.rejectedWith(/common\.NoPermission/);
-
- const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();
- expect(propertyRights).to.be.empty;
- });
+ async function testPreventsFromSettingAccessRightsNotAdminOrOwner(collection: UniqueNFTCollection | UniqueRFTCollection) {
+ await expect(collection.setTokenPropertyPermissions(bob, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]))
+ .to.be.rejectedWith(/common\.NoPermission/);
+
+ const propertyRights = await collection.getPropertyPermissions(['skullduggery']);
+ expect(propertyRights).to.be.empty;
}
- it('Prevents from setting access rights to properties of a NFT collection if not an onwer/admin', async () => {
- await testPreventsFromSettingAccessRightsNotAdminOrOwner({type: 'NFT'});
+
+ itSub('Prevents from setting access rights to properties of a NFT collection if not an onwer/admin', async ({helper}) => {
+ await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.nft.mintCollection(alice));
});
- it('Prevents from setting access rights to properties of a ReFungible collection if not an onwer/admin', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testPreventsFromSettingAccessRightsNotAdminOrOwner({type: 'ReFungible'});
+ itSub.ifWithPallets('Prevents from setting access rights to properties of a ReFungible collection if not an onwer/admin', [Pallets.ReFungible], async ({helper}) => {
+ await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.rft.mintCollection(alice));
});
- async function testPreventFromAddingTooManyPossibleProperties(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- const constitution = [];
- for (let i = 0; i < 65; i++) {
- constitution.push({
- key: 'property_' + i,
- permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},
- });
- }
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, constitution),
- )).to.be.rejectedWith(/common\.PropertyLimitReached/);
-
- const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();
- expect(propertyRights).to.be.empty;
- });
+ async function testPreventFromAddingTooManyPossibleProperties(collection: UniqueNFTCollection | UniqueRFTCollection) {
+ const constitution = [];
+ for (let i = 0; i < 65; i++) {
+ constitution.push({
+ key: 'property_' + i,
+ permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},
+ });
+ }
+
+ await expect(collection.setTokenPropertyPermissions(alice, constitution))
+ .to.be.rejectedWith(/common\.PropertyLimitReached/);
+
+ const propertyRights = await collection.getPropertyPermissions();
+ expect(propertyRights).to.be.empty;
}
- it('Prevents from adding too many possible properties (NFT)', async () => {
- await testPreventFromAddingTooManyPossibleProperties({type: 'NFT'});
+
+ itSub('Prevents from adding too many possible properties (NFT)', async ({helper}) => {
+ await testPreventFromAddingTooManyPossibleProperties(await helper.nft.mintCollection(alice));
});
- it('Prevents from adding too many possible properties (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testPreventFromAddingTooManyPossibleProperties({type: 'ReFungible'});
+ itSub.ifWithPallets('Prevents from adding too many possible properties (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ await testPreventFromAddingTooManyPossibleProperties(await helper.rft.mintCollection(alice));
});
- async function testPreventAccessRightsModifiedIfConstant(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]),
- )).to.not.be.rejected;
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]),
- )).to.be.rejectedWith(/common\.NoPermission/);
-
- const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();
- expect(propertyRights).to.deep.equal([
- {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},
- ]);
- });
+ async function testPreventAccessRightsModifiedIfConstant(collection: UniqueNFTCollection | UniqueRFTCollection) {
+ await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]))
+ .to.be.fulfilled;
+
+ await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {collectionAdmin: true}}]))
+ .to.be.rejectedWith(/common\.NoPermission/);
+
+ const propertyRights = await collection.getPropertyPermissions(['skullduggery']);
+ expect(propertyRights).to.deep.equal([
+ {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},
+ ]);
}
- it('Prevents access rights to be modified if constant (NFT)', async () => {
- await testPreventAccessRightsModifiedIfConstant({type: 'NFT'});
+
+ itSub('Prevents access rights to be modified if constant (NFT)', async ({helper}) => {
+ await testPreventAccessRightsModifiedIfConstant(await helper.nft.mintCollection(alice));
});
- it('Prevents access rights to be modified if constant (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testPreventAccessRightsModifiedIfConstant({type: 'ReFungible'});
+ itSub.ifWithPallets('Prevents access rights to be modified if constant (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ await testPreventAccessRightsModifiedIfConstant(await helper.rft.mintCollection(alice));
});
- async function testPreventsAddingPropertiesWithInvalidNames(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
-
- const invalidProperties = [
- [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],
- [{key: 'G#4', permission: {tokenOwner: true}}],
- [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],
- ];
-
- for (let i = 0; i < invalidProperties.length; i++) {
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, invalidProperties[i]),
- ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
- }
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: '', permission: {}}]),
- ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);
-
- const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [
- {key: correctKey, permission: {collectionAdmin: true}},
- ]),
- ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;
-
- const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');
-
- const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();
- expect(propertyRights).to.be.deep.equal([
- {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},
- ]);
- });
+ async function testPreventsAddingPropertiesWithInvalidNames(collection: UniqueNFTCollection | UniqueRFTCollection) {
+ const invalidProperties = [
+ [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],
+ [{key: 'G#4', permission: {tokenOwner: true}}],
+ [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],
+ ];
+
+ for (let i = 0; i < invalidProperties.length; i++) {
+ await expect(
+ collection.setTokenPropertyPermissions(alice, invalidProperties[i]),
+ `on setting the new badly-named property #${i}`,
+ ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
+ }
+
+ await expect(
+ collection.setTokenPropertyPermissions(alice, [{key: '', permission: {}}]),
+ 'on rejecting an unnamed property',
+ ).to.be.rejectedWith(/common\.EmptyPropertyKey/);
+
+ const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string
+ await expect(
+ collection.setTokenPropertyPermissions(alice, [
+ {key: correctKey, permission: {collectionAdmin: true}},
+ ]),
+ 'on setting the correctly-but-still-badly-named property',
+ ).to.be.fulfilled;
+
+ const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');
+
+ const propertyRights = await collection.getPropertyPermissions(keys);
+ expect(propertyRights).to.be.deep.equal([
+ {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},
+ ]);
}
- it('Prevents adding properties with invalid names (NFT)', async () => {
- await testPreventsAddingPropertiesWithInvalidNames({type: 'NFT'});
+
+ itSub('Prevents adding properties with invalid names (NFT)', async ({helper}) => {
+ await testPreventsAddingPropertiesWithInvalidNames(await helper.nft.mintCollection(alice));
});
- it('Prevents adding properties with invalid names (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testPreventsAddingPropertiesWithInvalidNames({type: 'ReFungible'});
+ itSub.ifWithPallets('Prevents adding properties with invalid names (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ await testPreventsAddingPropertiesWithInvalidNames(await helper.rft.mintCollection(alice));
});
});
// ---------- TOKEN PROPERTIES
describe('Integration Test: Token Properties', () => {
+ let alice: IKeyringPair; // collection owner
+ let bob: IKeyringPair; // collection admin
+ let charlie: IKeyringPair; // token owner
+
let permissions: {permission: any, signers: IKeyringPair[]}[];
before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice'); // collection owner
- bob = privateKeyWrapper('//Bob'); // collection admin
- charlie = privateKeyWrapper('//Charlie'); // token owner
-
- permissions = [
- {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},
- {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},
- {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},
- {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},
- {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},
- {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},
- ];
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
});
+
+ // todo:playgrounds probably separate these tests later
+ permissions = [
+ {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},
+ {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},
+ {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},
+ {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},
+ {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},
+ {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},
+ ];
});
-
- async function testReadsYetEmptyProperties(mode: CollectionMode) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
- const token = await createItemExpectSuccess(alice, collection, mode.type);
- const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();
- expect(properties.map).to.be.empty;
- expect(properties.consumedSpace).to.be.equal(0);
-
- const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;
- expect(tokenData).to.be.empty;
- });
+ async function testReadsYetEmptyProperties(token: UniqueNFTToken | UniqueRFTToken) {
+ const properties = await token.getProperties();
+ expect(properties).to.be.empty;
+
+ const tokenData = await token.getData();
+ expect(tokenData!.properties).to.be.empty;
}
- it('Reads yet empty properties of a token (NFT)', async () => {
- await testReadsYetEmptyProperties({type: 'NFT'});
- });
- it('Reads yet empty properties of a token (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testReadsYetEmptyProperties({type: 'ReFungible'});
+ itSub('Reads yet empty properties of a token (NFT)', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const token = await collection.mintToken(alice);
+ await testReadsYetEmptyProperties(token);
});
- async function testAssignPropertiesAccordingToPermissions(mode: CollectionMode, pieces: number) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
- const token = await createItemExpectSuccess(alice, collection, mode.type);
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);
+ itSub.ifWithPallets('Reads yet empty properties of a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice);
+ await testReadsYetEmptyProperties(token);
+ });
- const propertyKeys: string[] = [];
- let i = 0;
- for (const permission of permissions) {
- for (const signer of permission.signers) {
- const key = i + '_' + signer.address;
- propertyKeys.push(key);
+ async function testAssignPropertiesAccordingToPermissions(token: UniqueNFTToken | UniqueRFTToken, pieces: bigint) {
+ await token.collection.addAdmin(alice, {Substrate: bob.address});
+ await token.transfer(alice, {Substrate: charlie.address}, pieces);
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]),
- ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;
+ const propertyKeys: string[] = [];
+ let i = 0;
+ for (const permission of permissions) {
+ i++;
+ let j = 0;
+ for (const signer of permission.signers) {
+ j++;
+ const key = i + '_' + signer.address;
+ propertyKeys.push(key);
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]),
- ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;
- }
+ await expect(
+ token.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]),
+ `on setting permission #${i} by alice`,
+ ).to.be.fulfilled;
- i++;
+ await expect(
+ token.setProperties(signer, [{key: key, value: 'Serotonin increase'}]),
+ `on adding property #${i} by signer #${j}`,
+ ).to.be.fulfilled;
}
+ }
- const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];
- const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];
- for (let i = 0; i < properties.length; i++) {
- expect(properties[i].value).to.be.equal('Serotonin increase');
- expect(tokensData[i].value).to.be.equal('Serotonin increase');
- }
- });
+ const properties = await token.getProperties(propertyKeys);
+ const tokenData = await token.getData();
+ for (let i = 0; i < properties.length; i++) {
+ expect(properties[i].value).to.be.equal('Serotonin increase');
+ expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase');
+ }
}
- it('Assigns properties to a token according to permissions (NFT)', async () => {
- await testAssignPropertiesAccordingToPermissions({type: 'NFT'}, 1);
+
+ itSub('Assigns properties to a token according to permissions (NFT)', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const token = await collection.mintToken(alice);
+ await testAssignPropertiesAccordingToPermissions(token, 1n);
});
- it('Assigns properties to a token according to permissions (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testAssignPropertiesAccordingToPermissions({type: 'ReFungible'}, 100);
+ itSub.ifWithPallets('Assigns properties to a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 100n);
+ await testAssignPropertiesAccordingToPermissions(token, 100n);
});
- async function testChangesPropertiesAccordingPermission(mode: CollectionMode, pieces: number) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
- const token = await createItemExpectSuccess(alice, collection, mode.type);
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);
+ async function testChangesPropertiesAccordingPermission(token: UniqueNFTToken | UniqueRFTToken, pieces: bigint) {
+ await token.collection.addAdmin(alice, {Substrate: bob.address});
+ await token.transfer(alice, {Substrate: charlie.address}, pieces);
+
+ const propertyKeys: string[] = [];
+ let i = 0;
+ for (const permission of permissions) {
+ i++;
+ if (!permission.permission.mutable) continue;
+
+ let j = 0;
+ for (const signer of permission.signers) {
+ j++;
+ const key = i + '_' + signer.address;
+ propertyKeys.push(key);
+
+ await expect(
+ token.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]),
+ `on setting permission #${i} by alice`,
+ ).to.be.fulfilled;
+
+ await expect(
+ token.setProperties(signer, [{key, value: 'Serotonin increase'}]),
+ `on adding property #${i} by signer #${j}`,
+ ).to.be.fulfilled;
- const propertyKeys: string[] = [];
- let i = 0;
- for (const permission of permissions) {
- if (!permission.permission.mutable) continue;
-
- for (const signer of permission.signers) {
- const key = i + '_' + signer.address;
- propertyKeys.push(key);
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]),
- ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;
-
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]),
- ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;
-
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]),
- ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;
- }
-
- i++;
+ await expect(
+ token.setProperties(signer, [{key, value: 'Serotonin stable'}]),
+ `on changing property #${i} by signer #${j}`,
+ ).to.be.fulfilled;
}
-
- const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];
- const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];
- for (let i = 0; i < properties.length; i++) {
- expect(properties[i].value).to.be.equal('Serotonin stable');
- expect(tokensData[i].value).to.be.equal('Serotonin stable');
- }
- });
+ }
+
+ const properties = await token.getProperties(propertyKeys);
+ const tokenData = await token.getData();
+ for (let i = 0; i < properties.length; i++) {
+ expect(properties[i].value).to.be.equal('Serotonin stable');
+ expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable');
+ }
}
- it('Changes properties of a token according to permissions (NFT)', async () => {
- await testChangesPropertiesAccordingPermission({type: 'NFT'}, 1);
+
+ itSub('Changes properties of a token according to permissions (NFT)', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const token = await collection.mintToken(alice);
+ await testChangesPropertiesAccordingPermission(token, 1n);
});
- it('Changes properties of a token according to permissions (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testChangesPropertiesAccordingPermission({type: 'ReFungible'}, 100);
+ itSub.ifWithPallets('Changes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 100n);
+ await testChangesPropertiesAccordingPermission(token, 100n);
});
- async function testDeletePropertiesAccordingPermission(mode: CollectionMode, pieces: number) {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: mode});
- const token = await createItemExpectSuccess(alice, collection, mode.type);
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);
+ async function testDeletePropertiesAccordingPermission(token: UniqueNFTToken | UniqueRFTToken, pieces: bigint) {
+ await token.collection.addAdmin(alice, {Substrate: bob.address});
+ await token.transfer(alice, {Substrate: charlie.address}, pieces);
+
+ const propertyKeys: string[] = [];
+ let i = 0;
+
+ for (const permission of permissions) {
+ i++;
+ if (!permission.permission.mutable) continue;
+
+ let j = 0;
+ for (const signer of permission.signers) {
+ j++;
+ const key = i + '_' + signer.address;
+ propertyKeys.push(key);
+
+ await expect(
+ token.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]),
+ `on setting permission #${i} by alice`,
+ ).to.be.fulfilled;
- const propertyKeys: string[] = [];
- let i = 0;
-
- for (const permission of permissions) {
- if (!permission.permission.mutable) continue;
-
- for (const signer of permission.signers) {
- const key = i + '_' + signer.address;
- propertyKeys.push(key);
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]),
- ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;
-
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]),
- ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;
-
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.deleteTokenProperties(collection, token, [key]),
- ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;
- }
-
- i++;
+ await expect(
+ token.setProperties(signer, [{key, value: 'Serotonin increase'}]),
+ `on adding property #${i} by signer #${j}`,
+ ).to.be.fulfilled;
+
+ await expect(
+ token.deleteProperties(signer, [key]),
+ `on deleting property #${i} by signer #${j}`,
+ ).to.be.fulfilled;
}
-
- const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];
- expect(properties).to.be.empty;
- const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];
- expect(tokensData).to.be.empty;
- expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);
- });
+ }
+
+ expect(await token.getProperties(propertyKeys)).to.be.empty;
+ expect((await token.getData())!.properties).to.be.empty;
}
- it('Deletes properties of a token according to permissions (NFT)', async () => {
- await testDeletePropertiesAccordingPermission({type: 'NFT'}, 1);
+
+ itSub('Deletes properties of a token according to permissions (NFT)', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const token = await collection.mintToken(alice);
+ await testDeletePropertiesAccordingPermission(token, 1n);
});
- it('Deletes properties of a token according to permissions (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testDeletePropertiesAccordingPermission({type: 'ReFungible'}, 100);
+ itSub.ifWithPallets('Deletes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 100n);
+ await testDeletePropertiesAccordingPermission(token, 100n);
});
- it('Assigns properties to a nested token according to permissions', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const token = await createItemExpectSuccess(alice, collection, 'NFT');
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)});
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- await transferExpectSuccess(collection, token, alice, charlie);
+ itSub('Assigns properties to a nested token according to permissions', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const collectionB = await helper.nft.mintCollection(alice);
+ const targetToken = await collectionA.mintToken(alice);
+ const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAddress());
- const propertyKeys: string[] = [];
- let i = 0;
- for (const permission of permissions) {
- for (const signer of permission.signers) {
- const key = i + '_' + signer.address;
- propertyKeys.push(key);
+ await collectionB.addAdmin(alice, {Substrate: bob.address});
+ await targetToken.transfer(alice, {Substrate: charlie.address});
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]),
- ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;
+ const propertyKeys: string[] = [];
+ let i = 0;
+ for (const permission of permissions) {
+ i++;
+ let j = 0;
+ for (const signer of permission.signers) {
+ j++;
+ const key = i + '_' + signer.address;
+ propertyKeys.push(key);
+
+ await expect(
+ nestedToken.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]),
+ `on setting permission #${i} by alice`,
+ ).to.be.fulfilled;
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]),
- ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;
- }
-
- i++;
+ await expect(
+ nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]),
+ `on adding property #${i} by signer #${j}`,
+ ).to.be.fulfilled;
}
- const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toHuman() as any[];
- const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toHuman().properties as any[];
- for (let i = 0; i < properties.length; i++) {
- expect(properties[i].value).to.be.equal('Serotonin increase');
- expect(tokensData[i].value).to.be.equal('Serotonin increase');
- }
- });
+ }
+
+ const properties = await nestedToken.getProperties(propertyKeys);
+ const tokenData = await nestedToken.getData();
+ for (let i = 0; i < properties.length; i++) {
+ expect(properties[i].value).to.be.equal('Serotonin increase');
+ expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase');
+ }
+ expect(await targetToken.getProperties()).to.be.empty;
});
- it('Changes properties of a nested token according to permissions', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const token = await createItemExpectSuccess(alice, collection, 'NFT');
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)});
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- await transferExpectSuccess(collection, token, alice, charlie);
+ itSub('Changes properties of a nested token according to permissions', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const collectionB = await helper.nft.mintCollection(alice);
+ const targetToken = await collectionA.mintToken(alice);
+ const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAddress());
- const propertyKeys: string[] = [];
- let i = 0;
- for (const permission of permissions) {
- if (!permission.permission.mutable) continue;
-
- for (const signer of permission.signers) {
- const key = i + '_' + signer.address;
- propertyKeys.push(key);
+ await collectionB.addAdmin(alice, {Substrate: bob.address});
+ await targetToken.transfer(alice, {Substrate: charlie.address});
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]),
- ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;
+ const propertyKeys: string[] = [];
+ let i = 0;
+ for (const permission of permissions) {
+ i++;
+ if (!permission.permission.mutable) continue;
+
+ let j = 0;
+ for (const signer of permission.signers) {
+ j++;
+ const key = i + '_' + signer.address;
+ propertyKeys.push(key);
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]),
- ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;
+ await expect(
+ nestedToken.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]),
+ `on setting permission #${i} by alice`,
+ ).to.be.fulfilled;
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin stable'}]),
- ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;
- }
+ await expect(
+ nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]),
+ `on adding property #${i} by signer #${j}`,
+ ).to.be.fulfilled;
- i++;
+ await expect(
+ nestedToken.setProperties(signer, [{key, value: 'Serotonin stable'}]),
+ `on changing property #${i} by signer #${j}`,
+ ).to.be.fulfilled;
}
+ }
- const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toHuman() as any[];
- const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toHuman().properties as any[];
- for (let i = 0; i < properties.length; i++) {
- expect(properties[i].value).to.be.equal('Serotonin stable');
- expect(tokensData[i].value).to.be.equal('Serotonin stable');
- }
- });
+ const properties = await nestedToken.getProperties(propertyKeys);
+ const tokenData = await nestedToken.getData();
+ for (let i = 0; i < properties.length; i++) {
+ expect(properties[i].value).to.be.equal('Serotonin stable');
+ expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable');
+ }
+ expect(await targetToken.getProperties()).to.be.empty;
});
- it('Deletes properties of a nested token according to permissions', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const token = await createItemExpectSuccess(alice, collection, 'NFT');
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)});
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- await transferExpectSuccess(collection, token, alice, charlie);
+ itSub('Deletes properties of a nested token according to permissions', async ({helper}) => {
+ const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const collectionB = await helper.nft.mintCollection(alice);
+ const targetToken = await collectionA.mintToken(alice);
+ const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAddress());
- const propertyKeys: string[] = [];
- let i = 0;
+ await collectionB.addAdmin(alice, {Substrate: bob.address});
+ await targetToken.transfer(alice, {Substrate: charlie.address});
- for (const permission of permissions) {
- if (!permission.permission.mutable) continue;
-
- for (const signer of permission.signers) {
- const key = i + '_' + signer.address;
- propertyKeys.push(key);
+ const propertyKeys: string[] = [];
+ let i = 0;
+ for (const permission of permissions) {
+ i++;
+ if (!permission.permission.mutable) continue;
+
+ let j = 0;
+ for (const signer of permission.signers) {
+ j++;
+ const key = i + '_' + signer.address;
+ propertyKeys.push(key);
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]),
- ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;
+ await expect(
+ nestedToken.collection.setTokenPropertyPermissions(alice, [{key: key, permission: permission.permission}]),
+ `on setting permission #${i} by alice`,
+ ).to.be.fulfilled;
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]),
- ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;
+ await expect(
+ nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]),
+ `on adding property #${i} by signer #${j}`,
+ ).to.be.fulfilled;
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.deleteTokenProperties(collection, nestedToken, [key]),
- ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;
- }
-
- i++;
+ await expect(
+ nestedToken.deleteProperties(signer, [key]),
+ `on deleting property #${i} by signer #${j}`,
+ ).to.be.fulfilled;
}
+ }
- const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toJSON() as any[];
- expect(properties).to.be.empty;
- const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toJSON().properties as any[];
- expect(tokensData).to.be.empty;
- expect((await api.query.nonfungible.tokenProperties(collection, nestedToken)).toJSON().consumedSpace).to.be.equal(0);
- });
+ expect(await nestedToken.getProperties(propertyKeys)).to.be.empty;
+ expect((await nestedToken.getData())!.properties).to.be.empty;
+ expect(await targetToken.getProperties()).to.be.empty;
});
});
describe('Negative Integration Test: Token Properties', () => {
- let collection: number;
- let token: number;
- let originalSpace: number;
+ let alice: IKeyringPair; // collection owner
+ let bob: IKeyringPair; // collection admin
+ let charlie: IKeyringPair; // token owner
+
let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];
before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
- charlie = privateKeyWrapper('//Charlie');
- const dave = privateKeyWrapper('//Dave');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ let dave: IKeyringPair;
+ [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);
+ // todo:playgrounds probably separate these tests later
constitution = [
{permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},
{permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},
@@ -1010,278 +804,255 @@
});
});
- async function prepare(mode: CollectionMode, pieces: number) {
- collection = await createCollectionExpectSuccess({mode: mode});
- token = await createItemExpectSuccess(alice, collection, mode.type);
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
- await transferExpectSuccess(collection, token, alice, charlie, pieces, mode.type);
-
- await usingApi(async api => {
- let i = 0;
- for (const passage of constitution) {
- const signer = passage.signers[0];
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]),
- ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;
-
- await expect(executeTransaction(
- api,
- signer,
- api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]),
- ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;
-
- i++;
- }
-
- originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;
- });
+ async function getConsumedSpace(api: any, collectionId: number, tokenId: number, mode: 'NFT' | 'RFT'): Promise<number> {
+ return (await (mode == 'NFT' ? api.query.nonfungible : api.query.refungible).tokenProperties(collectionId, tokenId)).toJSON().consumedSpace;
+ }
+
+ async function prepare(token: UniqueNFTToken | UniqueRFTToken, pieces: bigint): Promise<number> {
+ await token.collection.addAdmin(alice, {Substrate: bob.address});
+ await token.transfer(alice, {Substrate: charlie.address}, pieces);
+
+ let i = 0;
+ for (const passage of constitution) {
+ i++;
+ const signer = passage.signers[0];
+
+ await expect(
+ token.collection.setTokenPropertyPermissions(alice, [{key: `${i}`, permission: passage.permission}]),
+ `on setting permission ${i} by alice`,
+ ).to.be.fulfilled;
+
+ await expect(
+ token.setProperties(signer, [{key: `${i}`, value: 'Serotonin increase'}]),
+ `on adding property ${i} by ${signer.address}`,
+ ).to.be.fulfilled;
+ }
+
+ const originalSpace = await getConsumedSpace(token.collection.helper.api, token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
+ return originalSpace;
}
- async function testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(mode: CollectionMode, pieces: number) {
- await prepare(mode, pieces);
-
- await usingApi(async api => {
- let i = -1;
- for (const forbiddance of constitution) {
- i++;
- if (!forbiddance.permission.mutable) continue;
-
- await expect(executeTransaction(
- api,
- forbiddance.sinner,
- api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]),
- ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);
-
- await expect(executeTransaction(
- api,
- forbiddance.sinner,
- api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]),
- ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);
- }
-
- const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();
- expect(properties.consumedSpace).to.be.equal(originalSpace);
- });
+ async function testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token: UniqueNFTToken | UniqueRFTToken, pieces: bigint) {
+ const originalSpace = await prepare(token, pieces);
+
+ let i = 0;
+ for (const forbiddance of constitution) {
+ i++;
+ if (!forbiddance.permission.mutable) continue;
+
+ await expect(
+ token.setProperties(forbiddance.sinner, [{key: `${i}`, value: 'Serotonin down'}]),
+ `on failing to change property ${i} by the malefactor`,
+ ).to.be.rejectedWith(/common\.NoPermission/);
+
+ await expect(
+ token.deleteProperties(forbiddance.sinner, [`${i}`]),
+ `on failing to delete property ${i} by the malefactor`,
+ ).to.be.rejectedWith(/common\.NoPermission/);
+ }
+
+ const consumedSpace = await getConsumedSpace(token.collection.helper.api, token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
+ expect(consumedSpace).to.be.equal(originalSpace);
}
- it('Forbids changing/deleting properties of a token if the user is outside of permissions (NFT)', async () => {
- await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions({type: 'NFT'}, 1);
+
+ itSub('Forbids changing/deleting properties of a token if the user is outside of permissions (NFT)', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const token = await collection.mintToken(alice);
+ await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, 1n);
});
- it('Forbids changing/deleting properties of a token if the user is outside of permissions (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions({type: 'ReFungible'}, 100);
+ itSub.ifWithPallets('Forbids changing/deleting properties of a token if the user is outside of permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 100n);
+ await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, 100n);
});
- async function testForbidsChangingDeletingPropertiesIfPropertyImmutable(mode: CollectionMode, pieces: number) {
- await prepare(mode, pieces);
-
- await usingApi(async api => {
- let i = -1;
- for (const permission of constitution) {
- i++;
- if (permission.permission.mutable) continue;
-
- await expect(executeTransaction(
- api,
- permission.signers[0],
- api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]),
- ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);
+ async function testForbidsChangingDeletingPropertiesIfPropertyImmutable(token: UniqueNFTToken | UniqueRFTToken, pieces: bigint) {
+ const originalSpace = await prepare(token, pieces);
+
+ let i = 0;
+ for (const permission of constitution) {
+ i++;
+ if (permission.permission.mutable) continue;
+
+ await expect(
+ token.setProperties(permission.signers[0], [{key: `${i}`, value: 'Serotonin down'}]),
+ `on failing to change property ${i} by signer #0`,
+ ).to.be.rejectedWith(/common\.NoPermission/);
+
+ await expect(
+ token.deleteProperties(permission.signers[0], [i.toString()]),
+ `on failing to delete property ${i} by signer #0`,
+ ).to.be.rejectedWith(/common\.NoPermission/);
+ }
- await expect(executeTransaction(
- api,
- permission.signers[0],
- api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]),
- ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);
- }
-
- const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();
- expect(properties.consumedSpace).to.be.equal(originalSpace);
- });
+ const consumedSpace = await getConsumedSpace(token.collection.helper.api, token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
+ expect(consumedSpace).to.be.equal(originalSpace);
}
- it('Forbids changing/deleting properties of a token if the property is permanent (immutable) (NFT)', async () => {
- await testForbidsChangingDeletingPropertiesIfPropertyImmutable({type: 'NFT'}, 1);
+
+ itSub('Forbids changing/deleting properties of a token if the property is permanent (immutable) (NFT)', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const token = await collection.mintToken(alice);
+ await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, 1n);
});
- it('Forbids changing/deleting properties of a token if the property is permanent (immutable) (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testForbidsChangingDeletingPropertiesIfPropertyImmutable({type: 'ReFungible'}, 100);
+ itSub.ifWithPallets('Forbids changing/deleting properties of a token if the property is permanent (immutable) (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 100n);
+ await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, 100n);
});
- async function testForbidsAddingPropertiesIfPropertyNotDeclared(mode: CollectionMode, pieces: number) {
- await prepare(mode, pieces);
+ async function testForbidsAddingPropertiesIfPropertyNotDeclared(token: UniqueNFTToken | UniqueRFTToken, pieces: bigint) {
+ const originalSpace = await prepare(token, pieces);
- await usingApi(async api => {
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]),
- ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]),
- ), 'on setting a new non-permitted property').to.not.be.rejected;
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]),
- ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);
-
- expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;
- const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();
- expect(properties.consumedSpace).to.be.equal(originalSpace);
- });
+ await expect(
+ token.setProperties(alice, [{key: 'non-existent', value: 'I exist!'}]),
+ 'on failing to add a previously non-existent property',
+ ).to.be.rejectedWith(/common\.NoPermission/);
+
+ await expect(
+ token.collection.setTokenPropertyPermissions(alice, [{key: 'now-existent', permission: {}}]),
+ 'on setting a new non-permitted property',
+ ).to.be.fulfilled;
+
+ await expect(
+ token.setProperties(alice, [{key: 'now-existent', value: 'I exist!'}]),
+ 'on failing to add a property forbidden by the \'None\' permission',
+ ).to.be.rejectedWith(/common\.NoPermission/);
+
+ expect(await token.getProperties(['non-existent', 'now-existent'])).to.be.empty;
+
+ const consumedSpace = await getConsumedSpace(token.collection.helper.api, token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
+ expect(consumedSpace).to.be.equal(originalSpace);
}
- it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (NFT)', async () => {
- await testForbidsAddingPropertiesIfPropertyNotDeclared({type: 'NFT'}, 1);
+
+ itSub('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (NFT)', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const token = await collection.mintToken(alice);
+ await testForbidsAddingPropertiesIfPropertyNotDeclared(token, 1n);
});
- it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testForbidsAddingPropertiesIfPropertyNotDeclared({type: 'ReFungible'}, 100);
+ itSub.ifWithPallets('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 100n);
+ await testForbidsAddingPropertiesIfPropertyNotDeclared(token, 100n);
});
- async function testForbidsAddingTooManyProperties(mode: CollectionMode, pieces: number) {
- await prepare(mode, pieces);
+ async function testForbidsAddingTooManyProperties(token: UniqueNFTToken | UniqueRFTToken, pieces: bigint) {
+ const originalSpace = await prepare(token, pieces);
+
+ await expect(
+ token.collection.setTokenPropertyPermissions(alice, [
+ {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}},
+ {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},
+ ]),
+ 'on setting new permissions for properties',
+ ).to.be.fulfilled;
+
+ // Mute the general tx parsing error
+ {
+ console.error = () => {};
+ await expect(token.setProperties(alice, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]))
+ .to.be.rejected;
+ }
- await usingApi(async api => {
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [
- {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}},
- {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},
- ]),
- ), 'on setting a new non-permitted property').to.not.be.rejected;
+ await expect(token.setProperties(alice, [
+ {key: 'a_holy_book', value: 'word '.repeat(3277)},
+ {key: 'young_years', value: 'neverending'.repeat(1490)},
+ ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);
- // Mute the general tx parsing error
- {
- console.error = () => {};
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]),
- )).to.be.rejected;
- }
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenProperties(collection, token, [
- {key: 'a_holy_book', value: 'word '.repeat(3277)},
- {key: 'young_years', value: 'neverending'.repeat(1490)},
- ]),
- )).to.be.rejectedWith(/common\.NoSpaceForProperty/);
-
- expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young_years'])).toJSON()).to.be.empty;
- const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();
- expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);
- });
+ expect(await token.getProperties(['a_holy_book', 'young_years'])).to.be.empty;
+ const consumedSpace = await getConsumedSpace(token.collection.helper.api, token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
+ expect(consumedSpace).to.be.equal(originalSpace);
}
- it('Forbids adding too many properties to a token (NFT)', async () => {
- await testForbidsAddingTooManyProperties({type: 'NFT'}, 1);
+
+ itSub('Forbids adding too many properties to a token (NFT)', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice);
+ const token = await collection.mintToken(alice);
+ await testForbidsAddingTooManyProperties(token, 1n);
});
- it('Forbids adding too many properties to a token (ReFungible)', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
- await testForbidsAddingTooManyProperties({type: 'ReFungible'}, 100);
+ itSub.ifWithPallets('Forbids adding too many properties to a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 100n);
+ await testForbidsAddingTooManyProperties(token, 100n);
});
});
describe('ReFungible token properties permissions tests', () => {
- let collection: number;
- let token: number;
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
before(async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ await usingPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
- charlie = privateKeyWrapper('//Charlie');
+ const donor = privateKey('//Alice');
+ [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
});
});
- beforeEach(async () => {
- await usingApi(async api => {
- collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
- token = await createItemExpectSuccess(alice, collection, 'ReFungible');
- await addCollectionAdminExpectSuccess(alice, collection, bob.address);
+ async function prepare(helper: UniqueHelper): Promise<UniqueRFTToken> {
+ const collection = await helper.rft.mintCollection(alice);
+ const token = await collection.mintToken(alice, 100n);
+
+ await collection.addAdmin(alice, {Substrate: bob.address});
+ await collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable: true, tokenOwner: true}}]);
+
+ return token;
+ }
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'key', permission: {mutable:true, tokenOwner: true}}]),
- )).to.not.be.rejected;
- });
+ itSub('Forbids adding token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) => {
+ const token = await prepare(helper);
+
+ await token.transfer(alice, {Substrate: charlie.address}, 33n);
+
+ await expect(token.setProperties(alice, [
+ {key: 'fractals', value: 'multiverse'},
+ ])).to.be.rejectedWith(/common\.NoPermission/);
});
- it('Forbids add token property with tokenOwher==true but signer have\'t all pieces', async () => {
- await usingApi(async api => {
- await transferExpectSuccess(collection, token, alice, charlie, 33, 'ReFungible');
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenProperties(collection, token, [
- {key: 'key', value: 'word'},
- ]),
- )).to.be.rejectedWith(/common\.NoPermission/);
- });
+ itSub('Forbids mutating token property with tokenOwher==true when signer doesn\'t have all pieces', async ({helper}) => {
+ const token = await prepare(helper);
+
+ await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, tokenOwner: true}}]))
+ .to.be.fulfilled;
+
+ await expect(token.setProperties(alice, [
+ {key: 'fractals', value: 'multiverse'},
+ ])).to.be.fulfilled;
+
+ await token.transfer(alice, {Substrate: charlie.address}, 33n);
+
+ await expect(token.setProperties(alice, [
+ {key: 'fractals', value: 'want to rule the world'},
+ ])).to.be.rejectedWith(/common\.NoPermission/);
});
- it('Forbids mutate token property with tokenOwher==true but signer have\'t all pieces', async () => {
- await usingApi(async api => {
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'key', permission: {mutable:true, tokenOwner: true}}]),
- )).to.not.be.rejected;
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenProperties(collection, token, [
- {key: 'key', value: 'word'},
- ]),
- )).to.be.not.rejected;
+ itSub('Forbids deleting token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) => {
+ const token = await prepare(helper);
- await transferExpectSuccess(collection, token, alice, charlie, 33, 'ReFungible');
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenProperties(collection, token, [
- {key: 'key', value: 'bad word'},
- ]),
- )).to.be.rejectedWith(/common\.NoPermission/);
- });
+ await expect(token.setProperties(alice, [
+ {key: 'fractals', value: 'one headline - why believe it'},
+ ])).to.be.fulfilled;
+
+ await token.transfer(alice, {Substrate: charlie.address}, 33n);
+
+ await expect(token.deleteProperties(alice, ['fractals'])).
+ to.be.rejectedWith(/common\.NoPermission/);
});
- it('Forbids delete token property with tokenOwher==true but signer have\'t all pieces', async () => {
- await usingApi(async api => {
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.setTokenProperties(collection, token, [
- {key: 'key', value: 'word'},
- ]),
- )).to.be.not.rejected;
+ itSub('Allows token property mutation with collectionOwner==true when admin doesn\'t have all pieces', async ({helper}) => {
+ const token = await prepare(helper);
+
+ await token.transfer(alice, {Substrate: charlie.address}, 33n);
+
+ await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, collectionAdmin: true}}]))
+ .to.be.fulfilled;
- await transferExpectSuccess(collection, token, alice, charlie, 33, 'ReFungible');
-
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.deleteTokenProperties(collection, token, [
- 'key',
- ]),
- )).to.be.rejectedWith(/common\.NoPermission/);
- });
+ await expect(token.setProperties(alice, [
+ {key: 'fractals', value: 'multiverse'},
+ ])).to.be.fulfilled;
});
});
tests/src/nesting/rules-smoke.test.tsdiffbeforeafterboth--- a/tests/src/nesting/rules-smoke.test.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import {expect} from 'chai';
-import {tokenIdToAddress} from '../eth/util/helpers';
-import usingApi, {executeTransaction} from '../substrate/substrate-api';
-import {createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, CrossAccountId, getCreateCollectionResult, requirePallets, Pallets} from '../util/helpers';
-import {IKeyringPair} from '@polkadot/types/types';
-
-describe('nesting check', () => {
- let alice!: IKeyringPair;
- let nestTarget!: CrossAccountId;
- before(async() => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- const bob = privateKeyWrapper('//Bob');
- const events = await executeTransaction(api, alice, api.tx.unique.createCollectionEx({
- mode: 'NFT',
- permissions: {
- nesting: {tokenOwner: true, restricted: []},
- },
- }));
- const collection = getCreateCollectionResult(events).collectionId;
- const token = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: bob.address});
- nestTarget = {Ethereum: tokenIdToAddress(collection, token)};
- });
- });
-
- it('called for fungible', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'Fungible',decimalPoints:0}});
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(collection, nestTarget, {Fungible: {Value: 1}})))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
-
- await createFungibleItemExpectSuccess(alice, collection, {Value:1n}, {Substrate: alice.address});
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(nestTarget, collection, 0, 1n)))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
- });
- });
-
- it('called for nonfungible', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(collection, nestTarget, {NFT: {properties: []}})))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
-
- const token = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(nestTarget, collection, token, 1n)))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
- });
- });
-
- it('called for refungible', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
-
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
- await expect(executeTransaction(api, alice, api.tx.unique.createItem(collection, nestTarget, {ReFungible: {}})))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
-
- const token = await createItemExpectSuccess(alice, collection, 'ReFungible', {Substrate: alice.address});
- await expect(executeTransaction(api, alice, api.tx.unique.transfer(nestTarget, collection, token, 1n)))
- .to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);
- });
- });
-});
tests/src/nesting/unnest.test.tsdiffbeforeafterboth--- a/tests/src/nesting/unnest.test.ts
+++ b/tests/src/nesting/unnest.test.ts
@@ -1,162 +1,126 @@
-import {expect} from 'chai';
-import {tokenIdToAddress} from '../eth/util/helpers';
-import usingApi, {executeTransaction} from '../substrate/substrate-api';
-import {
- createCollectionExpectSuccess,
- createItemExpectSuccess,
- getBalance,
- getTokenOwner,
- normalizeAccountId,
- setCollectionPermissionsExpectSuccess,
- transferExpectSuccess,
- transferFromExpectSuccess,
- requirePallets,
- Pallets,
-} from '../util/helpers';
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
import {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, Pallets, usingPlaygrounds} from '../util/playgrounds';
-let alice: IKeyringPair;
-let bob: IKeyringPair;
+describe('Integration Test: Unnesting', () => {
+ let alice: IKeyringPair;
-describe('Integration Test: Unnesting', () => {
before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice] = await helper.arrange.createAccounts([50n], donor);
});
});
- it('NFT: allows the owner to successfully unnest a token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};
-
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);
+ itSub('NFT: allows the owner to successfully unnest a token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
+
+ // Create a nested token
+ const nestedToken = await collection.mintToken(alice, targetToken.nestingAddress());
- // Unnest
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(alice), collection, nestedToken, 1),
- ), 'while unnesting').to.not.be.rejected;
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});
+ // Unnest
+ await expect(nestedToken.transferFrom(alice, targetToken.nestingAddress(), {Substrate: alice.address}), 'while unnesting').to.be.fulfilled;
+ expect(await nestedToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
- // Nest and burn
- await transferExpectSuccess(collection, nestedToken, alice, targetAddress);
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.burnFrom(collection, normalizeAccountId(targetAddress), nestedToken, 1),
- ), 'while burning').to.not.be.rejected;
- await expect(getTokenOwner(api, collection, nestedToken)).to.be.rejected;
- });
+ // Nest and burn
+ await nestedToken.nest(alice, targetToken);
+ await expect(nestedToken.burnFrom(alice, targetToken.nestingAddress()), 'while burning').to.be.fulfilled;
+ await expect(nestedToken.getOwner()).to.be.rejected;
});
- it('Fungible: allows the owner to successfully unnest a token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};
+ itSub('Fungible: allows the owner to successfully unnest a token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
- const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
- const nestedToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');
+ const collectionFT = await helper.ft.mintCollection(alice);
+
+ // Nest and unnest
+ await collectionFT.mint(alice, 10n, targetToken.nestingAddress());
+ await expect(collectionFT.transferFrom(alice, targetToken.nestingAddress(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled;
+ expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(9n);
+ expect(await collectionFT.getBalance(targetToken.nestingAddress())).to.be.equal(1n);
- // Nest and unnest
- await transferExpectSuccess(collectionFT, nestedToken, alice, targetAddress, 1, 'Fungible');
- await transferFromExpectSuccess(collectionFT, nestedToken, alice, targetAddress, alice, 1, 'Fungible');
-
- // Nest and burn
- await transferExpectSuccess(collectionFT, nestedToken, alice, targetAddress, 1, 'Fungible');
- const balanceBefore = await getBalance(api, collectionFT, normalizeAccountId(targetAddress), nestedToken);
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.burnFrom(collectionFT, normalizeAccountId(targetAddress), nestedToken, 1),
- ), 'while burning').to.not.be.rejected;
- const balanceAfter = await getBalance(api, collectionFT, normalizeAccountId(targetAddress), nestedToken);
- expect(balanceAfter + BigInt(1)).to.be.equal(balanceBefore);
- });
+ // Nest and burn
+ await collectionFT.transfer(alice, targetToken.nestingAddress(), 5n);
+ await expect(collectionFT.burnTokensFrom(alice, targetToken.nestingAddress(), 6n), 'while burning').to.be.fulfilled;
+ expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(4n);
+ expect(await collectionFT.getBalance(targetToken.nestingAddress())).to.be.equal(0n);
+ expect(await targetToken.getChildren()).to.be.length(0);
});
- it('ReFungible: allows the owner to successfully unnest a token', async function() {
- await requirePallets(this, [Pallets.ReFungible]);
+ itSub.ifWithPallets('ReFungible: allows the owner to successfully unnest a token', [Pallets.ReFungible], async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};
-
- const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
- const nestedToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');
-
- // Nest and unnest
- await transferExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, 1, 'ReFungible');
- await transferFromExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, alice, 1, 'ReFungible');
+ const collectionRFT = await helper.rft.mintCollection(alice);
+
+ // Nest and unnest
+ const token = await collectionRFT.mintToken(alice, 10n, targetToken.nestingAddress());
+ await expect(token.transferFrom(alice, targetToken.nestingAddress(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled;
+ expect(await token.getBalance({Substrate: alice.address})).to.be.equal(9n);
+ expect(await token.getBalance(targetToken.nestingAddress())).to.be.equal(1n);
- // Nest and burn
- await transferExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, 1, 'ReFungible');
- await expect(executeTransaction(
- api,
- alice,
- api.tx.unique.burnFrom(collectionRFT, normalizeAccountId(targetAddress), nestedToken, 1),
- ), 'while burning').to.not.be.rejected;
- const balance = await getBalance(api, collectionRFT, normalizeAccountId(targetAddress), nestedToken);
- expect(balance).to.be.equal(0n);
- });
+ // Nest and burn
+ await token.transfer(alice, targetToken.nestingAddress(), 5n);
+ await expect(token.burnFrom(alice, targetToken.nestingAddress(), 6n), 'while burning').to.be.fulfilled;
+ expect(await token.getBalance({Substrate: alice.address})).to.be.equal(4n);
+ expect(await token.getBalance(targetToken.nestingAddress())).to.be.equal(0n);
+ expect(await targetToken.getChildren()).to.be.length(0);
});
});
describe('Negative Test: Unnesting', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
before(async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- alice = privateKeyWrapper('//Alice');
- bob = privateKeyWrapper('//Bob');
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = privateKey('//Alice');
+ [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
});
});
- it('Disallows a non-owner to unnest/burn a token', async () => {
- await usingApi(async api => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
- const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};
+ itSub('Disallows a non-owner to unnest/burn a token', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
- // Create a nested token
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);
+ // Create a nested token
+ const nestedToken = await collection.mintToken(alice, targetToken.nestingAddress());
- // Try to unnest
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(bob), collection, nestedToken, 1),
- ), 'while unnesting').to.be.rejectedWith(/^common\.ApprovedValueTooLow$/);
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
+ // Try to unnest
+ await expect(nestedToken.unnest(bob, targetToken, {Substrate: alice.address})).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
- // Try to burn
- await expect(executeTransaction(
- api,
- bob,
- api.tx.unique.burnFrom(collection, normalizeAccountId(bob.address), nestedToken, 1),
- ), 'while burning').to.not.be.rejectedWith(/^common\.ApprovedValueTooLow$/);
- expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});
- });
+ // Try to burn
+ await expect(nestedToken.burnFrom(bob, targetToken.nestingAddress())).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+ expect(await nestedToken.getOwner()).to.be.deep.equal(targetToken.nestingAddress());
});
// todo another test for creating excessive depth matryoshka with Ethereum?
// Recursive nesting
- it('Prevents Ouroboros creation', async () => {
- const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});
- const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');
+ itSub('Prevents Ouroboros creation', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+ const targetToken = await collection.mintToken(alice);
- // Create a nested token ouroboros
- const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});
- await expect(transferExpectSuccess(collection, targetToken, alice, {Ethereum: tokenIdToAddress(collection, nestedToken)})).to.be.rejectedWith(/^structure\.OuroborosDetected$/);
+ // Fail to create a nested token ouroboros
+ const nestedToken = await collection.mintToken(alice, targetToken.nestingAddress());
+ await expect(targetToken.nest(alice, nestedToken)).to.be.rejectedWith(/^structure\.OuroborosDetected$/);
});
});
tests/src/util/playgrounds/types.tsdiffbeforeafterboth949495export interface IProperty {95export interface IProperty {96 key: string;96 key: string;97 value: string;97 value?: string;98}98}9999100export interface ITokenPropertyPermission {100export interface ITokenPropertyPermission {101 key: string;101 key: string;102 permission: {102 permission: {103 mutable: boolean;103 mutable?: boolean;104 tokenOwner: boolean;104 tokenOwner?: boolean;105 collectionAdmin: boolean;105 collectionAdmin?: boolean;106 }106 }107}107}108108109export interface IToken {109export interface IToken {110 collectionId: number;110 collectionId: number;111 tokenId: number;111 tokenId: number;112 //nestingAddress: () => {Ethereum: string};112}113}113114114export interface IBlock {115export interface IBlock {tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -55,8 +55,12 @@
RPC: 'rpc',
};
- static getNestingTokenAddress(collectionId: number, tokenId: number) {
- return nesting.tokenIdToAddress(collectionId, tokenId);
+ static getNestingTokenAddress(token: IToken) {
+ return {Ethereum: this.getNestingTokenAddressRaw(token).toLowerCase()};
+ }
+
+ static getNestingTokenAddressRaw(token: IToken) {
+ return nesting.tokenIdToAddress(token.collectionId, token.tokenId);
}
static getDefaultLogger(): ILogger {
@@ -897,6 +901,18 @@
}
/**
+ * Get collection properties.
+ *
+ * @param collectionId ID of collection
+ * @param propertyKeys optionally filter the returned properties to only these keys
+ * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);
+ * @returns array of key-value pairs
+ */
+ async getProperties(collectionId: number, propertyKeys: string[] | null = null): Promise<IProperty[]> {
+ return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();
+ }
+
+ /**
* Deletes onchain properties from the collection.
*
* @param signer keyring of signer
@@ -988,13 +1004,13 @@
*
* @param signer keyring of signer
* @param collectionId ID of collection
- * @param fromAddressObj address on behalf of which the token will be burnt
* @param tokenId ID of token
+ * @param fromAddressObj address on behalf of which the token will be burnt
* @param amount amount of tokens to be burned. For NFT must be set to 1n
* @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
- async burnTokenFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, tokenId: number, amount=1n): Promise<boolean> {
+ async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {
const burnResult = await this.helper.executeExtrinsic(
signer,
'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],
@@ -1117,7 +1133,7 @@
*
* @param signer keyring of signer
* @param collectionId ID of collection
- * @param permissions permissions to change a property by the collection owner or admin
+ * @param permissions permissions to change a property by the collection admin or token owner
* @example setTokenPropertyPermissions(
* aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]
* )
@@ -1134,6 +1150,18 @@
}
/**
+ * Get token property permissions.
+ *
+ * @param collectionId ID of collection
+ * @param propertyKeys optionally filter the returned property permissions to only these keys
+ * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);
+ * @returns array of key-permission pairs
+ */
+ async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {
+ return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();
+ }
+
+ /**
* Set token properties
*
* @param signer keyring of signer
@@ -1154,6 +1182,19 @@
}
/**
+ * Get properties, metadata assigned to a token.
+ *
+ * @param collectionId ID of collection
+ * @param tokenId ID of token
+ * @param propertyKeys optionally filter the returned properties to only these keys
+ * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);
+ * @returns array of key-value pairs
+ */
+ async getTokenProperties(collectionId: number, tokenId: number, propertyKeys: string[] | null = null): Promise<IProperty[]> {
+ return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();
+ }
+
+ /**
* Delete the provided properties of a token
* @param signer keyring of signer
* @param collectionId ID of collection
@@ -1339,7 +1380,7 @@
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {
- const rootTokenAddress = {Ethereum: this.helper.util.getNestingTokenAddress(rootTokenObj.collectionId, rootTokenObj.tokenId)};
+ const rootTokenAddress = this.helper.util.getNestingTokenAddress(rootTokenObj);
const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);
if(!result) {
throw Error('Unable to nest token!');
@@ -1357,7 +1398,7 @@
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {
- const rootTokenAddress = {Ethereum: this.helper.util.getNestingTokenAddress(rootTokenObj.collectionId, rootTokenObj.tokenId)};
+ const rootTokenAddress = this.helper.util.getNestingTokenAddress(rootTokenObj);
const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);
if(!result) {
throw Error('Unable to unnest token!');
@@ -1377,7 +1418,7 @@
* })
* @returns object of the created collection
*/
- async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions): Promise<UniqueNFTCollection> {
+ async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {
return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;
}
@@ -1563,7 +1604,7 @@
* })
* @returns object of the created collection
*/
- async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions): Promise<UniqueRFTCollection> {
+ async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {
return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;
}
@@ -1633,13 +1674,27 @@
* @param tokenId ID of token
* @param amount number of pieces to be burnt
* @example burnToken(aliceKeyring, 10, 5);
- * @returns ```true``` and burnt token number is extrinsic success. Otherwise ```false``` and ```null```
+ * @returns ```true``` and burnt token number, if extrinsic is successful. Otherwise ```false``` and ```null```
*/
async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<{ success: boolean; token: number | null; }> {
return await super.burnToken(signer, collectionId, tokenId, amount);
}
/**
+ * Destroys a concrete instance of RFT on behalf of the owner.
+ * @param signer keyring of signer
+ * @param collectionId ID of collection
+ * @param tokenId ID of token
+ * @param fromAddressObj address on behalf of which the token will be burnt
+ * @param amount number of pieces to be burnt
+ * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)
+ * @returns ```true``` if extrinsic success, otherwise ```false```
+ */
+ async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {
+ return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);
+ }
+
+ /**
* Set, change, or remove approved address to transfer the ownership of the RFT.
*
* @param signer keyring of signer
@@ -1711,7 +1766,7 @@
* }, 18)
* @returns newly created fungible collection
*/
- async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, decimalPoints = 0): Promise<UniqueFTCollection> {
+ async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {
collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object
if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');
collectionOptions.mode = {fungible: decimalPoints};
@@ -1840,7 +1895,7 @@
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {
- return await super.burnTokenFrom(signer, collectionId, fromAddressObj, 0, amount);
+ return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);
}
/**
@@ -2161,7 +2216,7 @@
}
-class UniqueCollectionBase {
+export class UniqueCollectionBase {
helper: UniqueHelper;
collectionId: number;
@@ -2194,6 +2249,10 @@
return await this.helper.collection.getEffectiveLimits(this.collectionId);
}
+ async getProperties(propertyKeys: string[] | null = null) {
+ return await this.helper.collection.getProperties(this.collectionId, propertyKeys);
+ }
+
async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {
return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);
}
@@ -2260,7 +2319,7 @@
}
-class UniqueNFTCollection extends UniqueCollectionBase {
+export class UniqueNFTCollection extends UniqueCollectionBase {
getTokenObject(tokenId: number) {
return new UniqueNFTToken(tokenId, this);
}
@@ -2285,6 +2344,14 @@
return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);
}
+ async getPropertyPermissions(propertyKeys: string[] | null = null) {
+ return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);
+ }
+
+ async getTokenProperties(tokenId: number, propertyKeys: string[] | null = null) {
+ return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);
+ }
+
async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {
return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);
}
@@ -2313,6 +2380,10 @@
return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);
}
+ async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {
+ return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);
+ }
+
async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {
return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);
}
@@ -2335,11 +2406,15 @@
}
-class UniqueRFTCollection extends UniqueCollectionBase {
+export class UniqueRFTCollection extends UniqueCollectionBase {
getTokenObject(tokenId: number) {
return new UniqueRFTToken(tokenId, this);
}
+ async getToken(tokenId: number, blockHashAt?: string) {
+ return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);
+ }
+
async getTokensByAddress(addressObj: ICrossAccountId) {
return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);
}
@@ -2356,6 +2431,14 @@
return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);
}
+ async getPropertyPermissions(propertyKeys: string[] | null = null) {
+ return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);
+ }
+
+ async getTokenProperties(tokenId: number, propertyKeys: string[] | null = null) {
+ return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);
+ }
+
async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {
return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);
}
@@ -2388,6 +2471,10 @@
return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);
}
+ async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n) {
+ return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);
+ }
+
async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {
return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);
}
@@ -2402,7 +2489,7 @@
}
-class UniqueFTCollection extends UniqueCollectionBase {
+export class UniqueFTCollection extends UniqueCollectionBase {
async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {
return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);
}
@@ -2449,7 +2536,7 @@
}
-class UniqueTokenBase implements IToken {
+export class UniqueTokenBase implements IToken {
collection: UniqueNFTCollection | UniqueRFTCollection;
collectionId: number;
tokenId: number;
@@ -2464,6 +2551,10 @@
return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);
}
+ async getProperties(propertyKeys: string[] | null = null) {
+ return await this.collection.getTokenProperties(this.tokenId, propertyKeys);
+ }
+
async setProperties(signer: TSigner, properties: IProperty[]) {
return await this.collection.setTokenProperties(signer, this.tokenId, properties);
}
@@ -2471,10 +2562,14 @@
async deleteProperties(signer: TSigner, propertyKeys: string[]) {
return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);
}
+
+ nestingAddress() {
+ return this.collection.helper.util.getNestingTokenAddress(this);
+ }
}
-class UniqueNFTToken extends UniqueTokenBase {
+export class UniqueNFTToken extends UniqueTokenBase {
collection: UniqueNFTCollection;
constructor(tokenId: number, collection: UniqueNFTCollection) {
@@ -2525,9 +2620,13 @@
async burn(signer: TSigner) {
return await this.collection.burnToken(signer, this.tokenId);
}
+
+ async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {
+ return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);
+ }
}
-class UniqueRFTToken extends UniqueTokenBase {
+export class UniqueRFTToken extends UniqueTokenBase {
collection: UniqueRFTCollection;
constructor(tokenId: number, collection: UniqueRFTCollection) {
@@ -2535,6 +2634,10 @@
this.collection = collection;
}
+ async getData(blockHashAt?: string) {
+ return await this.collection.getToken(this.tokenId, blockHashAt);
+ }
+
async getTop10Owners() {
return await this.collection.getTop10TokenOwners(this.tokenId);
}
@@ -2570,4 +2673,8 @@
async burn(signer: TSigner, amount=1n) {
return await this.collection.burnToken(signer, this.tokenId, amount);
}
+
+ async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {
+ return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);
+ }
}