git.delta.rocks / unique-network / refs/commits / c4efebc46da8

difftreelog

test transfer already nested token + destroy a non-empty collection + additional refactoring

Fahrrader2022-05-30parent: #b46fb6f.patch.diff
in: master

7 files changed

modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -54,6 +54,7 @@
     "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts",
     "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts",
     "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
+    "testDestroyCollection": "mocha --timeout 9999999 -r ts-node/register ./**/destroyCollection.test.ts",
     "testToggleContractAllowList": "mocha --timeout 9999999 -r ts-node/register ./**/toggleContractAllowList.test.ts",
     "testAddToContractAllowList": "mocha --timeout 9999999 -r ts-node/register ./**/addToContractAllowList.test.ts",
     "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts",
modifiedtests/src/createMultipleItemsEx.test.tsdiffbeforeafterboth
before · tests/src/createMultipleItemsEx.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {expect} from 'chai';18import privateKey from './substrate/privateKey';19import usingApi, {executeTransaction} from './substrate/substrate-api';20import {createCollectionExpectSuccess, createCollectionWithPropsExpectSuccess, addCollectionAdminExpectSuccess} from './util/helpers';2122describe('createMultipleItemsEx', () => {23  it('can initialize multiple NFT with different owners', async () => {24    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});25    const alice = privateKey('//Alice');26    const bob = privateKey('//Bob');27    const charlie = privateKey('//Charlie');28    await usingApi(async (api) => {29      const data = [30        {31          owner: {substrate: alice.address},32          // constData: '0x0000',33        }, {34          owner: {substrate: bob.address},35          // constData: '0x2222',36        }, {37          owner: {substrate: charlie.address},38          // constData: '0x4444',39        },40      ];4142      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {43        NFT: data,44      }));45      const tokens = await api.query.nonfungible.tokenData.entries(collection);46      const json = tokens.map(([, token]) => token.toJSON());47      expect(json).to.be.deep.equal(data);48    });49  });5051  it('createMultipleItemsEx with property Admin', async () => {52    const collection = await createCollectionWithPropsExpectSuccess({mode: {type: 'NFT'}, propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: false}}]});53    const alice = privateKey('//Alice');54    const bob = privateKey('//Bob');55    const charlie = privateKey('//Charlie');56    await usingApi(async (api) => {57      const data = [58        {59          owner: {substrate: alice.address},60          // constData: '0x1111',61          properties: [{key: 'k', value: 'v1'}],62        }, {63          owner: {substrate: bob.address},64          // constData: '0x2222',65          properties: [{key: 'k', value: 'v2'}],66        }, {67          owner: {substrate: charlie.address},68          // constData: '0x4444',69          properties: [{key: 'k', value: 'v3'}],70        },71      ];7273      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {74        NFT: data,75      }));76      for (let i = 1; i < 4; i++) {77        expect(await api.rpc.unique.tokenProperties(collection, i)).not.to.be.empty;78      }79    });80  });8182  it('createMultipleItemsEx with property AdminConst', async () => {83    const collection = await createCollectionWithPropsExpectSuccess({mode: {type: 'NFT'}, propPerm: [{key: 'k', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}]});84    const alice = privateKey('//Alice');85    const bob = privateKey('//Bob');86    const charlie = privateKey('//Charlie');87    await usingApi(async (api) => {88      const data = [89        {90          owner: {substrate: alice.address},91          // constData: '0x0000',92          properties: [{key: 'k', value: 'v1'}],93        }, {94          owner: {substrate: bob.address},95          // constData: '0x2222',96          properties: [{key: 'k', value: 'v2'}],97        }, {98          owner: {substrate: charlie.address},99          // constData: '0x4444',100          properties: [{key: 'k', value: 'v3'}],101        },102      ];103104      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {105        NFT: data,106      }));107      for (let i = 1; i < 4; i++) {108        expect(await api.rpc.unique.tokenProperties(collection, i)).not.to.be.empty;109      }110    });111  });112113  it('createMultipleItemsEx with property itemOwnerOrAdmin', async () => {114    const collection = await createCollectionWithPropsExpectSuccess({mode: {type: 'NFT'}, propPerm: [{key: 'k', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}]});115    const alice = privateKey('//Alice');116    const bob = privateKey('//Bob');117    const charlie = privateKey('//Charlie');118    await usingApi(async (api) => {119      const data = [120        {121          owner: {substrate: alice.address},122          // constData: '0x0000',123          properties: [{key: 'k', value: 'v1'}],124        }, {125          owner: {substrate: bob.address},126          // constData: '0x2222',127          properties: [{key: 'k', value: 'v2'}],128        }, {129          owner: {substrate: charlie.address},130          // constData: '0x4444',131          properties: [{key: 'k', value: 'v3'}],132        },133      ];134135      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {136        NFT: data,137      }));138      for (let i = 1; i < 4; i++) {139        expect(await api.rpc.unique.tokenProperties(collection, i)).not.to.be.empty;140      }141    });142  });143144  it('No editing rights', async () => {145    const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],146      propPerm:   [{key: 'key1', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}]});147    const alice = privateKey('//Alice');148    const bob = privateKey('//Bob');149    const charlie = privateKey('//Charlie');150    await addCollectionAdminExpectSuccess(alice, collection, bob.address);151    await usingApi(async (api) => {152      const data = [153        {154          owner: {substrate: alice.address},155          properties: [{key: 'key1', value: 'v2'}],156        }, {157          owner: {substrate: bob.address},158          properties: [{key: 'key1', value: 'v2'}],159        }, {160          owner: {substrate: charlie.address},161          properties: [{key: 'key1', value: 'v2'}],162        },163      ];164165      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});166      // await executeTransaction(api, alice, tx);167168      //await submitTransactionExpectFailAsync(alice, tx);169      await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);170    });171  });172173  it('User doesnt have editing rights', async () => {174    const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],175      propPerm:   [{key: 'key1', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}]});176    const alice = privateKey('//Alice');177    const bob = privateKey('//Bob');178    await addCollectionAdminExpectSuccess(alice, collection, bob.address);179    await usingApi(async (api) => {180      const data = [181        {182          owner: {substrate: alice.address},183          properties: [{key: 'key1', value: 'v2'}],184        }, {185          owner: {substrate: alice.address},186          properties: [{key: 'key1', value: 'v2'}],187        }, {188          owner: {substrate: alice.address},189          properties: [{key: 'key1', value: 'v2'}],190        },191      ];192193      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});194      // await executeTransaction(api, alice, tx);195196      //await submitTransactionExpectFailAsync(alice, tx);197      await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);198    });199  });200201  it('Adding property without access rights', async () => {202    const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}]});203    const alice = privateKey('//Alice');204    const bob = privateKey('//Bob');205    const charlie = privateKey('//Charlie');206    await addCollectionAdminExpectSuccess(alice, collection, bob.address);207    await usingApi(async (api) => {208      const data = [209        {210          owner: {substrate: alice.address},211          properties: [{key: 'key1', value: 'v2'}],212        }, {213          owner: {substrate: bob.address},214          properties: [{key: 'key1', value: 'v2'}],215        }, {216          owner: {substrate: charlie.address},217          properties: [{key: 'key1', value: 'v2'}],218        },219      ];220221      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});222223      await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);224      //await submitTransactionExpectFailAsync(alice, tx);225    });226  });227228  it('Adding more than 64 properties', async () => {229    const propPerms = [{key: 'key', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}];230231    for (let i = 0; i < 65; i++) {232      propPerms.push({key: `key${i}`, permission: {mutable: true, collectionAdmin: true, tokenOwner: true}});233    }234235    const alice = privateKey('//Alice');236    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});237    await usingApi(async (api) => {238      await expect(executeTransaction(api, alice, api.tx.unique.setPropertyPermissions(collection, propPerms))).to.be.rejectedWith(/common\.PropertyLimitReached/);239    });240  });241242  it('Trying to add bigger property than allowed', async () => {243    const collection = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}]});244    const alice = privateKey('//Alice');245    const bob = privateKey('//Bob');246    const charlie = privateKey('//Charlie');247    await addCollectionAdminExpectSuccess(alice, collection, bob.address);248    await usingApi(async (api) => {249      const data = [250        {251          owner: {substrate: alice.address}, properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}],252        }, {253          owner: {substrate: bob.address}, properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}],254        }, {255          owner: {substrate: charlie.address}, properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}],256        },257      ];258259      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});260261      //await submitTransactionExpectFailAsync(alice, tx);262      await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);263    });264  });265266  it('can initialize multiple NFT with different owners', async () => {267    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});268    const alice = privateKey('//Alice');269    const bob = privateKey('//Bob');270    const charlie = privateKey('//Charlie');271    await usingApi(async (api) => {272      const data = [273        {274          owner: {substrate: alice.address},275          // constData: '0x0000',276        }, {277          owner: {substrate: bob.address},278          // constData: '0x2222',279        }, {280          owner: {substrate: charlie.address},281          // constData: '0x4444',282        },283      ];284285      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {286        NFT: data,287      }));288      const tokens = await api.query.nonfungible.tokenData.entries(collection);289      const json = tokens.map(([, token]) => token.toJSON());290      expect(json).to.be.deep.equal(data);291    });292  });293294  it('can initialize multiple NFT with different owners', async () => {295    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});296    const alice = privateKey('//Alice');297    const bob = privateKey('//Bob');298    const charlie = privateKey('//Charlie');299    await usingApi(async (api) => {300      const data = [301        {302          owner: {substrate: alice.address},303          // constData: '0x0000',304        }, {305          owner: {substrate: bob.address},306          // constData: '0x2222',307        }, {308          owner: {substrate: charlie.address},309          // constData: '0x4444',310        },311      ];312313      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {314        NFT: data,315      }));316      const tokens = await api.query.nonfungible.tokenData.entries(collection);317      const json = tokens.map(([, token]) => token.toJSON());318      expect(json).to.be.deep.equal(data);319    });320  });321322  it('fails when trying to set multiple owners when creating multiple refungibles', async () => {323    const collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});324    const alice = privateKey('//Alice');325    const bob = privateKey('//Bob');326327    await usingApi(async (api) => {328      // Polkadot requires map, and yet requires keys to be JSON encoded329      const users = new Map();330      users.set(JSON.stringify({substrate: alice.address}), 1);331      users.set(JSON.stringify({substrate: bob.address}), 1);332333      // TODO: better error message?334      await expect(executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {335        RefungibleMultipleItems: [336          {users},337          {users},338        ],339      }))).to.be.rejectedWith(/^refungible\.NotRefungibleDataUsedToMintFungibleCollectionToken$/);340    });341  });342});343'';
after · tests/src/createMultipleItemsEx.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {expect} from 'chai';18import privateKey from './substrate/privateKey';19import usingApi, {executeTransaction} from './substrate/substrate-api';20import {createCollectionExpectSuccess, createCollectionWithPropsExpectSuccess, addCollectionAdminExpectSuccess} from './util/helpers';2122describe('createMultipleItemsEx', () => {23  it('can initialize multiple NFT with different owners', async () => {24    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});25    const alice = privateKey('//Alice');26    const bob = privateKey('//Bob');27    const charlie = privateKey('//Charlie');28    await usingApi(async (api) => {29      const data = [30        {31          owner: {substrate: alice.address},32        }, {33          owner: {substrate: bob.address},34        }, {35          owner: {substrate: charlie.address},36        },37      ];3839      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {40        NFT: data,41      }));42      const tokens = await api.query.nonfungible.tokenData.entries(collection);43      const json = tokens.map(([, token]) => token.toJSON());44      expect(json).to.be.deep.equal(data);45    });46  });4748  it('createMultipleItemsEx with property Admin', async () => {49    const collection = await createCollectionWithPropsExpectSuccess({mode: {type: 'NFT'}, propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: false}}]});50    const alice = privateKey('//Alice');51    const bob = privateKey('//Bob');52    const charlie = privateKey('//Charlie');53    await usingApi(async (api) => {54      const data = [55        {56          owner: {substrate: alice.address},57          properties: [{key: 'k', value: 'v1'}],58        }, {59          owner: {substrate: bob.address},60          properties: [{key: 'k', value: 'v2'}],61        }, {62          owner: {substrate: charlie.address},63          properties: [{key: 'k', value: 'v3'}],64        },65      ];6667      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {68        NFT: data,69      }));70      for (let i = 1; i < 4; i++) {71        expect(await api.rpc.unique.tokenProperties(collection, i)).not.to.be.empty;72      }73    });74  });7576  it('createMultipleItemsEx with property AdminConst', async () => {77    const collection = await createCollectionWithPropsExpectSuccess({mode: {type: 'NFT'}, propPerm: [{key: 'k', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}]});78    const alice = privateKey('//Alice');79    const bob = privateKey('//Bob');80    const charlie = privateKey('//Charlie');81    await usingApi(async (api) => {82      const data = [83        {84          owner: {substrate: alice.address},85          properties: [{key: 'k', value: 'v1'}],86        }, {87          owner: {substrate: bob.address},88          properties: [{key: 'k', value: 'v2'}],89        }, {90          owner: {substrate: charlie.address},91          properties: [{key: 'k', value: 'v3'}],92        },93      ];9495      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {96        NFT: data,97      }));98      for (let i = 1; i < 4; i++) {99        expect(await api.rpc.unique.tokenProperties(collection, i)).not.to.be.empty;100      }101    });102  });103104  it('createMultipleItemsEx with property itemOwnerOrAdmin', async () => {105    const collection = await createCollectionWithPropsExpectSuccess({mode: {type: 'NFT'}, propPerm: [{key: 'k', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}]});106    const alice = privateKey('//Alice');107    const bob = privateKey('//Bob');108    const charlie = privateKey('//Charlie');109    await usingApi(async (api) => {110      const data = [111        {112          owner: {substrate: alice.address},113          properties: [{key: 'k', value: 'v1'}],114        }, {115          owner: {substrate: bob.address},116          properties: [{key: 'k', value: 'v2'}],117        }, {118          owner: {substrate: charlie.address},119          properties: [{key: 'k', value: 'v3'}],120        },121      ];122123      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {124        NFT: data,125      }));126      for (let i = 1; i < 4; i++) {127        expect(await api.rpc.unique.tokenProperties(collection, i)).not.to.be.empty;128      }129    });130  });131132  it('No editing rights', async () => {133    const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],134      propPerm:   [{key: 'key1', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}]});135    const alice = privateKey('//Alice');136    const bob = privateKey('//Bob');137    const charlie = privateKey('//Charlie');138    await addCollectionAdminExpectSuccess(alice, collection, bob.address);139    await usingApi(async (api) => {140      const data = [141        {142          owner: {substrate: alice.address},143          properties: [{key: 'key1', value: 'v2'}],144        }, {145          owner: {substrate: bob.address},146          properties: [{key: 'key1', value: 'v2'}],147        }, {148          owner: {substrate: charlie.address},149          properties: [{key: 'key1', value: 'v2'}],150        },151      ];152153      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});154      // await executeTransaction(api, alice, tx);155156      //await submitTransactionExpectFailAsync(alice, tx);157      await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);158    });159  });160161  it('User doesnt have editing rights', async () => {162    const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],163      propPerm:   [{key: 'key1', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}]});164    const alice = privateKey('//Alice');165    const bob = privateKey('//Bob');166    await addCollectionAdminExpectSuccess(alice, collection, bob.address);167    await usingApi(async (api) => {168      const data = [169        {170          owner: {substrate: alice.address},171          properties: [{key: 'key1', value: 'v2'}],172        }, {173          owner: {substrate: alice.address},174          properties: [{key: 'key1', value: 'v2'}],175        }, {176          owner: {substrate: alice.address},177          properties: [{key: 'key1', value: 'v2'}],178        },179      ];180181      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});182      // await executeTransaction(api, alice, tx);183184      //await submitTransactionExpectFailAsync(alice, tx);185      await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);186    });187  });188189  it('Adding property without access rights', async () => {190    const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}]});191    const alice = privateKey('//Alice');192    const bob = privateKey('//Bob');193    const charlie = privateKey('//Charlie');194    await addCollectionAdminExpectSuccess(alice, collection, bob.address);195    await usingApi(async (api) => {196      const data = [197        {198          owner: {substrate: alice.address},199          properties: [{key: 'key1', value: 'v2'}],200        }, {201          owner: {substrate: bob.address},202          properties: [{key: 'key1', value: 'v2'}],203        }, {204          owner: {substrate: charlie.address},205          properties: [{key: 'key1', value: 'v2'}],206        },207      ];208209      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});210211      await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);212      //await submitTransactionExpectFailAsync(alice, tx);213    });214  });215216  it('Adding more than 64 properties', async () => {217    const propPerms = [{key: 'key', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}];218219    for (let i = 0; i < 65; i++) {220      propPerms.push({key: `key${i}`, permission: {mutable: true, collectionAdmin: true, tokenOwner: true}});221    }222223    const alice = privateKey('//Alice');224    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});225    await usingApi(async (api) => {226      await expect(executeTransaction(api, alice, api.tx.unique.setPropertyPermissions(collection, propPerms))).to.be.rejectedWith(/common\.PropertyLimitReached/);227    });228  });229230  it('Trying to add bigger property than allowed', async () => {231    const collection = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}]});232    const alice = privateKey('//Alice');233    const bob = privateKey('//Bob');234    const charlie = privateKey('//Charlie');235    await addCollectionAdminExpectSuccess(alice, collection, bob.address);236    await usingApi(async (api) => {237      const data = [238        {239          owner: {substrate: alice.address}, properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}],240        }, {241          owner: {substrate: bob.address}, properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}],242        }, {243          owner: {substrate: charlie.address}, properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}],244        },245      ];246247      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});248249      //await submitTransactionExpectFailAsync(alice, tx);250      await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);251    });252  });253254  it('can initialize multiple NFT with different owners', async () => {255    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});256    const alice = privateKey('//Alice');257    const bob = privateKey('//Bob');258    const charlie = privateKey('//Charlie');259    await usingApi(async (api) => {260      const data = [261        {262          owner: {substrate: alice.address},263        }, {264          owner: {substrate: bob.address},265        }, {266          owner: {substrate: charlie.address},267        },268      ];269270      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {271        NFT: data,272      }));273      const tokens = await api.query.nonfungible.tokenData.entries(collection);274      const json = tokens.map(([, token]) => token.toJSON());275      expect(json).to.be.deep.equal(data);276    });277  });278279  it('can initialize multiple NFT with different owners', async () => {280    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});281    const alice = privateKey('//Alice');282    const bob = privateKey('//Bob');283    const charlie = privateKey('//Charlie');284    await usingApi(async (api) => {285      const data = [286        {287          owner: {substrate: alice.address},288        }, {289          owner: {substrate: bob.address},290        }, {291          owner: {substrate: charlie.address},292        },293      ];294295      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {296        NFT: data,297      }));298      const tokens = await api.query.nonfungible.tokenData.entries(collection);299      const json = tokens.map(([, token]) => token.toJSON());300      expect(json).to.be.deep.equal(data);301    });302  });303304  it('fails when trying to set multiple owners when creating multiple refungibles', async () => {305    const collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});306    const alice = privateKey('//Alice');307    const bob = privateKey('//Bob');308309    await usingApi(async (api) => {310      // Polkadot requires map, and yet requires keys to be JSON encoded311      const users = new Map();312      users.set(JSON.stringify({substrate: alice.address}), 1);313      users.set(JSON.stringify({substrate: bob.address}), 1);314315      // TODO: better error message?316      await expect(executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {317        RefungibleMultipleItems: [318          {users},319          {users},320        ],321      }))).to.be.rejectedWith(/^refungible\.NotRefungibleDataUsedToMintFungibleCollectionToken$/);322    });323  });324});325'';
modifiedtests/src/destroyCollection.test.tsdiffbeforeafterboth
--- a/tests/src/destroyCollection.test.ts
+++ b/tests/src/destroyCollection.test.ts
@@ -25,6 +25,7 @@
   setCollectionLimitsExpectSuccess,
   addCollectionAdminExpectSuccess,
   getCreatedCollectionCount,
