1import {expect} from 'chai';2import {tokenIdToAddress} from '../eth/util/helpers';3import usingApi, {executeTransaction} from '../substrate/substrate-api';4import {5 createCollectionExpectSuccess,6 createItemExpectSuccess,7 getBalance,8 getTokenOwner,9 normalizeAccountId,10 setCollectionPermissionsExpectSuccess,11 transferExpectSuccess,12 transferFromExpectSuccess,13 requirePallets,14 Pallets,15} from '../util/helpers';16import {IKeyringPair} from '@polkadot/types/types';1718let alice: IKeyringPair;19let bob: IKeyringPair;2021describe('Integration Test: Unnesting', () => {22 before(async () => {23 await usingApi(async (api, privateKeyWrapper) => {24 alice = privateKeyWrapper('//Alice');25 bob = privateKeyWrapper('//Bob');26 });27 });2829 it('NFT: allows the owner to successfully unnest a token', async () => {30 await usingApi(async api => {31 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});32 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});33 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');34 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};3536 37 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);3839 40 await expect(executeTransaction(41 api,42 alice,43 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(alice), collection, nestedToken, 1),44 ), 'while unnesting').to.not.be.rejected;45 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});4647 48 await transferExpectSuccess(collection, nestedToken, alice, targetAddress);49 await expect(executeTransaction(50 api,51 alice,52 api.tx.unique.burnFrom(collection, normalizeAccountId(targetAddress), nestedToken, 1),53 ), 'while burning').to.not.be.rejected;54 await expect(getTokenOwner(api, collection, nestedToken)).to.be.rejected;55 });56 });5758 it('Fungible: allows the owner to successfully unnest a token', async () => {59 await usingApi(async api => {60 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});61 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});62 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');63 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};6465 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});66 const nestedToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');6768 69 await transferExpectSuccess(collectionFT, nestedToken, alice, targetAddress, 1, 'Fungible');70 await transferFromExpectSuccess(collectionFT, nestedToken, alice, targetAddress, alice, 1, 'Fungible');7172 73 await transferExpectSuccess(collectionFT, nestedToken, alice, targetAddress, 1, 'Fungible');74 const balanceBefore = await getBalance(api, collectionFT, normalizeAccountId(targetAddress), nestedToken);75 await expect(executeTransaction(76 api,77 alice,78 api.tx.unique.burnFrom(collectionFT, normalizeAccountId(targetAddress), nestedToken, 1),79 ), 'while burning').to.not.be.rejected;80 const balanceAfter = await getBalance(api, collectionFT, normalizeAccountId(targetAddress), nestedToken);81 expect(balanceAfter + BigInt(1)).to.be.equal(balanceBefore);82 });83 });8485 it('ReFungible: allows the owner to successfully unnest a token', async function() {86 await requirePallets(this, [Pallets.ReFungible]);8788 await usingApi(async api => {89 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});90 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});91 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');92 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};9394 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});95 const nestedToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');9697 98 await transferExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, 1, 'ReFungible');99 await transferFromExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, alice, 1, 'ReFungible');100101 102 await transferExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, 1, 'ReFungible');103 await expect(executeTransaction(104 api,105 alice,106 api.tx.unique.burnFrom(collectionRFT, normalizeAccountId(targetAddress), nestedToken, 1),107 ), 'while burning').to.not.be.rejected;108 const balance = await getBalance(api, collectionRFT, normalizeAccountId(targetAddress), nestedToken);109 expect(balance).to.be.equal(0n);110 });111 });112});113114describe('Negative Test: Unnesting', () => {115 before(async () => {116 await usingApi(async (api, privateKeyWrapper) => {117 alice = privateKeyWrapper('//Alice');118 bob = privateKeyWrapper('//Bob');119 });120 });121122 it('Disallows a non-owner to unnest/burn a token', async () => {123 await usingApi(async api => {124 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});125 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});126 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');127 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};128129 130 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);131132 133 await expect(executeTransaction(134 api,135 bob,136 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(bob), collection, nestedToken, 1),137 ), 'while unnesting').to.be.rejectedWith(/^common\.ApprovedValueTooLow$/);138 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});139140 141 await expect(executeTransaction(142 api,143 bob,144 api.tx.unique.burnFrom(collection, normalizeAccountId(bob.address), nestedToken, 1),145 ), 'while burning').to.not.be.rejectedWith(/^common\.ApprovedValueTooLow$/);146 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});147 });148 });149150 151152 153 it('Prevents Ouroboros creation', async () => {154 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});155 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});156 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');157158 159 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});160 await expect(transferExpectSuccess(collection, targetToken, alice, {Ethereum: tokenIdToAddress(collection, nestedToken)})).to.be.rejectedWith(/^structure\.OuroborosDetected$/);161 });162});