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} from '../util/helpers';14import {IKeyringPair} from '@polkadot/types/types';1516let alice: IKeyringPair;17let bob: IKeyringPair;1819describe('Integration Test: Unnesting', () => {20 before(async () => {21 await usingApi(async (api, privateKeyWrapper) => {22 alice = privateKeyWrapper('//Alice');23 bob = privateKeyWrapper('//Bob');24 });25 });2627 it('NFT: allows the owner to successfully unnest a token', async () => {28 await usingApi(async api => {29 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});30 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});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 ), 'while unnesting').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(targetAddress), nestedToken, 1),51 ), 'while burning').to.not.be.rejected;52 await expect(getTokenOwner(api, collection, nestedToken)).to.be.rejected;53 });54 });5556 it('Fungible: allows the owner to successfully unnest a token', async () => {57 await usingApi(async api => {58 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});59 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});60 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');61 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};6263 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});64 const nestedToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');6566 67 await transferExpectSuccess(collectionFT, nestedToken, alice, targetAddress, 1, 'Fungible');68 await transferFromExpectSuccess(collectionFT, nestedToken, alice, targetAddress, alice, 1, 'Fungible');6970 71 await transferExpectSuccess(collectionFT, nestedToken, alice, targetAddress, 1, 'Fungible');72 const balanceBefore = await getBalance(api, collectionFT, normalizeAccountId(targetAddress), nestedToken);73 await expect(executeTransaction(74 api,75 alice,76 api.tx.unique.burnFrom(collectionFT, normalizeAccountId(targetAddress), nestedToken, 1),77 ), 'while burning').to.not.be.rejected;78 const balanceAfter = await getBalance(api, collectionFT, normalizeAccountId(targetAddress), nestedToken);79 expect(balanceAfter + BigInt(1)).to.be.equal(balanceBefore);80 });81 });8283 it('ReFungible: allows the owner to successfully unnest a token', async () => {84 await usingApi(async api => {85 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});86 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});87 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');88 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};8990 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});91 const nestedToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');9293 94 await transferExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, 1, 'ReFungible');95 await transferFromExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, alice, 1, 'ReFungible');9697 98 await transferExpectSuccess(collectionRFT, nestedToken, alice, targetAddress, 1, 'ReFungible');99 await expect(executeTransaction(100 api,101 alice,102 api.tx.unique.burnFrom(collectionRFT, normalizeAccountId(targetAddress), nestedToken, 1),103 ), 'while burning').to.not.be.rejected;104 const balance = await getBalance(api, collectionRFT, normalizeAccountId(targetAddress), nestedToken);105 expect(balance).to.be.equal(0n);106 });107 });108});109110describe('Negative Test: Unnesting', () => {111 before(async () => {112 await usingApi(async (api, privateKeyWrapper) => {113 alice = privateKeyWrapper('//Alice');114 bob = privateKeyWrapper('//Bob');115 });116 });117118 it('Disallows a non-owner to unnest/burn a token', async () => {119 await usingApi(async api => {120 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});121 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});122 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');123 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken)};124125 126 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);127128 129 await expect(executeTransaction(130 api,131 bob,132 api.tx.unique.transferFrom(normalizeAccountId(targetAddress), normalizeAccountId(bob), collection, nestedToken, 1),133 ), 'while unnesting').to.be.rejectedWith(/^common\.ApprovedValueTooLow$/);134 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});135136 137 await expect(executeTransaction(138 api,139 bob,140 api.tx.unique.burnFrom(collection, normalizeAccountId(bob.address), nestedToken, 1),141 ), 'while burning').to.not.be.rejectedWith(/^common\.ApprovedValueTooLow$/);142 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});143 });144 });145146 147148 149 it('Prevents Ouroboros creation', async () => {150 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});151 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});152 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');153154 155 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});156 await expect(transferExpectSuccess(collection, targetToken, alice, {Ethereum: tokenIdToAddress(collection, nestedToken)})).to.be.rejectedWith(/^structure\.OuroborosDetected$/);157 });158});