+  createItemExpectSuccess,
 } from './util/helpers';
 
 chai.use(chaiAsPromised);
@@ -83,4 +84,10 @@
 
     await destroyCollectionExpectFailure(collectionId, '//Alice');
   });
+  it('fails when a collection still has a token', async () => {
+    const collectionId = await createCollectionExpectSuccess();
+    await createItemExpectSuccess(alice, collectionId, 'NFT');
+
+    await destroyCollectionExpectFailure(collectionId, '//Alice');
+  });
 });
modifiedtests/src/nesting/migration-check.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/migration-check.test.ts
+++ b/tests/src/nesting/migration-check.test.ts
@@ -31,12 +31,12 @@
         name: strToUTF16('Mojave Pictures'),
         description: strToUTF16('$2.2 billion power plant!'),
         tokenPrefix: '0x0002030',
-        offchainSchema: '0x111111',
-        schemaVersion: 'Unique',
+        //offchainSchema: '0x111111',
+        //schemaVersion: 'Unique',
         limits: {
           accountTokenOwnershipLimit: 3,
         },
-        constOnChainSchema: '0x333333',
+        //constOnChainSchema: '0x333333',
       });
       const events = await submitTransactionAsync(alice, tx);
       const result = getCreateCollectionResult(events);
@@ -96,17 +96,17 @@
       const collectionNew = (await api.query.common.collectionById(collectionId)).toJSON() as any;
 
       // Make sure the extra fields are what they should be
