1import {expect} from 'chai';2import {tokenIdToAddress} from '../eth/util/helpers';3import privateKey from '../substrate/privateKey';4import usingApi, {executeTransaction} from '../substrate/substrate-api';5import {6 createCollectionExpectSuccess,7 createItemExpectSuccess,8 getBalance,9 getTokenOwner,10 normalizeAccountId,11 setCollectionLimitsExpectSuccess,12 transferExpectSuccess,13 transferFromExpectSuccess,14} from '../util/helpers';15import {IKeyringPair} from '@polkadot/types/types';1617let alice: IKeyringPair;18let bob: IKeyringPair;1920describe('Integration Test: Unnesting', () => {21 before(async () => {22 await usingApi(async () => {23 alice = privateKey('//Alice');24 bob = privateKey('//Bob');25 });26 });2728 it('NFT: allows the owner to successfully unnest a token', async () => {29 await usingApi(async api => {30 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});31 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});32 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');33 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};3435 36 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);3738 39 await expect(executeTransaction(40 api,41 alice,42 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(alice), collection, nestedToken, 1),43 ), 'while unnesting').to.not.be.rejected;44 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});4546 47 await transferExpectSuccess(collection, nestedToken, alice, targetAddress);48 await expect(executeTransaction(49 api,50 alice,51 api.tx.unique.burnFrom(collection, normalizeAccountId(targetAddress), nestedToken, 1),52 ), 'while burning').to.not.be.rejected;53 await expect(getTokenOwner(api, collection, nestedToken)).to.be.rejected;54 });55 });5657 it('Fungible: allows the owner to successfully unnest a token', async () => {58 await usingApi(async api => {59 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});60 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});61 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');62 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};6364 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});65 const nestedToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');6667 68 await transferExpectSuccess(collectionFT, nestedToken, alice, targetAddress, 1, 'Fungible');69 await transferFromExpectSuccess(collectionFT, nestedToken, alice, targetAddress, alice, 1, 'Fungible');7071 72 await transferExpectSuccess(collectionFT, nestedToken, alice, targetAddress, 1, 'Fungible');73 const balanceBefore = await getBalance(api, collectionFT, normalizeAccountId(targetAddress), nestedToken);74 await expect(executeTransaction(75 api,76 alice,77 api.tx.unique.burnFrom(collectionFT, normalizeAccountId(targetAddress), nestedToken, 1),78 ), 'while burning').to.not.be.rejected;79 const balanceAfter = await getBalance(api, collectionFT, normalizeAccountId(targetAddress), nestedToken);80 expect(balanceAfter + BigInt(1)).to.be.equal(balanceBefore);81 });82 });8384 it('ReFungible: allows the owner to successfully unnest a token', async () => {85 await usingApi(async api => {86 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});87 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});88 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');89 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};9091 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});92 const nestedToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');9394 95 await transferExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, 1, 'ReFungible');96 await transferFromExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, alice, 1, 'ReFungible');9798 99 await transferExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, 1, 'ReFungible');100 await expect(executeTransaction(101 api,102 alice,103 api.tx.unique.burnFrom(collectionRFT, normalizeAccountId(targetAddress), nestedToken, 1),104 ), 'while burning').to.not.be.rejected;105 const balance = await getBalance(api, collectionRFT, normalizeAccountId(targetAddress), nestedToken);106 expect(balance).to.be.equal(0n);107 });108 });109});110111describe('Negative Test: Unnesting', () => {112 before(async () => {113 await usingApi(async () => {114 alice = privateKey('//Alice');115 bob = privateKey('//Bob');116 });117 });118119 it('Disallows a non-owner to unnest/burn a token', async () => {120 await usingApi(async api => {121 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});122 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});123 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');124 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};125126 127 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);128129 130 await expect(executeTransaction(131 api,132 bob,133 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(bob), collection, nestedToken, 1),134 ), 'while unnesting').to.be.rejectedWith(/^common\.ApprovedValueTooLow$/);135 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});136137 138 await expect(executeTransaction(139 api,140 bob,141 api.tx.unique.burnFrom(collection, normalizeAccountId(bob.address), nestedToken, 1),142 ), 'while burning').to.not.be.rejectedWith(/^common\.ApprovedValueTooLow$/);143 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});144 });145 });146147 148149 150 it('Prevents Ouroboros creation', async () => {151 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});152 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});153 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');154155 156 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});157 expect(transferExpectSuccess(collection, targetToken, alice, {Ethereum: tokenIdToAddress(collection, nestedToken)})).to.be.rejectedWith(/^structure\.OuroborosDetected$/);158 });159});