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 createItemExpectFailure, 8 createItemExpectSuccess,9 getTokenOwner, 10 getTopmostTokenOwner, 11 normalizeAccountId, 12 setCollectionLimitsExpectSuccess, 13 transferExpectFailure, 14 transferExpectSuccess, 15} from '../util/helpers';16import {IKeyringPair} from '@polkadot/types/types';1718let alice: IKeyringPair;19let bob: IKeyringPair;2021describe('Integration Test: Unnesting', () => {22 before(async () => {23 alice = privateKey('//Alice');24 bob = privateKey('//Bob');25 });2627 it('Allows the owner to successfully unnest a token', async () => {28 await usingApi(async api => {29 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});30 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});31 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');32 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};3334 35 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);3637 38 await expect(executeTransaction(39 api, 40 alice, 41 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(alice), collection, nestedToken, 1),42 )).to.not.be.rejected;43 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});4445 46 await transferExpectSuccess(collection, nestedToken, alice, targetAddress);47 await expect(executeTransaction(48 api, 49 alice, 50 api.tx.unique.burnFrom(collection, normalizeAccountId(alice.address), nestedToken, 1),51 )).to.not.be.rejected;52 await expect(getTokenOwner(api, collection, nestedToken)).to.be.rejected; 53 });54 });5556 57});5859describe('Negative Test: Unnesting', () => {60 before(async () => {61 alice = privateKey('//Alice');62 bob = privateKey('//Bob');63 });64 65 it('Disallows a non-owner to unnest/burn a token', async () => {66 await usingApi(async api => {67 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});68 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});69 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');70 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};7172 73 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);7475 76 await expect(executeTransaction(77 api, 78 bob, 79 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(bob), collection, nestedToken, 1),80 )).to.be.rejectedWith(/^common\.ApprovedValueTooLow$/);81 82 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});8384 85 await expect(executeTransaction(86 api, 87 bob, 88 api.tx.unique.burnFrom(collection, normalizeAccountId(bob.address), nestedToken, 1),89 )).to.not.be.rejectedWith(/^common\.ApprovedValueTooLow$/);90 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});91 });92 });93 94 it('Disallows excessive token nesting', async () => {95 await usingApi(async api => {96 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});97 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});98 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');99100 101 const nestedToken1 = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});102 const nestedToken2 = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, nestedToken1)});103 104 await createItemExpectFailure(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, nestedToken2)});105106 expect(await getTopmostTokenOwner(api, collection, nestedToken2)).to.be.deep.equal({Substrate: alice.address});107 });108 });109110 111 112 113 it('Prevents Ouroboros-nested operations', async () => { 114 await usingApi(async api => {115 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});116 await setCollectionLimitsExpectSuccess(alice, collection, {nestingRule: 'Owner'});117 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');118119 120 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});121 await transferExpectSuccess(collection, targetToken, alice, {Ethereum: tokenIdToAddress(collection, nestedToken)});122123 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});124125 126 await expect(getTopmostTokenOwner(api, collection, nestedToken)).to.be.rejected; 127 128 });129 });130});