-      const constOnChainSchema = await api.query.common.collectionData(collectionId, 'ConstOnChainSchema');
-      const offchainSchema = await api.query.common.collectionData(collectionId, 'OffchainSchema');
+      //const constOnChainSchema = await api.query.common.collectionData(collectionId, 'ConstOnChainSchema');
+      //const offchainSchema = await api.query.common.collectionData(collectionId, 'OffchainSchema');
 
-      expect(constOnChainSchema.toHex()).to.be.deep.equal(collectionOld.constOnChainSchema);
-      expect(offchainSchema.toHex()).to.be.deep.equal(collectionOld.offchainSchema);
+      //expect(constOnChainSchema.toHex()).to.be.deep.equal(collectionOld.constOnChainSchema);
+      //expect(offchainSchema.toHex()).to.be.deep.equal(collectionOld.offchainSchema);
       expect(collectionNew).to.have.nested.property('limits.nestingRule');
 
       // Get rid of extra fields to perform comparison on the rest of the collection
       delete collectionNew.limits.nestingRule;
-      delete collectionOld.constOnChainSchema;
-      delete collectionOld.offchainSchema;
+      //delete collectionOld.constOnChainSchema;
+      //delete collectionOld.offchainSchema;
 
       expect(collectionNew).to.be.deep.equal(collectionOld);
     });
