From a93496d968785d4dd88032c37f70fbb66416725c Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Fri, 20 Jan 2023 08:06:47 +0000 Subject: [PATCH] Tests refactoring WIP --- --- a/tests/src/nesting/nest.test.ts +++ b/tests/src/nesting/nest.test.ts @@ -28,6 +28,7 @@ }); }); + /// TODO move 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); @@ -56,77 +57,6 @@ 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}); - }); - - 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); - - // Create a nested token - const tokenC = await collection.mintToken(alice, tokenA.nestingAccount()); - expect(await tokenC.getOwner()).to.be.deep.equal(tokenA.nestingAccount().toLowerCase()); - - // Transfer the nested token to another token - await expect(tokenC.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount())).to.be.fulfilled; - expect(await tokenC.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); - expect(await tokenC.getOwner()).to.be.deep.equal(tokenB.nestingAccount().toLowerCase()); - }); - - 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'); - - // Create a nested NFT token - const tokenA = await collectionA.mintToken(alice, targetToken.nestingAccount()); - 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 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'); - - // 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)'); - - // Create a fungible token in another collection and then nest - await collectionB.mint(alice, 10n); - await collectionB.transfer(alice, targetToken.nestingAccount(), 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.nestingAccount(), tokenA.nestingAccount(), 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)'); - - // Move the remaining part of the fungible token inside token A deeper in the nesting tree - await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 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)'); }); }); --- a/tests/src/sub/nesting/common.test.ts +++ b/tests/src/sub/nesting/common.test.ts @@ -17,191 +17,90 @@ import {IKeyringPair} from '@polkadot/types/types'; import {expect, itSub, Pallets, usingPlaygrounds} from '../../util'; import {UniqueNFTCollection, UniqueRFTCollection} from '../../util/playgrounds/unique'; -import {itEth} from '../../eth/util'; -describe('Nesting', () => { - let alice: IKeyringPair; +let alice: IKeyringPair; - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({filename: __filename}); - [alice] = await helper.arrange.createAccounts([50n], donor); - }); +before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({filename: __filename}); + [alice] = await helper.arrange.createAccounts([100n], donor); }); +}); - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => { - itSub.ifWithPallets(`Owner can nest ${testCase.mode.toUpperCase()}`, testCase.requiredPallets, async ({helper}) => { - // Only NFT allows nesting, permissions should be set: - const aliceNFTCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - const targetToken = await aliceNFTCollection.mintToken(alice); - - const collectionForNesting = await helper[testCase.mode].mintCollection(alice); - - // 1. Alice can immediately create nested token: - const nestedToken1 = testCase.mode === 'nft' - ? await (collectionForNesting as UniqueNFTCollection).mintToken(alice, targetToken.nestingAccount()) - : await (collectionForNesting as UniqueRFTCollection).mintToken(alice, 10n, targetToken.nestingAccount()); - expect(await nestedToken1.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); - expect(await nestedToken1.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase()); - - // 2. Alice can mint and nest token: - const nestedToken2 = await collectionForNesting.mintToken(alice); - await nestedToken2.nest(alice, targetToken); - expect(await nestedToken2.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); - expect(await nestedToken2.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase()); - }); - }); - - itSub('Owner can nest FT', async ({helper}) => { +[ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, +].map(testCase => { + itSub.ifWithPallets(`Owner can nest ${testCase.mode.toUpperCase()} in NFT`, testCase.requiredPallets, async ({helper}) => { // Only NFT allows nesting, permissions should be set: const aliceNFTCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); const targetToken = await aliceNFTCollection.mintToken(alice); - const collectionForNesting = await helper.ft.mintCollection(alice); + const collectionForNesting = await helper[testCase.mode].mintCollection(alice); - // 1. Alice can immediately create nested tokens: - await collectionForNesting.mint(alice, 100n, targetToken.nestingAccount()); - expect(await collectionForNesting.getTop10Owners()).deep.eq([targetToken.nestingAccount().toLowerCase()]); - expect(await collectionForNesting.getBalance(targetToken.nestingAccount())).eq(100n); + // 1. Alice can immediately create nested token: + const nestedToken1 = testCase.mode === 'nft' + ? await (collectionForNesting as UniqueNFTCollection).mintToken(alice, targetToken.nestingAccount()) + : await (collectionForNesting as UniqueRFTCollection).mintToken(alice, 10n, targetToken.nestingAccount()); + expect(await nestedToken1.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); + expect(await nestedToken1.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase()); // 2. Alice can mint and nest token: - await collectionForNesting.mint(alice, 100n); - await collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n); - expect(await collectionForNesting.getBalance(targetToken.nestingAccount())).eq(150n); + const nestedToken2 = await collectionForNesting.mintToken(alice); + await nestedToken2.nest(alice, targetToken); + expect(await nestedToken2.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); + expect(await nestedToken2.getOwner()).to.be.deep.equal(targetToken.nestingAccount().toLowerCase()); }); }); - -describe('Nesting negative', () => { - let alice: IKeyringPair; - - before(async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const donor = await privateKey({filename: __filename}); - [alice] = await helper.arrange.createAccounts([50n], donor); - }); - }); - [ - {mode: 'nft' as const, requiredPallets: []}, - {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, - ].map(testCase => { - itSub.ifWithPallets(`Owner cannot nest ${testCase.mode.toUpperCase()} if nesting not allowed`, testCase.requiredPallets, async ({helper}) => { - // Create default collection, permissions are not set: - const aliceNFTCollection = await helper.nft.mintCollection(alice); - const targetToken = await aliceNFTCollection.mintToken(alice); - const collectionForNesting = await helper[testCase.mode].mintCollection(alice); - - // 1. Alice cannot create immediately nested tokens: - const nestingTx = testCase.mode === 'nft' - ? (collectionForNesting as UniqueNFTCollection).mintToken(alice, targetToken.nestingAccount()) - : (collectionForNesting as UniqueRFTCollection).mintToken(alice, 10n, targetToken.nestingAccount()); - await expect(nestingTx).to.be.rejectedWith('common.UserIsNotAllowedToNest'); - - // 2. Alice cannot mint and nest token: - const nestedToken2 = await collectionForNesting.mintToken(alice); - await expect(nestedToken2.nest(alice, targetToken)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); - }); - }); - - itSub('Owner cannot nest FT if nesting not allowed', async ({helper}) => { - // Create default collection, permissions are not set: - const aliceNFTCollection = await helper.nft.mintCollection(alice); - const targetToken = await aliceNFTCollection.mintToken(alice); - - const collectionForNesting = await helper.ft.mintCollection(alice); - - // 1. Alice cannot create immediately nested tokens: - await expect(collectionForNesting.mint(alice, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); - - // 2. Alice can mint and nest token: - await collectionForNesting.mint(alice, 100n); - await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); - }); +itSub('Owner can nest FT in NFT', async ({helper}) => { + // Only NFT allows nesting, permissions should be set: + const aliceNFTCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const targetToken = await aliceNFTCollection.mintToken(alice); - itSub.ifWithPallets('Cannot nest to a future collection', [Pallets.ReFungible], async ({helper}) => { - const nonExistingCollectionId = await helper.collection.getTotalCount() + 1000; - const futureToken = helper.nft.getTokenObject(nonExistingCollectionId, 1); + const collectionForNesting = await helper.ft.mintCollection(alice); - const nftCollectionForNesting = await helper.nft.mintCollection(alice); - const rftCollectionForNesting = await helper.rft.mintCollection(alice); - const ftCollectionForNesting = await helper.ft.mintCollection(alice); + // 1. Alice can immediately create nested tokens: + await collectionForNesting.mint(alice, 100n, targetToken.nestingAccount()); + expect(await collectionForNesting.getTop10Owners()).deep.eq([targetToken.nestingAccount().toLowerCase()]); + expect(await collectionForNesting.getBalance(targetToken.nestingAccount())).eq(100n); - // 1. Alice cannot create nested token to future token - await expect(nftCollectionForNesting.mintToken(alice, futureToken.nestingAccount())).to.be.rejectedWith('CollectionNotFound'); - await expect(rftCollectionForNesting.mintToken(alice, 10n, futureToken.nestingAccount())).to.be.rejectedWith('CollectionNotFound'); - await expect(ftCollectionForNesting.mint(alice, 10n, futureToken.nestingAccount())).to.be.rejectedWith('CollectionNotFound'); + // 2. Alice can mint and nest token: + await collectionForNesting.mint(alice, 100n); + await collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n); + expect(await collectionForNesting.getBalance(targetToken.nestingAccount())).eq(150n); +}); - // 2. Alice cannot mint and nest token: - const nft = await nftCollectionForNesting.mintToken(alice); - const rft = await rftCollectionForNesting.mintToken(alice, 100n); - const _ft = await ftCollectionForNesting.mint(alice, 100n); - await expect(nft.transfer(alice, futureToken.nestingAccount())).to.be.rejectedWith('CollectionNotFound'); - await expect(rft.transfer(alice, futureToken.nestingAccount())).to.be.rejectedWith('CollectionNotFound'); - await expect(ftCollectionForNesting.transfer(alice, futureToken.nestingAccount(), 50n)).to.be.rejectedWith('CollectionNotFound'); - }); - itSub.ifWithPallets('Cannot nest to a future token in a NFT collection', [Pallets.ReFungible], async ({helper}) => { - const {collectionId} = await helper.nft.mintCollection(alice); - // To avoid UserIsNotAllowedToNest error - await helper.collection.setPermissions(alice, collectionId, {nesting: {collectionAdmin: true}}); - const futureToken = helper.nft.getTokenObject(collectionId, 1); +itSub.ifWithPallets('Owner can transferFrom nested tokens', [Pallets.ReFungible], async ({helper}) => { + const collectionToNest = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const tokenA = await collectionToNest.mintToken(alice); + const tokenB = await collectionToNest.mintToken(alice); - const nftCollectionForNesting = await helper.nft.mintCollection(alice); - const rftCollectionForNesting = await helper.rft.mintCollection(alice); - const ftCollectionForNesting = await helper.ft.mintCollection(alice); + // Create a nested token + const nftCollectionToBeNested = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const rftCollectionToBeNested = await helper.rft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const ftCollectionToBeNested = await helper.ft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); - // 1. Alice cannot create nested token to future token - await expect(nftCollectionForNesting.mintToken(alice, futureToken.nestingAccount())).to.be.rejectedWith('TokenNotFound'); - await expect(rftCollectionForNesting.mintToken(alice, 10n, futureToken.nestingAccount())).to.be.rejectedWith('TokenNotFound'); - await expect(ftCollectionForNesting.mint(alice, 10n, futureToken.nestingAccount())).to.be.rejectedWith('TokenNotFound'); - - // 2. Alice cannot mint and nest token: - const nft = await nftCollectionForNesting.mintToken(alice); - const rft = await rftCollectionForNesting.mintToken(alice, 100n); - const _ft = await ftCollectionForNesting.mint(alice, 100n); - await expect(nft.transfer(alice, futureToken.nestingAccount())).to.be.rejectedWith('TokenNotFound'); - await expect(rft.transfer(alice, futureToken.nestingAccount())).to.be.rejectedWith('TokenNotFound'); - await expect(ftCollectionForNesting.transfer(alice, futureToken.nestingAccount(), 50n)).to.be.rejectedWith('TokenNotFound'); - }); - - itEth.ifWithPallets('Cannot nest to collection address', [Pallets.ReFungible], async({helper}) => { - const existingCollection = await helper.nft.mintCollection(alice); - const existingCollectionAddress = helper.ethAddress.fromCollectionId(existingCollection.collectionId); - const futureCollectionAddress = helper.ethAddress.fromCollectionId(99999999); - - const nftCollectionForNesting = await helper.nft.mintCollection(alice); - - // 1. Alice cannot create nested token to collection address - await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); - await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); + const nestedNFT = await nftCollectionToBeNested.mintToken(alice, tokenA.nestingAccount()); + const nestedRFT = await rftCollectionToBeNested.mintToken(alice, 100n, tokenA.nestingAccount()); + const _nestedFT = await ftCollectionToBeNested.mint(alice, 100n, tokenA.nestingAccount()); + expect(await nestedNFT.getOwner()).to.be.deep.equal(tokenA.nestingAccount().toLowerCase()); + expect(await nestedRFT.getOwner()).to.be.deep.equal(tokenA.nestingAccount().toLowerCase()); + expect(await ftCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(100n); - // 2. Alice cannot mint and nest token to collection address: - const nft = await nftCollectionForNesting.mintToken(alice); - await expect(nft.transfer(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); - await expect(nft.transfer(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); - }); + // Transfer the nested token to another token + await nestedNFT.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount()); + await nestedRFT.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n); + await ftCollectionToBeNested.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n); - itEth.ifWithPallets('Cannot nest in RFT or FT', [Pallets.ReFungible], async ({helper}) => { - // Create default collection, permissions are not set: - const rftCollection = await helper.rft.mintCollection(alice); - const ftCollection = await helper.ft.mintCollection(alice); - - const rftToken = await rftCollection.mintToken(alice); - const _ftToken = await ftCollection.mint(alice, 100n); - - const collectionForNesting = await helper.nft.mintCollection(alice); + expect(await nestedNFT.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address}); + expect(await nestedNFT.getOwner()).to.be.deep.equal(tokenB.nestingAccount().toLowerCase()); - // 1. Alice cannot create immediately nested tokens: - await expect(collectionForNesting.mintToken(alice, rftToken.nestingAccount())).to.be.rejectedWith('refungible.RefungibleDisallowsNesting'); - await expect(collectionForNesting.mintToken(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting'); + expect(await nestedRFT.getBalance(tokenB.nestingAccount())).to.equal(25n); + expect(await nestedRFT.getBalance(tokenA.nestingAccount())).to.equal(75n); - // 2. Alice cannot mint and nest token: - const nestedToken2 = await collectionForNesting.mintToken(alice); - await expect(nestedToken2.nest(alice, rftToken)).to.be.rejectedWith('refungible.RefungibleDisallowsNesting'); - await expect(ftCollection.transfer(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting'); - }); + expect(await ftCollectionToBeNested.getBalance(tokenB.nestingAccount())).to.equal(25n); + expect(await ftCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(75n); }); --- /dev/null +++ b/tests/src/sub/nesting/e2e.test.ts @@ -0,0 +1,104 @@ +// 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 . + +import {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, usingPlaygrounds} from '../../util'; + +describe('Composite nesting tests', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({filename: __filename}); + [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); + }); + }); + + itSub('Checks token children e2e', async ({helper}) => { + const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}}); + const collectionB = await helper.ft.mintCollection(alice); + const collectionC = await helper.rft.mintCollection(alice); + + const targetToken = await collectionA.mintToken(alice); + expect((await targetToken.getChildren()).length).to.be.equal(0, 'Children length check at creation'); + + // Create a nested NFT token + const tokenA = await collectionA.mintToken(alice, targetToken.nestingAccount()); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + ]).and.has.length(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}, + ]).and.has.length(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.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + ]).and.has.length(1); + + // Create a fungible token in another collection and then nest + await collectionB.mint(alice, 10n); + await collectionB.transfer(alice, targetToken.nestingAccount(), 2n); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + {tokenId: 0, collectionId: collectionB.collectionId}, + ]).and.has.length(2); + + // Create a refungible token in another collection and then nest + const tokenC = await collectionC.mintToken(alice, 10n); + await tokenC.transfer(alice, targetToken.nestingAccount(), 2n); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + {tokenId: 0, collectionId: collectionB.collectionId}, + {tokenId: tokenC.tokenId, collectionId: collectionC.collectionId}, + ]).and.has.length(3); + + // Burn all nested pieces + await tokenC.burnFrom(alice, targetToken.nestingAccount(), 2n); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + {tokenId: 0, collectionId: collectionB.collectionId}, + ]) + .and.has.length(2); + + // Move part of the fungible token inside token A deeper in the nesting tree + await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n); + expect(await targetToken.getChildren()).to.be.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + {tokenId: 0, collectionId: collectionB.collectionId}, + ]).and.has.length(2); + // Nested token also has children now: + expect(await tokenA.getChildren()).to.have.deep.members([ + {tokenId: 0, collectionId: collectionB.collectionId}, + ]).and.has.length(1); + + // Move the remaining part of the fungible token inside token A deeper in the nesting tree + await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n); + expect(await targetToken.getChildren()).to.have.deep.members([ + {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId}, + ]).and.has.length(1); + expect(await tokenA.getChildren()).to.have.deep.members([ + {tokenId: 0, collectionId: collectionB.collectionId}, + ]).and.has.length(1); + }); +}); --- /dev/null +++ b/tests/src/sub/nesting/negative.test.ts @@ -0,0 +1,142 @@ +// 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 . + +import {IKeyringPair} from '@polkadot/types/types'; +import {expect, itSub, Pallets, usingPlaygrounds} from '../../util'; +import {UniqueNFTCollection, UniqueRFTCollection} from '../../util/playgrounds/unique'; +import {itEth} from '../../eth/util'; + +let alice: IKeyringPair; + +before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = await privateKey({filename: __filename}); + [alice] = await helper.arrange.createAccounts([50n], donor); + }); +}); + +[ + {mode: 'nft' as const, requiredPallets: []}, + {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, +].map(testCase => { + itSub.ifWithPallets(`Owner cannot nest ${testCase.mode.toUpperCase()} if nesting not allowed`, testCase.requiredPallets, async ({helper}) => { + // Create default collection, permissions are not set: + const aliceNFTCollection = await helper.nft.mintCollection(alice); + const targetToken = await aliceNFTCollection.mintToken(alice); + + const collectionForNesting = await helper[testCase.mode].mintCollection(alice); + + // 1. Alice cannot create immediately nested tokens: + const nestingTx = testCase.mode === 'nft' + ? (collectionForNesting as UniqueNFTCollection).mintToken(alice, targetToken.nestingAccount()) + : (collectionForNesting as UniqueRFTCollection).mintToken(alice, 10n, targetToken.nestingAccount()); + await expect(nestingTx).to.be.rejectedWith('common.UserIsNotAllowedToNest'); + + // 2. Alice cannot mint and nest token: + const nestedToken2 = await collectionForNesting.mintToken(alice); + await expect(nestedToken2.nest(alice, targetToken)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); + }); +}); + +itSub('Owner cannot nest FT if nesting not allowed', async ({helper}) => { + // Create default collection, permissions are not set: + const aliceNFTCollection = await helper.nft.mintCollection(alice); + const targetToken = await aliceNFTCollection.mintToken(alice); + + const collectionForNesting = await helper.ft.mintCollection(alice); + + // 1. Alice cannot create immediately nested tokens: + await expect(collectionForNesting.mint(alice, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); + + // 2. Alice can mint and nest token: + await collectionForNesting.mint(alice, 100n); + await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); +}); + +itSub.ifWithPallets('Cannot nest in non existing token', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.nft.mintCollection(alice); + // To avoid UserIsNotAllowedToNest error + await helper.collection.setPermissions(alice, collection.collectionId, {nesting: {collectionAdmin: true}}); + + // The list of non-existing tokens: + const tokenFromNonExistingCollection = helper.nft.getTokenObject(9999999, 1); + const tokenBurnt = await collection.mintToken(alice); + await tokenBurnt.burn(alice); + const tokenNotMintedYet = helper.nft.getTokenObject(collection.collectionId, 2); + + // The list of collections to nest tokens from: + const nftCollectionForNesting = await helper.nft.mintCollection(alice); + const rftCollectionForNesting = await helper.rft.mintCollection(alice); + const ftCollectionForNesting = await helper.ft.mintCollection(alice); + + const testCases = [ + {token: tokenFromNonExistingCollection, error: 'CollectionNotFound'}, + {token: tokenBurnt, error: 'TokenNotFound'}, + {token: tokenNotMintedYet, error: 'TokenNotFound'}, + ]; + + for(const testCase of testCases) { + // 1. Alice cannot create nested token to non-existing token + await expect(nftCollectionForNesting.mintToken(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); + await expect(rftCollectionForNesting.mintToken(alice, 10n, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); + await expect(ftCollectionForNesting.mint(alice, 10n, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); + + // 2. Alice cannot mint and nest token: + const nft = await nftCollectionForNesting.mintToken(alice); + const rft = await rftCollectionForNesting.mintToken(alice, 100n); + const _ft = await ftCollectionForNesting.mint(alice, 100n); + await expect(nft.transfer(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); + await expect(rft.transfer(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error); + await expect(ftCollectionForNesting.transfer(alice, testCase.token.nestingAccount(), 50n)).to.be.rejectedWith(testCase.error); + } +}); + +itEth.ifWithPallets('Cannot nest to collection address', [Pallets.ReFungible], async({helper}) => { + const existingCollection = await helper.nft.mintCollection(alice); + const existingCollectionAddress = helper.ethAddress.fromCollectionId(existingCollection.collectionId); + const futureCollectionAddress = helper.ethAddress.fromCollectionId(99999999); + + const nftCollectionForNesting = await helper.nft.mintCollection(alice); + + // 1. Alice cannot create nested token to collection address + await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); + await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); + + // 2. Alice cannot mint and nest token to collection address: + const nft = await nftCollectionForNesting.mintToken(alice); + await expect(nft.transfer(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); + await expect(nft.transfer(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection'); +}); + +itEth.ifWithPallets('Cannot nest in RFT or FT', [Pallets.ReFungible], async ({helper}) => { + // Create default collection, permissions are not set: + const rftCollection = await helper.rft.mintCollection(alice); + const ftCollection = await helper.ft.mintCollection(alice); + + const rftToken = await rftCollection.mintToken(alice); + const _ftToken = await ftCollection.mint(alice, 100n); + + const collectionForNesting = await helper.nft.mintCollection(alice); + + // 1. Alice cannot create immediately nested tokens: + await expect(collectionForNesting.mintToken(alice, rftToken.nestingAccount())).to.be.rejectedWith('refungible.RefungibleDisallowsNesting'); + await expect(collectionForNesting.mintToken(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting'); + + // 2. Alice cannot mint and nest token: + const nestedToken2 = await collectionForNesting.mintToken(alice); + await expect(nestedToken2.nest(alice, rftToken)).to.be.rejectedWith('refungible.RefungibleDisallowsNesting'); + await expect(ftCollection.transfer(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting'); +}); -- gitstuff