modifiedtests/src/nesting/nest.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/nest.test.ts
+++ b/tests/src/nesting/nest.test.ts
@@ -10,6 +10,7 @@
   enablePublicMintingExpectSuccess,
   getTokenOwner,
   getTopmostTokenOwner,
+  normalizeAccountId,
   setCollectionPermissionsExpectSuccess,
   transferExpectFailure,
   transferExpectSuccess,
@@ -28,7 +29,7 @@
     });
   });
 
-  it('Performs the full suite: bundles a token, transfers, and allows to unnest', async () => {
+  it('Performs the full suite: bundles a token, transfers, and unnests', async () => {
     await usingApi(async api => {
       const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
       await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: 'Owner'});
@@ -58,6 +59,33 @@
     });
   });
 
+  it('Transfers an already bundled token', async () => {
+    await usingApi(async api => {
+      const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+      await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: 'Owner'});
+
+      const tokenA = await createItemExpectSuccess(alice, collection, 'NFT');
+      const tokenB = await createItemExpectSuccess(alice, collection, 'NFT');
+
+      // Create a nested token
+      const tokenC = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, tokenA)});
+      expect(await getTopmostTokenOwner(api, collection, tokenC)).to.be.deep.equal({Substrate: alice.address});
+      expect(await getTokenOwner(api, collection, tokenC)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, tokenA).toLowerCase()});
+
+      // Transfer the nested token to another token
+      await expect(executeTransaction(
+        api,
+        alice,
+        api.tx.unique.transferFrom(
+          normalizeAccountId({Ethereum: tokenIdToAddress(collection, tokenA)}), 
+          normalizeAccountId({Ethereum: tokenIdToAddress(collection, tokenB)}), 
+          collection, tokenC, 1),
+      )).to.not.be.rejected;
+      expect(await getTopmostTokenOwner(api, collection, tokenC)).to.be.deep.equal({Substrate: alice.address});
+      expect(await getTokenOwner(api, collection, tokenC)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, tokenB).toLowerCase()});
+    });
+  });
+
   // ---------- Non-Fungible ----------
 
   it('NFT: allows an Owner to nest/unnest their token', async () => {
modifiedtests/src/setChainLimits.test.tsdiffbeforeafterboth
--- a/tests/src/setChainLimits.test.ts
+++ b/tests/src/setChainLimits.test.ts
@@ -43,8 +43,6 @@
         nftSponsorTransferTimeout: 1,
         fungibleSponsorTransferTimeout: 1,
         refungibleSponsorTransferTimeout: 1,
-        offchainSchemaLimit: 1,
-        constOnChainSchemaLimit: 1,
       };
     });
   });
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -135,8 +135,8 @@
   nftSponsorTransferTimeout: number;
   fungibleSponsorTransferTimeout: number;
   refungibleSponsorTransferTimeout: number;
-  offchainSchemaLimit: number;
-  constOnChainSchemaLimit: number;
+  //offchainSchemaLimit: number;
+  //constOnChainSchemaLimit: number;
 }
 
 export interface IReFungibleTokenDataType {
@@ -310,7 +310,6 @@
   name: string,
   description: string,
   tokenPrefix: string,
-  schemaVersion: string,
   properties?: Array<Property>,
   propPerm?: Array<PropertyPermission>
 };
@@ -320,7 +319,6 @@
   mode: {type: 'NFT'},
   name: 'name',
   tokenPrefix: 'prefix',
-  schemaVersion: 'ImageURL',
 };
 
 export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {
@@ -780,25 +778,8 @@
     const result = getGenericResult(events);
 
     expect(result.success).to.be.false;
-  });
-}
-
-/*export async function setOffchainSchemaExpectSuccess(sender: IKeyringPair, collectionId: number, data: number[]) {
-  await usingApi(async (api) => {
-    const tx = api.tx.unique.setOffchainSchema(collectionId, '0x' + Buffer.from(data).toString('hex'));
-    const events = await submitTransactionAsync(sender, tx);
-    const result = getGenericResult(events);
-
-    expect(result.success).to.be.true;
   });
 }
-
-export async function setOffchainSchemaExpectFailure(sender: IKeyringPair, collectionId: number, data: number[]) {
-  await usingApi(async (api) => {
-    const tx = api.tx.unique.setOffchainSchema(collectionId, '0x' + Buffer.from(data).toString('hex'));
-    await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
-  });
-}*/
 
 export interface CreateFungibleData {
   readonly Value: bigint;
@@ -1110,13 +1091,6 @@
 ): Promise<string[]> {
   return (await api.rpc.unique.adminlist(collectionId)).toHuman() as any;
 }
-/*export async function getConstMetadata(
-  api: ApiPromise,
-  collectionId: number,
-  tokenId: number,
-): Promise<number[]> {
-  return [...(await api.rpc.unique.constMetadata(collectionId, tokenId))];
-}*/
 export async function getTokenProperties(
   api: ApiPromise,
   collectionId: number,