git.delta.rocks / unique-network / refs/commits / 71a39d07af30

difftreelog

Merge pull request #693 from UniqueNetwork/tests/increase_timeout

ut-akuznetsov2022-11-07parents: #1e082e3 #da93be2.patch.diff
in: master
Tests up

15 files changed

addedtests/src/eth/createFTCollection.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/createFTCollection.seqtest.ts
@@ -0,0 +1,76 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {Pallets, requirePalletsOrSkip} from '../util';
+import {expect, itEth, usingEthPlaygrounds} from './util';
+
+const DECIMALS = 18;
+
+describe('Create FT collection from EVM', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
+      donor = await privateKey({filename: __filename});
+    });
+  });
+
+  itEth('Create collection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    
+    const name = 'CollectionEVM';
+    const description = 'Some description';
+    const prefix = 'token prefix';
+  
+    // todo:playgrounds this might fail when in async environment.
+    const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+
+    const {collectionId} = await helper.eth.createFungibleCollection(owner, name, DECIMALS, description, prefix);
+    
+    const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+    const data = (await helper.ft.getData(collectionId))!;
+
+    expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
+    expect(collectionId).to.be.eq(collectionCountAfter);
+    expect(data.name).to.be.eq(name);
+    expect(data.description).to.be.eq(description);
+    expect(data.raw.tokenPrefix).to.be.eq(prefix);
+    expect(data.raw.mode).to.be.deep.eq({Fungible: DECIMALS.toString()});
+  });
+
+  // todo:playgrounds this test will fail when in async environment.
+  itEth('Check collection address exist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
+    const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
+    expect(await collectionHelpers.methods
+      .isCollectionExist(expectedCollectionAddress)
+      .call()).to.be.false;
+
+    
+    await helper.eth.createFungibleCollection(owner, 'A', DECIMALS, 'A', 'A');
+
+    
+    expect(await collectionHelpers.methods
+      .isCollectionExist(expectedCollectionAddress)
+      .call()).to.be.true;
+  });
+});
modifiedtests/src/eth/createFTCollection.test.tsdiffbeforeafterboth
--- a/tests/src/eth/createFTCollection.test.ts
+++ b/tests/src/eth/createFTCollection.test.ts
@@ -30,50 +30,6 @@
       donor = await privateKey({filename: __filename});
     });
   });
-
-  itEth('Create collection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    
-    const name = 'CollectionEVM';
-    const description = 'Some description';
-    const prefix = 'token prefix';
-  
-    // todo:playgrounds this might fail when in async environment.
-    const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
-
-    const {collectionId} = await helper.eth.createFungibleCollection(owner, name, DECIMALS, description, prefix);
-    
-    const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
-    const data = (await helper.ft.getData(collectionId))!;
-
-    expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
-    expect(collectionId).to.be.eq(collectionCountAfter);
-    expect(data.name).to.be.eq(name);
-    expect(data.description).to.be.eq(description);
-    expect(data.raw.tokenPrefix).to.be.eq(prefix);
-    expect(data.raw.mode).to.be.deep.eq({Fungible: DECIMALS.toString()});
-  });
-
-  // todo:playgrounds this test will fail when in async environment.
-  itEth('Check collection address exist', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
-    const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
-    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
-
-    expect(await collectionHelpers.methods
-      .isCollectionExist(expectedCollectionAddress)
-      .call()).to.be.false;
-
-    
-    await helper.eth.createFungibleCollection(owner, 'A', DECIMALS, 'A', 'A');
-
-    
-    expect(await collectionHelpers.methods
-      .isCollectionExist(expectedCollectionAddress)
-      .call()).to.be.true;
-  });
   
   itEth('Set sponsorship', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
addedtests/src/eth/createNFTCollection.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/createNFTCollection.seqtest.ts
@@ -0,0 +1,89 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {expect, itEth, usingEthPlaygrounds} from './util';
+
+
+describe('Create NFT collection from EVM', () => {
+  let donor: IKeyringPair;
+
+  before(async function () {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({filename: __filename});
+    });
+  });
+
+  itEth('Create collection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const name = 'CollectionEVM';
+    const description = 'Some description';
+    const prefix = 'token prefix';
+
+    // todo:playgrounds this might fail when in async environment.
+    const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+    const {collectionId, collectionAddress, events} = await helper.eth.createNFTCollection(owner, name, description, prefix);
+    
+    expect(events).to.be.deep.equal([
+      {
+        address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',
+        event: 'CollectionCreated',
+        args: {
+          owner: owner,
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    
+    const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+
+    const collection = helper.nft.getCollectionObject(collectionId);
+    const data = (await collection.getData())!;
+
+    expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
+    expect(collectionId).to.be.eq(collectionCountAfter);
+    expect(data.name).to.be.eq(name);
+    expect(data.description).to.be.eq(description);
+    expect(data.raw.tokenPrefix).to.be.eq(prefix);
+    expect(data.raw.mode).to.be.eq('NFT');
+
+    const options = await collection.getOptions();
+
+    expect(options.tokenPropertyPermissions).to.be.empty;
+  });
+
+  // this test will occasionally fail when in async environment.
+  itEth('Check collection address exist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
+    const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
+    expect(await collectionHelpers.methods
+      .isCollectionExist(expectedCollectionAddress)
+      .call()).to.be.false;
+
+    await collectionHelpers.methods
+      .createNFTCollection('A', 'A', 'A')
+      .send({value: Number(2n * helper.balance.getOneTokenNominal())});
+
+    expect(await collectionHelpers.methods
+      .isCollectionExist(expectedCollectionAddress)
+      .call()).to.be.true;
+  });
+});
modifiedtests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth
--- a/tests/src/eth/createNFTCollection.test.ts
+++ b/tests/src/eth/createNFTCollection.test.ts
@@ -28,45 +28,6 @@
     });
   });
 
-  itEth('Create collection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const name = 'CollectionEVM';
-    const description = 'Some description';
-    const prefix = 'token prefix';
-
-    // todo:playgrounds this might fail when in async environment.
-    const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
-    const {collectionId, collectionAddress, events} = await helper.eth.createNFTCollection(owner, name, description, prefix);
-    
-    expect(events).to.be.deep.equal([
-      {
-        address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',
-        event: 'CollectionCreated',
-        args: {
-          owner: owner,
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    
-    const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
-
-    const collection = helper.nft.getCollectionObject(collectionId);
-    const data = (await collection.getData())!;
-
-    expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
-    expect(collectionId).to.be.eq(collectionCountAfter);
-    expect(data.name).to.be.eq(name);
-    expect(data.description).to.be.eq(description);
-    expect(data.raw.tokenPrefix).to.be.eq(prefix);
-    expect(data.raw.mode).to.be.eq('NFT');
-
-    const options = await collection.getOptions();
-
-    expect(options.tokenPropertyPermissions).to.be.empty;
-  });
-
   itEth('Create collection with properties', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
 
@@ -107,27 +68,6 @@
         permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
       },
     ]);
-  });
-
-  // this test will occasionally fail when in async environment.
-  itEth.skip('Check collection address exist', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
-    const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
-    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
-
-    expect(await collectionHelpers.methods
-      .isCollectionExist(expectedCollectionAddress)
-      .call()).to.be.false;
-
-    await collectionHelpers.methods
-      .createNFTCollection('A', 'A', 'A')
-      .send({value: Number(2n * helper.balance.getOneTokenNominal())});
-
-    expect(await collectionHelpers.methods
-      .isCollectionExist(expectedCollectionAddress)
-      .call()).to.be.true;
   });
 
   itEth('Set sponsorship', async ({helper}) => {
modifiedtests/src/eth/fungible.test.tsdiffbeforeafterboth
before · tests/src/eth/fungible.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, itEth, usingEthPlaygrounds} from './util';18import {IKeyringPair} from '@polkadot/types/types';1920describe('Fungible: Information getting', () => {21  let donor: IKeyringPair;22  let alice: IKeyringPair;2324  before(async function() {25    await usingEthPlaygrounds(async (helper, privateKey) => {26      donor = await privateKey({filename: __filename});27      [alice] = await helper.arrange.createAccounts([20n], donor);28    });29  });3031  itEth('totalSupply', async ({helper}) => {32    const caller = await helper.eth.createAccountWithBalance(donor);33    const collection = await helper.ft.mintCollection(alice);34    await collection.mint(alice, 200n);3536    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'ft', caller);37    const totalSupply = await contract.methods.totalSupply().call();38    expect(totalSupply).to.equal('200');39  });4041  itEth('balanceOf', async ({helper}) => {42    const caller = await helper.eth.createAccountWithBalance(donor);43    const collection = await helper.ft.mintCollection(alice);44    await collection.mint(alice, 200n, {Ethereum: caller});4546    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'ft', caller);47    const balance = await contract.methods.balanceOf(caller).call();48    expect(balance).to.equal('200');49  });50});5152describe('Fungible: Plain calls', () => {53  let donor: IKeyringPair;54  let alice: IKeyringPair;5556  before(async function() {57    await usingEthPlaygrounds(async (helper, privateKey) => {58      donor = await privateKey({filename: __filename});59      [alice] = await helper.arrange.createAccounts([20n], donor);60    });61  });6263  itEth('Can perform mint()', async ({helper}) => {64    const owner = await helper.eth.createAccountWithBalance(donor);65    const receiver = helper.eth.createAccount();66    const collection = await helper.ft.mintCollection(alice);67    await collection.addAdmin(alice, {Ethereum: owner});6869    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);70    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);7172    const result = await contract.methods.mint(receiver, 100).send();73    74    const event = result.events.Transfer;75    expect(event.address).to.equal(collectionAddress);76    expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');77    expect(event.returnValues.to).to.equal(receiver);78    expect(event.returnValues.value).to.equal('100');79  });8081  itEth('Can perform mintBulk()', async ({helper}) => {82    const owner = await helper.eth.createAccountWithBalance(donor);83    const bulkSize = 3;84    const receivers = [...new Array(bulkSize)].map(() => helper.eth.createAccount());85    const collection = await helper.ft.mintCollection(alice);86    await collection.addAdmin(alice, {Ethereum: owner});8788    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);89    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);9091    const result = await contract.methods.mintBulk(Array.from({length: bulkSize}, (_, i) => (92      [receivers[i], (i + 1) * 10]93    ))).send();94    const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.value - b.returnValues.value);95    for (let i = 0; i < bulkSize; i++) {96      const event = events[i];97      expect(event.address).to.equal(collectionAddress);98      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');99      expect(event.returnValues.to).to.equal(receivers[i]);100      expect(event.returnValues.value).to.equal(String(10 * (i + 1)));101    }102  });103104  itEth('Can perform burn()', async ({helper}) => {105    const owner = await helper.eth.createAccountWithBalance(donor);106    const receiver = await helper.eth.createAccountWithBalance(donor);107    const collection = await helper.ft.mintCollection(alice);108    await collection.addAdmin(alice, {Ethereum: owner});109110    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);111    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);112    await contract.methods.mint(receiver, 100).send();113114    const result = await contract.methods.burnFrom(receiver, 49).send({from: receiver});115    116    const event = result.events.Transfer;117    expect(event.address).to.equal(collectionAddress);118    expect(event.returnValues.from).to.equal(receiver);119    expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');120    expect(event.returnValues.value).to.equal('49');121122    const balance = await contract.methods.balanceOf(receiver).call();123    expect(balance).to.equal('51');124  });125126  itEth('Can perform approve()', async ({helper}) => {127    const owner = await helper.eth.createAccountWithBalance(donor);128    const spender = helper.eth.createAccount();129    const collection = await helper.ft.mintCollection(alice);130    await collection.mint(alice, 200n, {Ethereum: owner});131132    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);133    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);134135    {136      const result = await contract.methods.approve(spender, 100).send({from: owner});137138      const event = result.events.Approval;139      expect(event.address).to.be.equal(collectionAddress);140      expect(event.returnValues.owner).to.be.equal(owner);141      expect(event.returnValues.spender).to.be.equal(spender);142      expect(event.returnValues.value).to.be.equal('100');143    }144145    {146      const allowance = await contract.methods.allowance(owner, spender).call();147      expect(+allowance).to.equal(100);148    }149  });150151  itEth('Can perform burnFromCross()', async ({helper, privateKey}) => {152    const owner = await privateKey('//Alice');153    const sender = await helper.eth.createAccountWithBalance(donor);154155    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);156157    await collection.mint(owner, 200n, {Substrate: owner.address});158    await collection.approveTokens(owner, {Ethereum: sender}, 100n);159160    const address = helper.ethAddress.fromCollectionId(collection.collectionId);161    const contract = helper.ethNativeContract.collection(address, 'ft');162163    const fromBalanceBefore = await collection.getBalance({Substrate: owner.address});164    165    const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);166    const result = await contract.methods.burnFromCross(ownerCross, 49).send({from: sender});167    const events = result.events;168169    expect(events).to.be.like({170      Transfer: {171        address: helper.ethAddress.fromCollectionId(collection.collectionId),172        event: 'Transfer',173        returnValues: {174          from: helper.address.substrateToEth(owner.address),175          to: '0x0000000000000000000000000000000000000000',176          value: '49',177        },178      },179      Approval: {180        address: helper.ethAddress.fromCollectionId(collection.collectionId),181        returnValues: {182          owner: helper.address.substrateToEth(owner.address),183          spender: sender,184          value: '51',185        },186        event: 'Approval',187      },188    });189190    const fromBalanceAfter = await collection.getBalance({Substrate: owner.address});191    expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(49n);192  });193194  itEth('Can perform transferFrom()', async ({helper}) => {195    const owner = await helper.eth.createAccountWithBalance(donor);196    const spender = await helper.eth.createAccountWithBalance(donor);197    const receiver = helper.eth.createAccount();198    const collection = await helper.ft.mintCollection(alice);199    await collection.mint(alice, 200n, {Ethereum: owner});200201    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);202    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);203204    await contract.methods.approve(spender, 100).send();205206    {207      const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});208      209      let event = result.events.Transfer;210      expect(event.address).to.be.equal(collectionAddress);211      expect(event.returnValues.from).to.be.equal(owner);212      expect(event.returnValues.to).to.be.equal(receiver);213      expect(event.returnValues.value).to.be.equal('49');214215      event = result.events.Approval;216      expect(event.address).to.be.equal(collectionAddress);217      expect(event.returnValues.owner).to.be.equal(owner);218      expect(event.returnValues.spender).to.be.equal(spender);219      expect(event.returnValues.value).to.be.equal('51');220    }221222    {223      const balance = await contract.methods.balanceOf(receiver).call();224      expect(+balance).to.equal(49);225    }226227    {228      const balance = await contract.methods.balanceOf(owner).call();229      expect(+balance).to.equal(151);230    }231  });232233  itEth('Can perform transfer()', async ({helper}) => {234    const owner = await helper.eth.createAccountWithBalance(donor);235    const receiver = await helper.eth.createAccountWithBalance(donor);236    const collection = await helper.ft.mintCollection(alice);237    await collection.mint(alice, 200n, {Ethereum: owner});238239    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);240    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);241242    {243      const result = await contract.methods.transfer(receiver, 50).send({from: owner});244      245      const event = result.events.Transfer;246      expect(event.address).to.be.equal(collectionAddress);247      expect(event.returnValues.from).to.be.equal(owner);248      expect(event.returnValues.to).to.be.equal(receiver);249      expect(event.returnValues.value).to.be.equal('50');250    }251252    {253      const balance = await contract.methods.balanceOf(owner).call();254      expect(+balance).to.equal(150);255    }256257    {258      const balance = await contract.methods.balanceOf(receiver).call();259      expect(+balance).to.equal(50);260    }261  });262263  itEth('Can perform transferFromCross()', async ({helper, privateKey}) => {264    const owner = await privateKey('//Alice');265    const sender = await helper.eth.createAccountWithBalance(donor);266267    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);268269    const receiver = helper.eth.createAccount();270271    await collection.mint(owner, 200n, {Substrate: owner.address});272    await collection.approveTokens(owner, {Ethereum: sender}, 100n);273274    const address = helper.ethAddress.fromCollectionId(collection.collectionId);275    const contract = helper.ethNativeContract.collection(address, 'ft');276277    const from = helper.ethCrossAccount.fromKeyringPair(owner);278    const to = helper.ethCrossAccount.fromAddress(receiver);279280    const fromBalanceBefore = await collection.getBalance({Substrate: owner.address});281    const toBalanceBefore = await collection.getBalance({Ethereum: receiver});282    283    const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender});284285    expect(result.events).to.be.like({286      Transfer: {287        address,288        event: 'Transfer',289        returnValues: {290          from: helper.address.substrateToEth(owner.address),291          to: receiver,292          value: '51',293        },294      },295      Approval: {296        address,297        event: 'Approval',298        returnValues: {299          owner: helper.address.substrateToEth(owner.address),300          spender: sender,301          value: '49',302        },303      }});304305    const fromBalanceAfter = await collection.getBalance({Substrate: owner.address});306    expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(51n);307    const toBalanceAfter = await collection.getBalance({Ethereum: receiver});308    expect(toBalanceAfter - toBalanceBefore).to.be.eq(51n);309  });310});311312describe('Fungible: Fees', () => {313  let donor: IKeyringPair;314  let alice: IKeyringPair;315316  before(async function() {317    await usingEthPlaygrounds(async (helper, privateKey) => {318      donor = await privateKey({filename: __filename});319      [alice] = await helper.arrange.createAccounts([20n], donor);320    });321  });322  323  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {324    const owner = await helper.eth.createAccountWithBalance(donor);325    const spender = helper.eth.createAccount();326    const collection = await helper.ft.mintCollection(alice);327    await collection.mint(alice, 200n, {Ethereum: owner});328329    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);330    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);331332    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));333    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));334  });335336  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {337    const owner = await helper.eth.createAccountWithBalance(donor);338    const spender = await helper.eth.createAccountWithBalance(donor);339    const collection = await helper.ft.mintCollection(alice);340    await collection.mint(alice, 200n, {Ethereum: owner});341342    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);343    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);344345    await contract.methods.approve(spender, 100).send({from: owner});346347    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));348    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));349  });350351  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {352    const owner = await helper.eth.createAccountWithBalance(donor);353    const receiver = helper.eth.createAccount();354    const collection = await helper.ft.mintCollection(alice);355    await collection.mint(alice, 200n, {Ethereum: owner});356357    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);358    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);359360    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));361    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));362  });363});364365describe('Fungible: Substrate calls', () => {366  let donor: IKeyringPair;367  let alice: IKeyringPair;368369  before(async function() {370    await usingEthPlaygrounds(async (helper, privateKey) => {371      donor = await privateKey({filename: __filename});372      [alice] = await helper.arrange.createAccounts([20n], donor);373    });374  });375376  itEth('Events emitted for approve()', async ({helper}) => {377    const receiver = helper.eth.createAccount();378    const collection = await helper.ft.mintCollection(alice);379    await collection.mint(alice, 200n);380381    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);382    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');383384    const events: any = [];385    contract.events.allEvents((_: any, event: any) => {386      events.push(event);387    });388    389    await collection.approveTokens(alice, {Ethereum: receiver}, 100n);390    if (events.length == 0) await helper.wait.newBlocks(1);391    const event = events[0];392393    expect(event.event).to.be.equal('Approval');394    expect(event.address).to.be.equal(collectionAddress);395    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));396    expect(event.returnValues.spender).to.be.equal(receiver);397    expect(event.returnValues.value).to.be.equal('100');398  });399400  itEth('Events emitted for transferFrom()', async ({helper}) => {401    const [bob] = await helper.arrange.createAccounts([10n], donor);402    const receiver = helper.eth.createAccount();403    const collection = await helper.ft.mintCollection(alice);404    await collection.mint(alice, 200n);405    await collection.approveTokens(alice, {Substrate: bob.address}, 100n);406407    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);408    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');409410    const events: any = [];411    contract.events.allEvents((_: any, event: any) => {412      events.push(event);413    });414415    await collection.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n);416    if (events.length == 0) await helper.wait.newBlocks(1);417    let event = events[0];418419    expect(event.event).to.be.equal('Transfer');420    expect(event.address).to.be.equal(collectionAddress);421    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));422    expect(event.returnValues.to).to.be.equal(receiver);423    expect(event.returnValues.value).to.be.equal('51');424425    event = events[1];426    expect(event.event).to.be.equal('Approval');427    expect(event.address).to.be.equal(collectionAddress);428    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));429    expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));430    expect(event.returnValues.value).to.be.equal('49');431  });432433  itEth('Events emitted for transfer()', async ({helper}) => {434    const receiver = helper.eth.createAccount();435    const collection = await helper.ft.mintCollection(alice);436    await collection.mint(alice, 200n);437438    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);439    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');440441    const events: any = [];442    contract.events.allEvents((_: any, event: any) => {443      events.push(event);444    });445    446    await collection.transfer(alice, {Ethereum:receiver}, 51n);447    if (events.length == 0) await helper.wait.newBlocks(1);448    const event = events[0];449450    expect(event.event).to.be.equal('Transfer');451    expect(event.address).to.be.equal(collectionAddress);452    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));453    expect(event.returnValues.to).to.be.equal(receiver);454    expect(event.returnValues.value).to.be.equal('51');455  });456457  itEth('Events emitted for transferFromCross()', async ({helper, privateKey}) => {458    const owner = await privateKey('//Alice');459    const sender = await helper.eth.createAccountWithBalance(donor);460461    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);462463    const receiver = helper.eth.createAccount();464465    await collection.mint(owner, 200n, {Substrate: owner.address});466    await collection.approveTokens(owner, {Ethereum: sender}, 100n);467468    const address = helper.ethAddress.fromCollectionId(collection.collectionId);469    const contract = helper.ethNativeContract.collection(address, 'ft');470471    const from = helper.ethCrossAccount.fromKeyringPair(owner);472    const to = helper.ethCrossAccount.fromAddress(receiver);473    474    const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender});475476    expect(result.events).to.be.like({477      Transfer: {478        address,479        event: 'Transfer',480        returnValues: {481          from: helper.address.substrateToEth(owner.address),482          to: receiver,483          value: '51',484        },485      },486      Approval: {487        address,488        event: 'Approval',489        returnValues: {490          owner: helper.address.substrateToEth(owner.address),491          spender: sender,492          value: '49',493        },494      }});495  });496});
after · tests/src/eth/fungible.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, itEth, usingEthPlaygrounds} from './util';18import {IKeyringPair} from '@polkadot/types/types';1920describe('Fungible: Information getting', () => {21  let donor: IKeyringPair;22  let alice: IKeyringPair;2324  before(async function() {25    await usingEthPlaygrounds(async (helper, privateKey) => {26      donor = await privateKey({filename: __filename});27      [alice] = await helper.arrange.createAccounts([20n], donor);28    });29  });3031  itEth('totalSupply', async ({helper}) => {32    const caller = await helper.eth.createAccountWithBalance(donor);33    const collection = await helper.ft.mintCollection(alice);34    await collection.mint(alice, 200n);3536    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'ft', caller);37    const totalSupply = await contract.methods.totalSupply().call();38    expect(totalSupply).to.equal('200');39  });4041  itEth('balanceOf', async ({helper}) => {42    const caller = await helper.eth.createAccountWithBalance(donor);43    const collection = await helper.ft.mintCollection(alice);44    await collection.mint(alice, 200n, {Ethereum: caller});4546    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'ft', caller);47    const balance = await contract.methods.balanceOf(caller).call();48    expect(balance).to.equal('200');49  });50});5152describe('Fungible: Plain calls', () => {53  let donor: IKeyringPair;54  let alice: IKeyringPair;55  let owner: IKeyringPair;5657  before(async function() {58    await usingEthPlaygrounds(async (helper, privateKey) => {59      donor = await privateKey({filename: __filename});60      [alice, owner] = await helper.arrange.createAccounts([20n, 20n], donor);61    });62  });6364  itEth('Can perform mint()', async ({helper}) => {65    const owner = await helper.eth.createAccountWithBalance(donor);66    const receiver = helper.eth.createAccount();67    const collection = await helper.ft.mintCollection(alice);68    await collection.addAdmin(alice, {Ethereum: owner});6970    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);71    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);7273    const result = await contract.methods.mint(receiver, 100).send();74    75    const event = result.events.Transfer;76    expect(event.address).to.equal(collectionAddress);77    expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');78    expect(event.returnValues.to).to.equal(receiver);79    expect(event.returnValues.value).to.equal('100');80  });8182  itEth('Can perform mintBulk()', async ({helper}) => {83    const owner = await helper.eth.createAccountWithBalance(donor);84    const bulkSize = 3;85    const receivers = [...new Array(bulkSize)].map(() => helper.eth.createAccount());86    const collection = await helper.ft.mintCollection(alice);87    await collection.addAdmin(alice, {Ethereum: owner});8889    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);90    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);9192    const result = await contract.methods.mintBulk(Array.from({length: bulkSize}, (_, i) => (93      [receivers[i], (i + 1) * 10]94    ))).send();95    const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.value - b.returnValues.value);96    for (let i = 0; i < bulkSize; i++) {97      const event = events[i];98      expect(event.address).to.equal(collectionAddress);99      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');100      expect(event.returnValues.to).to.equal(receivers[i]);101      expect(event.returnValues.value).to.equal(String(10 * (i + 1)));102    }103  });104105  itEth('Can perform burn()', async ({helper}) => {106    const owner = await helper.eth.createAccountWithBalance(donor);107    const receiver = await helper.eth.createAccountWithBalance(donor);108    const collection = await helper.ft.mintCollection(alice);109    await collection.addAdmin(alice, {Ethereum: owner});110111    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);112    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);113    await contract.methods.mint(receiver, 100).send();114115    const result = await contract.methods.burnFrom(receiver, 49).send({from: receiver});116    117    const event = result.events.Transfer;118    expect(event.address).to.equal(collectionAddress);119    expect(event.returnValues.from).to.equal(receiver);120    expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');121    expect(event.returnValues.value).to.equal('49');122123    const balance = await contract.methods.balanceOf(receiver).call();124    expect(balance).to.equal('51');125  });126127  itEth('Can perform approve()', async ({helper}) => {128    const owner = await helper.eth.createAccountWithBalance(donor);129    const spender = helper.eth.createAccount();130    const collection = await helper.ft.mintCollection(alice);131    await collection.mint(alice, 200n, {Ethereum: owner});132133    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);134    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);135136    {137      const result = await contract.methods.approve(spender, 100).send({from: owner});138139      const event = result.events.Approval;140      expect(event.address).to.be.equal(collectionAddress);141      expect(event.returnValues.owner).to.be.equal(owner);142      expect(event.returnValues.spender).to.be.equal(spender);143      expect(event.returnValues.value).to.be.equal('100');144    }145146    {147      const allowance = await contract.methods.allowance(owner, spender).call();148      expect(+allowance).to.equal(100);149    }150  });151152  itEth('Can perform burnFromCross()', async ({helper}) => {153    const sender = await helper.eth.createAccountWithBalance(donor, 100n);154155    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);156157    await collection.mint(owner, 200n, {Substrate: owner.address});158    await collection.approveTokens(owner, {Ethereum: sender}, 100n);159160    const address = helper.ethAddress.fromCollectionId(collection.collectionId);161    const contract = helper.ethNativeContract.collection(address, 'ft');162163    const fromBalanceBefore = await collection.getBalance({Substrate: owner.address});164    165    const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);166    const result = await contract.methods.burnFromCross(ownerCross, 49).send({from: sender});167    const events = result.events;168169    expect(events).to.be.like({170      Transfer: {171        address: helper.ethAddress.fromCollectionId(collection.collectionId),172        event: 'Transfer',173        returnValues: {174          from: helper.address.substrateToEth(owner.address),175          to: '0x0000000000000000000000000000000000000000',176          value: '49',177        },178      },179      Approval: {180        address: helper.ethAddress.fromCollectionId(collection.collectionId),181        returnValues: {182          owner: helper.address.substrateToEth(owner.address),183          spender: sender,184          value: '51',185        },186        event: 'Approval',187      },188    });189190    const fromBalanceAfter = await collection.getBalance({Substrate: owner.address});191    expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(49n);192  });193194  itEth('Can perform transferFrom()', async ({helper}) => {195    const owner = await helper.eth.createAccountWithBalance(donor);196    const spender = await helper.eth.createAccountWithBalance(donor);197    const receiver = helper.eth.createAccount();198    const collection = await helper.ft.mintCollection(alice);199    await collection.mint(alice, 200n, {Ethereum: owner});200201    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);202    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);203204    await contract.methods.approve(spender, 100).send();205206    {207      const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});208      209      let event = result.events.Transfer;210      expect(event.address).to.be.equal(collectionAddress);211      expect(event.returnValues.from).to.be.equal(owner);212      expect(event.returnValues.to).to.be.equal(receiver);213      expect(event.returnValues.value).to.be.equal('49');214215      event = result.events.Approval;216      expect(event.address).to.be.equal(collectionAddress);217      expect(event.returnValues.owner).to.be.equal(owner);218      expect(event.returnValues.spender).to.be.equal(spender);219      expect(event.returnValues.value).to.be.equal('51');220    }221222    {223      const balance = await contract.methods.balanceOf(receiver).call();224      expect(+balance).to.equal(49);225    }226227    {228      const balance = await contract.methods.balanceOf(owner).call();229      expect(+balance).to.equal(151);230    }231  });232233  itEth('Can perform transfer()', async ({helper}) => {234    const owner = await helper.eth.createAccountWithBalance(donor);235    const receiver = await helper.eth.createAccountWithBalance(donor);236    const collection = await helper.ft.mintCollection(alice);237    await collection.mint(alice, 200n, {Ethereum: owner});238239    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);240    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);241242    {243      const result = await contract.methods.transfer(receiver, 50).send({from: owner});244      245      const event = result.events.Transfer;246      expect(event.address).to.be.equal(collectionAddress);247      expect(event.returnValues.from).to.be.equal(owner);248      expect(event.returnValues.to).to.be.equal(receiver);249      expect(event.returnValues.value).to.be.equal('50');250    }251252    {253      const balance = await contract.methods.balanceOf(owner).call();254      expect(+balance).to.equal(150);255    }256257    {258      const balance = await contract.methods.balanceOf(receiver).call();259      expect(+balance).to.equal(50);260    }261  });262263  itEth('Can perform transferFromCross()', async ({helper, privateKey}) => {264    const sender = await helper.eth.createAccountWithBalance(donor, 100n);265266    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);267268    const receiver = helper.eth.createAccount();269270    await collection.mint(owner, 200n, {Substrate: owner.address});271    await collection.approveTokens(owner, {Ethereum: sender}, 100n);272273    const address = helper.ethAddress.fromCollectionId(collection.collectionId);274    const contract = helper.ethNativeContract.collection(address, 'ft');275276    const from = helper.ethCrossAccount.fromKeyringPair(owner);277    const to = helper.ethCrossAccount.fromAddress(receiver);278279    const fromBalanceBefore = await collection.getBalance({Substrate: owner.address});280    const toBalanceBefore = await collection.getBalance({Ethereum: receiver});281    282    const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender});283284    expect(result.events).to.be.like({285      Transfer: {286        address,287        event: 'Transfer',288        returnValues: {289          from: helper.address.substrateToEth(owner.address),290          to: receiver,291          value: '51',292        },293      },294      Approval: {295        address,296        event: 'Approval',297        returnValues: {298          owner: helper.address.substrateToEth(owner.address),299          spender: sender,300          value: '49',301        },302      }});303304    const fromBalanceAfter = await collection.getBalance({Substrate: owner.address});305    expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(51n);306    const toBalanceAfter = await collection.getBalance({Ethereum: receiver});307    expect(toBalanceAfter - toBalanceBefore).to.be.eq(51n);308  });309});310311describe('Fungible: Fees', () => {312  let donor: IKeyringPair;313  let alice: IKeyringPair;314315  before(async function() {316    await usingEthPlaygrounds(async (helper, privateKey) => {317      donor = await privateKey({filename: __filename});318      [alice] = await helper.arrange.createAccounts([20n], donor);319    });320  });321  322  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {323    const owner = await helper.eth.createAccountWithBalance(donor);324    const spender = helper.eth.createAccount();325    const collection = await helper.ft.mintCollection(alice);326    await collection.mint(alice, 200n, {Ethereum: owner});327328    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);329    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);330331    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));332    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));333  });334335  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {336    const owner = await helper.eth.createAccountWithBalance(donor);337    const spender = await helper.eth.createAccountWithBalance(donor);338    const collection = await helper.ft.mintCollection(alice);339    await collection.mint(alice, 200n, {Ethereum: owner});340341    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);342    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);343344    await contract.methods.approve(spender, 100).send({from: owner});345346    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));347    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));348  });349350  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {351    const owner = await helper.eth.createAccountWithBalance(donor);352    const receiver = helper.eth.createAccount();353    const collection = await helper.ft.mintCollection(alice);354    await collection.mint(alice, 200n, {Ethereum: owner});355356    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);357    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);358359    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));360    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));361  });362});363364describe('Fungible: Substrate calls', () => {365  let donor: IKeyringPair;366  let alice: IKeyringPair;367  let owner: IKeyringPair;368369  before(async function() {370    await usingEthPlaygrounds(async (helper, privateKey) => {371      donor = await privateKey({filename: __filename});372      [alice, owner] = await helper.arrange.createAccounts([20n, 20n], donor);373    });374  });375376  itEth('Events emitted for approve()', async ({helper}) => {377    const receiver = helper.eth.createAccount();378    const collection = await helper.ft.mintCollection(alice);379    await collection.mint(alice, 200n);380381    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);382    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');383384    const events: any = [];385    contract.events.allEvents((_: any, event: any) => {386      events.push(event);387    });388    389    await collection.approveTokens(alice, {Ethereum: receiver}, 100n);390    if (events.length == 0) await helper.wait.newBlocks(1);391    const event = events[0];392393    expect(event.event).to.be.equal('Approval');394    expect(event.address).to.be.equal(collectionAddress);395    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));396    expect(event.returnValues.spender).to.be.equal(receiver);397    expect(event.returnValues.value).to.be.equal('100');398  });399400  itEth('Events emitted for transferFrom()', async ({helper}) => {401    const [bob] = await helper.arrange.createAccounts([10n], donor);402    const receiver = helper.eth.createAccount();403    const collection = await helper.ft.mintCollection(alice);404    await collection.mint(alice, 200n);405    await collection.approveTokens(alice, {Substrate: bob.address}, 100n);406407    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);408    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');409410    const events: any = [];411    contract.events.allEvents((_: any, event: any) => {412      events.push(event);413    });414415    await collection.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n);416    if (events.length == 0) await helper.wait.newBlocks(1);417    let event = events[0];418419    expect(event.event).to.be.equal('Transfer');420    expect(event.address).to.be.equal(collectionAddress);421    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));422    expect(event.returnValues.to).to.be.equal(receiver);423    expect(event.returnValues.value).to.be.equal('51');424425    event = events[1];426    expect(event.event).to.be.equal('Approval');427    expect(event.address).to.be.equal(collectionAddress);428    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));429    expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));430    expect(event.returnValues.value).to.be.equal('49');431  });432433  itEth('Events emitted for transfer()', async ({helper}) => {434    const receiver = helper.eth.createAccount();435    const collection = await helper.ft.mintCollection(alice);436    await collection.mint(alice, 200n);437438    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);439    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');440441    const events: any = [];442    contract.events.allEvents((_: any, event: any) => {443      events.push(event);444    });445    446    await collection.transfer(alice, {Ethereum:receiver}, 51n);447    if (events.length == 0) await helper.wait.newBlocks(1);448    const event = events[0];449450    expect(event.event).to.be.equal('Transfer');451    expect(event.address).to.be.equal(collectionAddress);452    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));453    expect(event.returnValues.to).to.be.equal(receiver);454    expect(event.returnValues.value).to.be.equal('51');455  });456457  itEth('Events emitted for transferFromCross()', async ({helper, privateKey}) => {458    const sender = await helper.eth.createAccountWithBalance(donor, 100n);459460    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);461462    const receiver = helper.eth.createAccount();463464    await collection.mint(owner, 200n, {Substrate: owner.address});465    await collection.approveTokens(owner, {Ethereum: sender}, 100n);466467    const address = helper.ethAddress.fromCollectionId(collection.collectionId);468    const contract = helper.ethNativeContract.collection(address, 'ft');469470    const from = helper.ethCrossAccount.fromKeyringPair(owner);471    const to = helper.ethCrossAccount.fromAddress(receiver);472    473    const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender});474475    expect(result.events).to.be.like({476      Transfer: {477        address,478        event: 'Transfer',479        returnValues: {480          from: helper.address.substrateToEth(owner.address),481          to: receiver,482          value: '51',483        },484      },485      Approval: {486        address,487        event: 'Approval',488        returnValues: {489          owner: helper.address.substrateToEth(owner.address),490          spender: sender,491          value: '49',492        },493      }});494  });495});
addedtests/src/eth/migration.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/migration.seqtest.ts
@@ -0,0 +1,108 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {expect, itEth, usingEthPlaygrounds} from './util';
+import {IKeyringPair} from '@polkadot/types/types';
+
+describe('EVM Migrations', () => {
+  let superuser: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      superuser = await privateKey('//Alice');
+    });
+  });
+  
+  // todo:playgrounds requires sudo, look into later
+  itEth('Deploy contract saved state', async ({helper}) => {
+    /*
+      contract StatefulContract {
+        uint counter;
+        mapping (uint => uint) kv;
+
+        function inc() public {
+          counter = counter + 1;
+        }
+        function counterValue() public view returns (uint) {
+          return counter;
+        }
+
+        function set(uint key, uint value) public {
+          kv[key] = value;
+        }
+
+        function get(uint key) public view returns (uint) {
+          return kv[key];
+        }
+      }
+    */
+    const ADDRESS = '0x4956bf52ef9ed8789f21bc600e915e0d961079f6';
+    const CODE = '0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80631ab06ee514610051578063371303c01461006d5780637bfdec3b146100775780639507d39a14610095575b600080fd5b61006b60048036038101906100669190610160565b6100c5565b005b6100756100e1565b005b61007f6100f8565b60405161008c91906101af565b60405180910390f35b6100af60048036038101906100aa9190610133565b610101565b6040516100bc91906101af565b60405180910390f35b8060016000848152602001908152602001600020819055505050565b60016000546100f091906101ca565b600081905550565b60008054905090565b600060016000838152602001908152602001600020549050919050565b60008135905061012d8161025e565b92915050565b60006020828403121561014957610148610259565b5b60006101578482850161011e565b91505092915050565b6000806040838503121561017757610176610259565b5b60006101858582860161011e565b92505060206101968582860161011e565b9150509250929050565b6101a981610220565b82525050565b60006020820190506101c460008301846101a0565b92915050565b60006101d582610220565b91506101e083610220565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156102155761021461022a565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61026781610220565b811461027257600080fd5b5056fea26469706673582212206a02d2fb5c244105ab884961479c1aee3b4c1011e4b5530ab483eb22344a865664736f6c63430008060033';
+    const DATA = [
+      // counter = 10
+      ['0x0000000000000000000000000000000000000000000000000000000000000000', '0x000000000000000000000000000000000000000000000000000000000000000a'],
+      // kv = {1: 1, 2: 2, 3: 3, 4: 4},
+      ['0xcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f', '0x0000000000000000000000000000000000000000000000000000000000000001'],
+      ['0xd9d16d34ffb15ba3a3d852f0d403e2ce1d691fb54de27ac87cd2f993f3ec330f', '0x0000000000000000000000000000000000000000000000000000000000000002'],
+      ['0x7dfe757ecd65cbd7922a9c0161e935dd7fdbcc0e999689c7d31633896b1fc60b', '0x0000000000000000000000000000000000000000000000000000000000000003'],
+      ['0xedc95719e9a3b28dd8e80877cb5880a9be7de1a13fc8b05e7999683b6b567643', '0x0000000000000000000000000000000000000000000000000000000000000004'],
+    ];
+
+    const caller = await helper.eth.createAccountWithBalance(superuser);
+
+    const txBegin = helper.constructApiCall('api.tx.evmMigration.begin', [ADDRESS]);
+    const txSetData = helper.constructApiCall('api.tx.evmMigration.setData', [ADDRESS, DATA]);
+    const txFinish = helper.constructApiCall('api.tx.evmMigration.finish', [ADDRESS, CODE]);
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txBegin])).to.be.fulfilled;
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txSetData])).to.be.fulfilled;
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txFinish])).to.be.fulfilled;
+
+    const web3 = helper.getWeb3();
+    const contract = new web3.eth.Contract([
+      {
+        inputs: [],
+        name: 'counterValue',
+        outputs: [{
+          internalType: 'uint256',
+          name: '',
+          type: 'uint256',
+        }],
+        stateMutability: 'view',
+        type: 'function',
+      },
+      {
+        inputs: [{
+          internalType: 'uint256',
+          name: 'key',
+          type: 'uint256',
+        }],
+        name: 'get',
+        outputs: [{
+          internalType: 'uint256',
+          name: '',
+          type: 'uint256',
+        }],
+        stateMutability: 'view',
+        type: 'function',
+      },
+    ], ADDRESS, {from: caller, gas: helper.eth.DEFAULT_GAS});
+
+    expect(await contract.methods.counterValue().call()).to.be.equal('10');
+    for (let i = 1; i <= 4; i++) {
+      expect(await contract.methods.get(i).call()).to.be.equal(i.toString());
+    }
+  });
+});
deletedtests/src/eth/migration.test.tsdiffbeforeafterboth
--- a/tests/src/eth/migration.test.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-import {expect, itEth, usingEthPlaygrounds} from './util';
-import {IKeyringPair} from '@polkadot/types/types';
-
-describe('EVM Migrations', () => {
-  let superuser: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      superuser = await privateKey('//Alice');
-    });
-  });
-  
-  // todo:playgrounds requires sudo, look into later
-  itEth('Deploy contract saved state', async ({helper}) => {
-    /*
-      contract StatefulContract {
-        uint counter;
-        mapping (uint => uint) kv;
-
-        function inc() public {
-          counter = counter + 1;
-        }
-        function counterValue() public view returns (uint) {
-          return counter;
-        }
-
-        function set(uint key, uint value) public {
-          kv[key] = value;
-        }
-
-        function get(uint key) public view returns (uint) {
-          return kv[key];
-        }
-      }
-    */
-    const ADDRESS = '0x4956bf52ef9ed8789f21bc600e915e0d961079f6';
-    const CODE = '0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80631ab06ee514610051578063371303c01461006d5780637bfdec3b146100775780639507d39a14610095575b600080fd5b61006b60048036038101906100669190610160565b6100c5565b005b6100756100e1565b005b61007f6100f8565b60405161008c91906101af565b60405180910390f35b6100af60048036038101906100aa9190610133565b610101565b6040516100bc91906101af565b60405180910390f35b8060016000848152602001908152602001600020819055505050565b60016000546100f091906101ca565b600081905550565b60008054905090565b600060016000838152602001908152602001600020549050919050565b60008135905061012d8161025e565b92915050565b60006020828403121561014957610148610259565b5b60006101578482850161011e565b91505092915050565b6000806040838503121561017757610176610259565b5b60006101858582860161011e565b92505060206101968582860161011e565b9150509250929050565b6101a981610220565b82525050565b60006020820190506101c460008301846101a0565b92915050565b60006101d582610220565b91506101e083610220565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156102155761021461022a565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61026781610220565b811461027257600080fd5b5056fea26469706673582212206a02d2fb5c244105ab884961479c1aee3b4c1011e4b5530ab483eb22344a865664736f6c63430008060033';
-    const DATA = [
-      // counter = 10
-      ['0x0000000000000000000000000000000000000000000000000000000000000000', '0x000000000000000000000000000000000000000000000000000000000000000a'],
-      // kv = {1: 1, 2: 2, 3: 3, 4: 4},
-      ['0xcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f', '0x0000000000000000000000000000000000000000000000000000000000000001'],
-      ['0xd9d16d34ffb15ba3a3d852f0d403e2ce1d691fb54de27ac87cd2f993f3ec330f', '0x0000000000000000000000000000000000000000000000000000000000000002'],
-      ['0x7dfe757ecd65cbd7922a9c0161e935dd7fdbcc0e999689c7d31633896b1fc60b', '0x0000000000000000000000000000000000000000000000000000000000000003'],
-      ['0xedc95719e9a3b28dd8e80877cb5880a9be7de1a13fc8b05e7999683b6b567643', '0x0000000000000000000000000000000000000000000000000000000000000004'],
-    ];
-
-    const caller = await helper.eth.createAccountWithBalance(superuser);
-
-    const txBegin = helper.constructApiCall('api.tx.evmMigration.begin', [ADDRESS]);
-    const txSetData = helper.constructApiCall('api.tx.evmMigration.setData', [ADDRESS, DATA]);
-    const txFinish = helper.constructApiCall('api.tx.evmMigration.finish', [ADDRESS, CODE]);
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txBegin])).to.be.fulfilled;
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txSetData])).to.be.fulfilled;
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txFinish])).to.be.fulfilled;
-
-    const web3 = helper.getWeb3();
-    const contract = new web3.eth.Contract([
-      {
-        inputs: [],
-        name: 'counterValue',
-        outputs: [{
-          internalType: 'uint256',
-          name: '',
-          type: 'uint256',
-        }],
-        stateMutability: 'view',
-        type: 'function',
-      },
-      {
-        inputs: [{
-          internalType: 'uint256',
-          name: 'key',
-          type: 'uint256',
-        }],
-        name: 'get',
-        outputs: [{
-          internalType: 'uint256',
-          name: '',
-          type: 'uint256',
-        }],
-        stateMutability: 'view',
-        type: 'function',
-      },
-    ], ADDRESS, {from: caller, gas: helper.eth.DEFAULT_GAS});
-
-    expect(await contract.methods.counterValue().call()).to.be.equal('10');
-    for (let i = 1; i <= 4; i++) {
-      expect(await contract.methods.get(i).call()).to.be.equal(i.toString());
-    }
-  });
-});
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -138,12 +138,14 @@
 
 describe('NFT: Plain calls', () => {
   let donor: IKeyringPair;
-  let alice: IKeyringPair;
+  let minter: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
 
   before(async function() {
     await usingEthPlaygrounds(async (helper, privateKey) => {
       donor = await privateKey({filename: __filename});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
+      [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
     });
   });
 
@@ -176,8 +178,8 @@
     const caller = await helper.eth.createAccountWithBalance(donor);
     const receiver = helper.eth.createAccount();
 
-    const collection = await helper.nft.mintCollection(alice);
-    await collection.addAdmin(alice, {Ethereum: caller});
+    const collection = await helper.nft.mintCollection(minter);
+    await collection.addAdmin(minter, {Ethereum: caller});
 
     const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
     const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
@@ -208,8 +210,8 @@
   itEth('Can perform burn()', async ({helper}) => {
     const caller = await helper.eth.createAccountWithBalance(donor);
 
-    const collection = await helper.nft.mintCollection(alice, {});
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: caller});
+    const collection = await helper.nft.mintCollection(minter, {});
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: caller});
 
     const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
     const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
@@ -229,8 +231,8 @@
     const owner = await helper.eth.createAccountWithBalance(donor);
     const spender = helper.eth.createAccount();
 
-    const collection = await helper.nft.mintCollection(alice, {});
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
+    const collection = await helper.nft.mintCollection(minter, {});
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
 
     const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
     const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -246,12 +248,11 @@
     }
   });
 
-  itEth('Can perform burnFromCross()', async ({helper, privateKey}) => {
-    const minter = await privateKey('//Alice');
+  itEth('Can perform burnFromCross()', async ({helper}) => {
     const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
 
-    const owner = await privateKey('//Bob');
-    const spender = await helper.eth.createAccountWithBalance(donor);
+    const owner = bob;
+    const spender = await helper.eth.createAccountWithBalance(donor, 100n);
 
     const token = await collection.mintToken(minter, {Substrate: owner.address});
 
@@ -276,12 +277,11 @@
     }
   });
 
-  itEth('Can perform approveCross()', async ({helper, privateKey}) => {
-    const minter = await privateKey('//Alice');
+  itEth('Can perform approveCross()', async ({helper}) => {
     const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
 
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await privateKey('//Charlie');
+    const owner = await helper.eth.createAccountWithBalance(donor, 100n);
+    const receiver = charlie;
 
     const token = await collection.mintToken(minter, {Ethereum: owner});
 
@@ -309,8 +309,8 @@
     const spender = await helper.eth.createAccountWithBalance(donor);
     const receiver = helper.eth.createAccount();
 
-    const collection = await helper.nft.mintCollection(alice, {});
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
+    const collection = await helper.nft.mintCollection(minter, {});
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
 
     const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
     const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -373,11 +373,11 @@
   });
 
   itEth('Can perform transfer()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {});
+    const collection = await helper.nft.mintCollection(minter, {});
     const owner = await helper.eth.createAccountWithBalance(donor);
     const receiver = helper.eth.createAccount();
 
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
 
     const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
     const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
@@ -407,11 +407,13 @@
 describe('NFT: Fees', () => {
   let donor: IKeyringPair;
   let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
 
   before(async function() {
     await usingEthPlaygrounds(async (helper, privateKey) => {
       donor = await privateKey({filename: __filename});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
+      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
     });
   });
 
@@ -443,6 +445,40 @@
     expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
   });
 
+  itEth('Can perform transferFromCross()', async ({helper, privateKey}) => {
+    const collectionMinter = alice;
+    const owner = bob;
+    const receiver = charlie;
+    const collection = await helper.nft.mintCollection(collectionMinter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const spender = await helper.eth.createAccountWithBalance(donor, 100n);
+
+    const token = await collection.mintToken(collectionMinter, {Substrate: owner.address});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(address, 'nft');
+
+    await token.approve(owner, {Ethereum: spender});
+
+    {
+      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});
+      const event = result.events.Transfer;
+      expect(event).to.be.like({
+        address: helper.ethAddress.fromCollectionId(collection.collectionId),
+        event: 'Transfer',
+        returnValues: {
+          from: helper.address.substrateToEth(owner.address),
+          to: helper.address.substrateToEth(receiver.address),
+          tokenId: token.tokenId.toString(),
+        },
+      });
+    }
+
+    expect(await token.getOwner()).to.be.like({Substrate: receiver.address});
+  });
+
   itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
     const receiver = helper.eth.createAccount();
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -104,12 +104,16 @@
 
 describe('Refungible: Plain calls', () => {
   let donor: IKeyringPair;
+  let minter: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
 
   before(async function() {
     await usingEthPlaygrounds(async (helper, privateKey) => {
       requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
 
       donor = await privateKey({filename: __filename});
+      [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
     });
   });
 
@@ -227,12 +231,11 @@
     }
   });
 
-  itEth('Can perform burnFrom()', async ({helper, privateKey}) => {
-    const minter = await privateKey('//Alice');
+  itEth('Can perform burnFrom()', async ({helper}) => {
     const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
 
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
+    const owner = await helper.eth.createAccountWithBalance(donor, 100n);
+    const spender = await helper.eth.createAccountWithBalance(donor, 100n);
 
     const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
 
@@ -261,12 +264,11 @@
     expect(await collection.getTokenBalance(token.tokenId, {Ethereum: owner})).to.be.eq(0n);
   });
 
-  itEth('Can perform burnFromCross()', async ({helper, privateKey}) => {
-    const minter = await privateKey('//Alice');
+  itEth('Can perform burnFromCross()', async ({helper}) => {
     const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = await privateKey('//Bob');
-    const spender = await helper.eth.createAccountWithBalance(donor);
+    
+    const owner = bob;
+    const spender = await helper.eth.createAccountWithBalance(donor, 100n);
 
     const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});
 
@@ -294,13 +296,12 @@
     expect(await collection.getTokenBalance(token.tokenId, {Substrate: owner.address})).to.be.eq(0n);
   });
 
-  itEth('Can perform transferFromCross()', async ({helper, privateKey}) => {
-    const minter = await privateKey('//Alice');
+  itEth('Can perform transferFromCross()', async ({helper}) => {
     const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
 
-    const owner = await privateKey('//Bob');
-    const spender = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await privateKey('//Charlie');
+    const owner = bob;
+    const spender = await helper.eth.createAccountWithBalance(donor, 100n);
+    const receiver = charlie;
 
     const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});
 
modifiedtests/src/eth/scheduling.test.tsdiffbeforeafterboth
--- a/tests/src/eth/scheduling.test.ts
+++ b/tests/src/eth/scheduling.test.ts
@@ -27,7 +27,8 @@
   });
 
   itEth.ifWithPallets('Successfully schedules and periodically executes an EVM contract', [Pallets.Scheduler], async ({helper, privateKey}) => {
-    const alice = await privateKey('//Alice');
+    const donor = await privateKey({filename: __filename});
+    const [alice] = await helper.arrange.createAccounts([1000n], donor);
 
     const scheduledId = await helper.arrange.makeScheduledId();
 
addedtests/src/inflation.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/inflation.seqtest.ts
@@ -0,0 +1,58 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, usingPlaygrounds} from './util';
+
+// todo:playgrounds requires sudo, look into on the later stage
+describe('integration test: Inflation', () => {
+  let superuser: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (_, privateKey) => {
+      superuser = await privateKey('//Alice');
+    });
+  });
+  
+  itSub('First year inflation is 10%', async ({helper}) => {
+    // Make sure non-sudo can't start inflation
+    const [bob] = await helper.arrange.createAccounts([10n], superuser);
+
+    await expect(helper.executeExtrinsic(bob, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);
+
+    // Make sure superuser can't start inflation without explicit sudo
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);
+
+    // Start inflation on relay block 1 (Alice is sudo)
+    const tx = helper.constructApiCall('api.tx.inflation.startInflation', [1]);
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.not.be.rejected;
+
+    const blockInterval = (helper.getApi().consts.inflation.inflationBlockInterval as any).toBigInt();
+    const totalIssuanceStart = ((await helper.callRpc('api.query.inflation.startingYearTotalIssuance', [])) as any).toBigInt();
+    const blockInflation = (await helper.callRpc('api.query.inflation.blockInflation', []) as any).toBigInt();
+
+    const YEAR = 5259600n;  // 6-second block. Blocks in one year
+    // const YEAR = 2629800n; // 12-second block. Blocks in one year
+
+    const totalExpectedInflation = totalIssuanceStart / 10n;
+    const totalActualInflation = blockInflation * YEAR / blockInterval;
+
+    const tolerance = 0.00001; // Relative difference per year between theoretical and actual inflation
+    const expectedInflation = totalExpectedInflation / totalActualInflation - 1n;
+
+    expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance);
+  });
+});
deletedtests/src/inflation.test.tsdiffbeforeafterboth
--- a/tests/src/inflation.test.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-import {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from './util';
-
-// todo:playgrounds requires sudo, look into on the later stage
-describe('integration test: Inflation', () => {
-  let superuser: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (_, privateKey) => {
-      superuser = await privateKey('//Alice');
-    });
-  });
-  
-  itSub('First year inflation is 10%', async ({helper}) => {
-    // Make sure non-sudo can't start inflation
-    const [bob] = await helper.arrange.createAccounts([10n], superuser);
-
-    await expect(helper.executeExtrinsic(bob, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);
-
-    // Make sure superuser can't start inflation without explicit sudo
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);
-
-    // Start inflation on relay block 1 (Alice is sudo)
-    const tx = helper.constructApiCall('api.tx.inflation.startInflation', [1]);
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.not.be.rejected;
-
-    const blockInterval = (helper.getApi().consts.inflation.inflationBlockInterval as any).toBigInt();
-    const totalIssuanceStart = ((await helper.callRpc('api.query.inflation.startingYearTotalIssuance', [])) as any).toBigInt();
-    const blockInflation = (await helper.callRpc('api.query.inflation.blockInflation', []) as any).toBigInt();
-
-    const YEAR = 5259600n;  // 6-second block. Blocks in one year
-    // const YEAR = 2629800n; // 12-second block. Blocks in one year
-
-    const totalExpectedInflation = totalIssuanceStart / 10n;
-    const totalActualInflation = blockInflation * YEAR / blockInterval;
-
-    const tolerance = 0.00001; // Relative difference per year between theoretical and actual inflation
-    const expectedInflation = totalExpectedInflation / totalActualInflation - 1n;
-
-    expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance);
-  });
-});
addedtests/src/scheduler.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/scheduler.seqtest.ts
@@ -0,0 +1,647 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {expect, itSub, Pallets, usingPlaygrounds} from './util';
+import {IKeyringPair} from '@polkadot/types/types';
+import {DevUniqueHelper} from './util/playgrounds/unique.dev';
+
+describe('Scheduling token and balance transfers', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKeyWrapper) => {
+      alice = await privateKeyWrapper('//Alice');
+      bob = await privateKeyWrapper('//Bob');
+      charlie = await privateKeyWrapper('//Charlie');
+
+      await helper.testUtils.enable();
+    });
+  });
+
+  itSub.ifWithPallets('Can delay a transfer of an owned token', [Pallets.Scheduler], async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(alice);
+    const schedulerId = await helper.arrange.makeScheduledId();
+    const blocksBeforeExecution = 4;
+
+    await token.scheduleAfter(schedulerId, blocksBeforeExecution)
+      .transfer(alice, {Substrate: bob.address});
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
+
+    await helper.wait.newBlocks(blocksBeforeExecution + 1);
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
+  });
+
+  itSub.ifWithPallets('Can transfer funds periodically', [Pallets.Scheduler], async ({helper}) => {
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 1;
+
+    const amount = 1n * helper.balance.getOneTokenNominal();
+    const periodic = {
+      period: 2,
+      repetitions: 2,
+    };
+
+    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic})
+      .balance.transferToSubstrate(alice, bob.address, amount);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+
+    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);
+    expect(bobsBalanceAfterFirst)
+      .to.be.equal(
+        bobsBalanceBefore + 1n * amount,
+        '#1 Balance of the recipient should be increased by 1 * amount',
+      );
+
+    await helper.wait.newBlocks(periodic.period);
+
+    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);
+    expect(bobsBalanceAfterSecond)
+      .to.be.equal(
+        bobsBalanceBefore + 2n * amount,
+        '#2 Balance of the recipient should be increased by 2 * amount',
+      );
+  });
+
+  itSub.ifWithPallets('Can cancel a scheduled operation which has not yet taken effect', [Pallets.Scheduler], async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(alice);
+
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
+
+    await token.scheduleAfter(scheduledId, waitForBlocks)
+      .transfer(alice, {Substrate: bob.address});
+
+    await helper.scheduler.cancelScheduled(alice, scheduledId);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
+  });
+
+  itSub.ifWithPallets('Can cancel a periodic operation (transfer of funds)', [Pallets.Scheduler], async ({helper}) => {
+    const waitForBlocks = 1;
+    const periodic = {
+      period: 3,
+      repetitions: 2,
+    };
+
+    const scheduledId = await helper.arrange.makeScheduledId();
+
+    const amount = 1n * helper.balance.getOneTokenNominal();
+
+    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic})
+      .balance.transferToSubstrate(alice, bob.address, amount);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+
+    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);
+
+    expect(bobsBalanceAfterFirst)
+      .to.be.equal(
+        bobsBalanceBefore + 1n * amount,
+        '#1 Balance of the recipient should be increased by 1 * amount',
+      );
+
+    await helper.scheduler.cancelScheduled(alice, scheduledId);
+    await helper.wait.newBlocks(periodic.period);
+
+    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);
+    expect(bobsBalanceAfterSecond)
+      .to.be.equal(
+        bobsBalanceAfterFirst,
+        '#2 Balance of the recipient should not be changed',
+      );
+  });
+
+  itSub.ifWithPallets('Scheduled tasks are transactional', [Pallets.Scheduler, Pallets.TestUtils], async ({helper}) => {
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    const initTestVal = 42;
+    const changedTestVal = 111;
+
+    await helper.testUtils.setTestValue(alice, initTestVal);
+
+    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks)
+      .testUtils.setTestValueAndRollback(alice, changedTestVal);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+
+    const testVal = await helper.testUtils.testValue();
+    expect(testVal, 'The test value should NOT be commited')
+      .to.be.equal(initTestVal);
+  });
+
+  itSub.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.Scheduler, Pallets.TestUtils], async function({helper}) {
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+    const periodic = {
+      period: 2,
+      repetitions: 2,
+    };
+
+    const dummyTx = helper.constructApiCall('api.tx.testUtils.justTakeFee', []);
+    const scheduledLen = dummyTx.callIndex.length;
+
+    const expectedScheduledFee = (await helper.getPaymentInfo(alice, dummyTx, scheduledLen))
+      .partialFee.toBigInt();
+
+    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks, {periodic})
+      .testUtils.justTakeFee(alice);
+
+    await helper.wait.newBlocks(1);
+
+    const aliceInitBalance = await helper.balance.getSubstrate(alice.address);
+    let diff;
+
+    await helper.wait.newBlocks(waitForBlocks);
+
+    const aliceBalanceAfterFirst = await helper.balance.getSubstrate(alice.address);
+    expect(
+      aliceBalanceAfterFirst < aliceInitBalance,
+      '[after execution #1] Scheduled task should take a fee',
+    ).to.be.true;
+
+    diff = aliceInitBalance - aliceBalanceAfterFirst;
+    expect(diff).to.be.equal(
+      expectedScheduledFee,
+      'Scheduled task should take the right amount of fees',
+    );
+
+    await helper.wait.newBlocks(periodic.period);
+
+    const aliceBalanceAfterSecond = await helper.balance.getSubstrate(alice.address);
+    expect(
+      aliceBalanceAfterSecond < aliceBalanceAfterFirst,
+      '[after execution #2] Scheduled task should take a fee',
+    ).to.be.true;
+
+    diff = aliceBalanceAfterFirst - aliceBalanceAfterSecond;
+    expect(diff).to.be.equal(
+      expectedScheduledFee,
+      'Scheduled task should take the right amount of fees',
+    );
+  });
+
+  // Check if we can cancel a scheduled periodic operation
+  // in the same block in which it is running
+  itSub.ifWithPallets('Can cancel the periodic sheduled tx when the tx is running', [Pallets.Scheduler, Pallets.TestUtils], async ({helper}) => {
+    const currentBlockNumber = await helper.chain.getLatestBlockNumber();
+    const blocksBeforeExecution = 10;
+    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;
+
+    const [
+      scheduledId,
+      scheduledCancelId,
+    ] = await helper.arrange.makeScheduledIds(2);
+
+    const periodic = {
+      period: 5,
+      repetitions: 5,
+    };
+
+    const initTestVal = 0;
+    const incTestVal = initTestVal + 1;
+    const finalTestVal = initTestVal + 2;
+
+    await helper.testUtils.setTestValue(alice, initTestVal);
+
+    await helper.scheduler.scheduleAt<DevUniqueHelper>(scheduledId, firstExecutionBlockNumber, {periodic})
+      .testUtils.incTestValue(alice);
+
+    // Cancel the inc tx after 2 executions
+    // *in the same block* in which the second execution is scheduled
+    await helper.scheduler.scheduleAt(
+      scheduledCancelId,
+      firstExecutionBlockNumber + periodic.period,
+    ).scheduler.cancelScheduled(alice, scheduledId);
+
+    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);
+
+    // execution #0
+    expect(await helper.testUtils.testValue())
+      .to.be.equal(incTestVal);
+
+    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);
+
+    // execution #1
+    expect(await helper.testUtils.testValue())
+      .to.be.equal(finalTestVal);
+
+    for (let i = 1; i < periodic.repetitions; i++) {
+      await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period * (i + 1));
+      expect(await helper.testUtils.testValue())
+        .to.be.equal(finalTestVal);
+    }
+  });
+
+  itSub.ifWithPallets('A scheduled operation can cancel itself', [Pallets.Scheduler, Pallets.TestUtils], async ({helper}) => {
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+    const periodic = {
+      period: 2,
+      repetitions: 5,
+    };
+
+    const initTestVal = 0;
+    const maxTestVal = 2;
+
+    await helper.testUtils.setTestValue(alice, initTestVal);
+
+    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks, {periodic})
+      .testUtils.selfCancelingInc(alice, scheduledId, maxTestVal);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+
+    // execution #0
+    expect(await helper.testUtils.testValue())
+      .to.be.equal(initTestVal + 1);
+
+    await helper.wait.newBlocks(periodic.period);
+
+    // execution #1
+    expect(await helper.testUtils.testValue())
+      .to.be.equal(initTestVal + 2);
+
+    await helper.wait.newBlocks(periodic.period);
+
+    // <canceled>
+    expect(await helper.testUtils.testValue())
+      .to.be.equal(initTestVal + 2);
+  });
+
+  itSub.ifWithPallets('Root can cancel any scheduled operation', [Pallets.Scheduler], async ({helper}) => {
+    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(bob);
+
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    await token.scheduleAfter(scheduledId, waitForBlocks)
+      .transfer(bob, {Substrate: alice.address});
+
+    await helper.getSudo().scheduler.cancelScheduled(alice, scheduledId);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
+  });
+
+  itSub.ifWithPallets('Root can set prioritized scheduled operation', [Pallets.Scheduler], async ({helper}) => {
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    const amount = 42n * helper.balance.getOneTokenNominal();
+
+    const balanceBefore = await helper.balance.getSubstrate(charlie.address);
+
+    await helper.getSudo()
+      .scheduler.scheduleAfter(scheduledId, waitForBlocks, {priority: 42})
+      .balance.forceTransferToSubstrate(alice, bob.address, charlie.address, amount);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+
+    const balanceAfter = await helper.balance.getSubstrate(charlie.address);
+
+    expect(balanceAfter > balanceBefore).to.be.true;
+
+    const diff = balanceAfter - balanceBefore;
+    expect(diff).to.be.equal(amount);
+  });
+
+  itSub.ifWithPallets("Root can change scheduled operation's priority", [Pallets.Scheduler], async ({helper}) => {
+    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(bob);
+
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 6;
+
+    await token.scheduleAfter(scheduledId, waitForBlocks)
+      .transfer(bob, {Substrate: alice.address});
+
+    const priority = 112;
+    await helper.getSudo().scheduler.changePriority(alice, scheduledId, priority);
+
+    const priorityChanged = await helper.wait.event(
+      waitForBlocks,
+      'scheduler',
+      'PriorityChanged',
+    );
+
+    expect(priorityChanged !== null).to.be.true;
+    expect(priorityChanged!.event.data[2].toString()).to.be.equal(priority.toString());
+  });
+
+  itSub.ifWithPallets('Prioritized operations executes in valid order', [Pallets.Scheduler], async ({helper}) => {
+    const [
+      scheduledFirstId,
+      scheduledSecondId,
+    ] = await helper.arrange.makeScheduledIds(2);
+
+    const currentBlockNumber = await helper.chain.getLatestBlockNumber();
+    const blocksBeforeExecution = 6;
+    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;
+
+    const prioHigh = 0;
+    const prioLow = 255;
+
+    const periodic = {
+      period: 6,
+      repetitions: 2,
+    };
+
+    const amount = 1n * helper.balance.getOneTokenNominal();
+
+    // Scheduler a task with a lower priority first, then with a higher priority
+    await helper.getSudo().scheduler.scheduleAt(scheduledFirstId, firstExecutionBlockNumber, {priority: prioLow, periodic})
+      .balance.forceTransferToSubstrate(alice, alice.address, bob.address, amount);
+
+    await helper.getSudo().scheduler.scheduleAt(scheduledSecondId, firstExecutionBlockNumber, {priority: prioHigh, periodic})
+      .balance.forceTransferToSubstrate(alice, alice.address, bob.address, amount);
+
+    const capture = await helper.arrange.captureEvents('scheduler', 'Dispatched');
+
+    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);
+
+    // Flip priorities
+    await helper.getSudo().scheduler.changePriority(alice, scheduledFirstId, prioHigh);
+    await helper.getSudo().scheduler.changePriority(alice, scheduledSecondId, prioLow);
+
+    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);
+
+    const dispatchEvents = capture.extractCapturedEvents();
+    expect(dispatchEvents.length).to.be.equal(4);
+
+    const dispatchedIds = dispatchEvents.map(r => r.event.data[1].toString());
+
+    const firstExecuctionIds = [dispatchedIds[0], dispatchedIds[1]];
+    const secondExecuctionIds = [dispatchedIds[2], dispatchedIds[3]];
+
+    expect(firstExecuctionIds[0]).to.be.equal(scheduledSecondId);
+    expect(firstExecuctionIds[1]).to.be.equal(scheduledFirstId);
+
+    expect(secondExecuctionIds[0]).to.be.equal(scheduledFirstId);
+    expect(secondExecuctionIds[1]).to.be.equal(scheduledSecondId);
+  });
+});
+
+describe('Negative Test: Scheduling', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKeyWrapper) => {
+      alice = await privateKeyWrapper('//Alice');
+      bob = await privateKeyWrapper('//Bob');
+
+      await helper.testUtils.enable();
+    });
+  });
+
+  itSub.ifWithPallets("Can't overwrite a scheduled ID", [Pallets.Scheduler], async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(alice);
+
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    await token.scheduleAfter(scheduledId, waitForBlocks)
+      .transfer(alice, {Substrate: bob.address});
+
+    const scheduled = helper.scheduler.scheduleAfter(scheduledId, waitForBlocks);
+    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, 1n * helper.balance.getOneTokenNominal()))
+      .to.be.rejectedWith(/scheduler\.FailedToSchedule/);
+
+    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+
+    const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
+    expect(bobsBalanceBefore).to.be.equal(bobsBalanceAfter);
+  });
+
+  itSub.ifWithPallets("Can't cancel an operation which is not scheduled", [Pallets.Scheduler], async ({helper}) => {
+    const scheduledId = await helper.arrange.makeScheduledId();
+    await expect(helper.scheduler.cancelScheduled(alice, scheduledId))
+      .to.be.rejectedWith(/scheduler\.NotFound/);
+  });
+
+  itSub.ifWithPallets("Can't cancel a non-owned scheduled operation", [Pallets.Scheduler], async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(alice);
+
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    await token.scheduleAfter(scheduledId, waitForBlocks)
+      .transfer(alice, {Substrate: bob.address});
+
+    await expect(helper.scheduler.cancelScheduled(bob, scheduledId))
+      .to.be.rejectedWith(/BadOrigin/);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
+  });
+
+  itSub.ifWithPallets("Regular user can't set prioritized scheduled operation", [Pallets.Scheduler], async ({helper}) => {
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    const amount = 42n * helper.balance.getOneTokenNominal();
+
+    const balanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    const scheduled = helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {priority: 42});
+    
+    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, amount))
+      .to.be.rejectedWith(/BadOrigin/);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+
+    const balanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(balanceAfter).to.be.equal(balanceBefore);
+  });
+
+  itSub.ifWithPallets("Regular user can't change scheduled operation's priority", [Pallets.Scheduler], async ({helper}) => {
+    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(bob);
+
+    const scheduledId = await helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    await token.scheduleAfter(scheduledId, waitForBlocks)
+      .transfer(bob, {Substrate: alice.address});
+
+    const priority = 112;
+    await expect(helper.scheduler.changePriority(alice, scheduledId, priority))
+      .to.be.rejectedWith(/BadOrigin/);
+
+    const priorityChanged = await helper.wait.event(
+      waitForBlocks,
+      'scheduler',
+      'PriorityChanged',
+    );
+
+    expect(priorityChanged === null).to.be.true;
+  });
+});
+
+// Implementation of the functionality tested here was postponed/shelved
+describe.skip('Sponsoring scheduling', () => {
+  // let alice: IKeyringPair;
+  // let bob: IKeyringPair;
+
+  // before(async() => {
+  //   await usingApi(async (_, privateKeyWrapper) => {
+  //     alice = privateKeyWrapper('//Alice');
+  //     bob = privateKeyWrapper('//Bob');
+  //   });
+  // });
+
+  it('Can sponsor scheduling a transaction', async () => {
+    // const collectionId = await createCollectionExpectSuccess();
+    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    // await usingApi(async api => {
+    //   const scheduledId = await makeScheduledId();
+    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
+
+    //   const bobBalanceBefore = await getFreeBalance(bob);
+    //   const waitForBlocks = 4;
+    //   // no need to wait to check, fees must be deducted on scheduling, immediately
+    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, scheduledId);
+    //   const bobBalanceAfter = await getFreeBalance(bob);
+    //   // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;
+    //   expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
+    //   // wait for sequentiality matters
+    //   await waitNewBlocks(waitForBlocks - 1);
+    // });
+  });
+
+  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {
+    // await usingApi(async (api, privateKeyWrapper) => {
+    //   // Find an empty, unused account
+    //   const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
+
+    //   const collectionId = await createCollectionExpectSuccess();
+
+    //   // Add zeroBalance address to allow list
+    //   await enablePublicMintingExpectSuccess(alice, collectionId);
+    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
+
+    //   // Grace zeroBalance with money, enough to cover future transactions
+    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
+    //   await submitTransactionAsync(alice, balanceTx);
+
+    //   // Mint a fresh NFT
+    //   const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');
+    //   const scheduledId = await makeScheduledId();
+
+    //   // Schedule transfer of the NFT a few blocks ahead
+    //   const waitForBlocks = 5;
+    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, scheduledId);
+
+    //   // Get rid of the account's funds before the scheduled transaction takes place
+    //   const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);
+    //   const events = await submitTransactionAsync(zeroBalance, balanceTx2);
+    //   expect(getGenericResult(events).success).to.be.true;
+    //   /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?
+    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);
+    //   const events = await submitTransactionAsync(alice, sudoTx);
+    //   expect(getGenericResult(events).success).to.be.true;*/
+
+    //   // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions
+    //   await waitNewBlocks(waitForBlocks - 3);
+
+    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));
+    // });
+  });
+
+  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {
+    // const collectionId = await createCollectionExpectSuccess();
+
+    // await usingApi(async (api, privateKeyWrapper) => {
+    //   const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
+    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
+    //   await submitTransactionAsync(alice, balanceTx);
+
+    //   await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);
+    //   await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);
+
+    //   const scheduledId = await makeScheduledId();
+    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
+
+    //   const waitForBlocks = 5;
+    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, scheduledId);
+
+    //   const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);
+    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);
+    //   const events = await submitTransactionAsync(alice, sudoTx);
+    //   expect(getGenericResult(events).success).to.be.true;
+
+    //   // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions
+    //   await waitNewBlocks(waitForBlocks - 3);
+
+    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));
+    // });
+  });
+
+  it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {
+    // const collectionId = await createCollectionExpectSuccess();
+    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    // await usingApi(async (api, privateKeyWrapper) => {
+    //   const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
+
+    //   await enablePublicMintingExpectSuccess(alice, collectionId);
+    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
+
+    //   const bobBalanceBefore = await getFreeBalance(bob);
+
+    //   const createData = {nft: {const_data: [], variable_data: []}};
+    //   const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any);
+    //   const scheduledId = await makeScheduledId();
+
+    //   /*const badTransaction = async function () {
+    //     await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
+    //   };
+    //   await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/
+
+    //   await expect(scheduleAfter(api, creationTx, zeroBalance, 3, scheduledId, 1, 3)).to.be.rejectedWith(/Inability to pay some fees/);
+
+    //   expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);
+    // });
+  });
+});
deletedtests/src/scheduler.test.tsdiffbeforeafterboth
--- a/tests/src/scheduler.test.ts
+++ /dev/null
@@ -1,647 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-import {expect, itSub, Pallets, usingPlaygrounds} from './util';
-import {IKeyringPair} from '@polkadot/types/types';
-import {DevUniqueHelper} from './util/playgrounds/unique.dev';
-
-describe('Scheduling token and balance transfers', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKeyWrapper) => {
-      alice = await privateKeyWrapper('//Alice');
-      bob = await privateKeyWrapper('//Bob');
-      charlie = await privateKeyWrapper('//Charlie');
-
-      await helper.testUtils.enable();
-    });
-  });
-
-  itSub.ifWithPallets('Can delay a transfer of an owned token', [Pallets.Scheduler], async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(alice);
-    const schedulerId = await helper.arrange.makeScheduledId();
-    const blocksBeforeExecution = 4;
-
-    await token.scheduleAfter(schedulerId, blocksBeforeExecution)
-      .transfer(alice, {Substrate: bob.address});
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
-
-    await helper.wait.newBlocks(blocksBeforeExecution + 1);
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
-  });
-
-  itSub.ifWithPallets('Can transfer funds periodically', [Pallets.Scheduler], async ({helper}) => {
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 1;
-
-    const amount = 1n * helper.balance.getOneTokenNominal();
-    const periodic = {
-      period: 2,
-      repetitions: 2,
-    };
-
-    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic})
-      .balance.transferToSubstrate(alice, bob.address, amount);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-
-    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);
-    expect(bobsBalanceAfterFirst)
-      .to.be.equal(
-        bobsBalanceBefore + 1n * amount,
-        '#1 Balance of the recipient should be increased by 1 * amount',
-      );
-
-    await helper.wait.newBlocks(periodic.period);
-
-    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);
-    expect(bobsBalanceAfterSecond)
-      .to.be.equal(
-        bobsBalanceBefore + 2n * amount,
-        '#2 Balance of the recipient should be increased by 2 * amount',
-      );
-  });
-
-  itSub.ifWithPallets('Can cancel a scheduled operation which has not yet taken effect', [Pallets.Scheduler], async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(alice);
-
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
-
-    await token.scheduleAfter(scheduledId, waitForBlocks)
-      .transfer(alice, {Substrate: bob.address});
-
-    await helper.scheduler.cancelScheduled(alice, scheduledId);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
-  });
-
-  itSub.ifWithPallets('Can cancel a periodic operation (transfer of funds)', [Pallets.Scheduler], async ({helper}) => {
-    const waitForBlocks = 1;
-    const periodic = {
-      period: 3,
-      repetitions: 2,
-    };
-
-    const scheduledId = await helper.arrange.makeScheduledId();
-
-    const amount = 1n * helper.balance.getOneTokenNominal();
-
-    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic})
-      .balance.transferToSubstrate(alice, bob.address, amount);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-
-    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);
-
-    expect(bobsBalanceAfterFirst)
-      .to.be.equal(
-        bobsBalanceBefore + 1n * amount,
-        '#1 Balance of the recipient should be increased by 1 * amount',
-      );
-
-    await helper.scheduler.cancelScheduled(alice, scheduledId);
-    await helper.wait.newBlocks(periodic.period);
-
-    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);
-    expect(bobsBalanceAfterSecond)
-      .to.be.equal(
-        bobsBalanceAfterFirst,
-        '#2 Balance of the recipient should not be changed',
-      );
-  });
-
-  itSub.ifWithPallets('Scheduled tasks are transactional', [Pallets.Scheduler, Pallets.TestUtils], async ({helper}) => {
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    const initTestVal = 42;
-    const changedTestVal = 111;
-
-    await helper.testUtils.setTestValue(alice, initTestVal);
-
-    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks)
-      .testUtils.setTestValueAndRollback(alice, changedTestVal);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-
-    const testVal = await helper.testUtils.testValue();
-    expect(testVal, 'The test value should NOT be commited')
-      .to.be.equal(initTestVal);
-  });
-
-  itSub.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.Scheduler, Pallets.TestUtils], async function({helper}) {
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-    const periodic = {
-      period: 2,
-      repetitions: 2,
-    };
-
-    const dummyTx = helper.constructApiCall('api.tx.testUtils.justTakeFee', []);
-    const scheduledLen = dummyTx.callIndex.length;
-
-    const expectedScheduledFee = (await helper.getPaymentInfo(alice, dummyTx, scheduledLen))
-      .partialFee.toBigInt();
-
-    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks, {periodic})
-      .testUtils.justTakeFee(alice);
-
-    await helper.wait.newBlocks(1);
-
-    const aliceInitBalance = await helper.balance.getSubstrate(alice.address);
-    let diff;
-
-    await helper.wait.newBlocks(waitForBlocks);
-
-    const aliceBalanceAfterFirst = await helper.balance.getSubstrate(alice.address);
-    expect(
-      aliceBalanceAfterFirst < aliceInitBalance,
-      '[after execution #1] Scheduled task should take a fee',
-    ).to.be.true;
-
-    diff = aliceInitBalance - aliceBalanceAfterFirst;
-    expect(diff).to.be.equal(
-      expectedScheduledFee,
-      'Scheduled task should take the right amount of fees',
-    );
-
-    await helper.wait.newBlocks(periodic.period);
-
-    const aliceBalanceAfterSecond = await helper.balance.getSubstrate(alice.address);
-    expect(
-      aliceBalanceAfterSecond < aliceBalanceAfterFirst,
-      '[after execution #2] Scheduled task should take a fee',
-    ).to.be.true;
-
-    diff = aliceBalanceAfterFirst - aliceBalanceAfterSecond;
-    expect(diff).to.be.equal(
-      expectedScheduledFee,
-      'Scheduled task should take the right amount of fees',
-    );
-  });
-
-  // Check if we can cancel a scheduled periodic operation
-  // in the same block in which it is running
-  itSub.ifWithPallets('Can cancel the periodic sheduled tx when the tx is running', [Pallets.Scheduler, Pallets.TestUtils], async ({helper}) => {
-    const currentBlockNumber = await helper.chain.getLatestBlockNumber();
-    const blocksBeforeExecution = 10;
-    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;
-
-    const [
-      scheduledId,
-      scheduledCancelId,
-    ] = await helper.arrange.makeScheduledIds(2);
-
-    const periodic = {
-      period: 5,
-      repetitions: 5,
-    };
-
-    const initTestVal = 0;
-    const incTestVal = initTestVal + 1;
-    const finalTestVal = initTestVal + 2;
-
-    await helper.testUtils.setTestValue(alice, initTestVal);
-
-    await helper.scheduler.scheduleAt<DevUniqueHelper>(scheduledId, firstExecutionBlockNumber, {periodic})
-      .testUtils.incTestValue(alice);
-
-    // Cancel the inc tx after 2 executions
-    // *in the same block* in which the second execution is scheduled
-    await helper.scheduler.scheduleAt(
-      scheduledCancelId,
-      firstExecutionBlockNumber + periodic.period,
-    ).scheduler.cancelScheduled(alice, scheduledId);
-
-    await helper.wait.newBlocks(blocksBeforeExecution);
-
-    // execution #0
-    expect(await helper.testUtils.testValue())
-      .to.be.equal(incTestVal);
-
-    await helper.wait.newBlocks(periodic.period);
-
-    // execution #1
-    expect(await helper.testUtils.testValue())
-      .to.be.equal(finalTestVal);
-
-    for (let i = 1; i < periodic.repetitions; i++) {
-      await helper.wait.newBlocks(periodic.period);
-      expect(await helper.testUtils.testValue())
-        .to.be.equal(finalTestVal);
-    }
-  });
-
-  itSub.ifWithPallets('A scheduled operation can cancel itself', [Pallets.Scheduler, Pallets.TestUtils], async ({helper}) => {
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-    const periodic = {
-      period: 2,
-      repetitions: 5,
-    };
-
-    const initTestVal = 0;
-    const maxTestVal = 2;
-
-    await helper.testUtils.setTestValue(alice, initTestVal);
-
-    await helper.scheduler.scheduleAfter<DevUniqueHelper>(scheduledId, waitForBlocks, {periodic})
-      .testUtils.selfCancelingInc(alice, scheduledId, maxTestVal);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-
-    // execution #0
-    expect(await helper.testUtils.testValue())
-      .to.be.equal(initTestVal + 1);
-
-    await helper.wait.newBlocks(periodic.period);
-
-    // execution #1
-    expect(await helper.testUtils.testValue())
-      .to.be.equal(initTestVal + 2);
-
-    await helper.wait.newBlocks(periodic.period);
-
-    // <canceled>
-    expect(await helper.testUtils.testValue())
-      .to.be.equal(initTestVal + 2);
-  });
-
-  itSub.ifWithPallets('Root can cancel any scheduled operation', [Pallets.Scheduler], async ({helper}) => {
-    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(bob);
-
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    await token.scheduleAfter(scheduledId, waitForBlocks)
-      .transfer(bob, {Substrate: alice.address});
-
-    await helper.getSudo().scheduler.cancelScheduled(alice, scheduledId);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
-  });
-
-  itSub.ifWithPallets('Root can set prioritized scheduled operation', [Pallets.Scheduler], async ({helper}) => {
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    const amount = 42n * helper.balance.getOneTokenNominal();
-
-    const balanceBefore = await helper.balance.getSubstrate(charlie.address);
-
-    await helper.getSudo()
-      .scheduler.scheduleAfter(scheduledId, waitForBlocks, {priority: 42})
-      .balance.forceTransferToSubstrate(alice, bob.address, charlie.address, amount);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-
-    const balanceAfter = await helper.balance.getSubstrate(charlie.address);
-
-    expect(balanceAfter > balanceBefore).to.be.true;
-
-    const diff = balanceAfter - balanceBefore;
-    expect(diff).to.be.equal(amount);
-  });
-
-  itSub.ifWithPallets("Root can change scheduled operation's priority", [Pallets.Scheduler], async ({helper}) => {
-    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(bob);
-
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 6;
-
-    await token.scheduleAfter(scheduledId, waitForBlocks)
-      .transfer(bob, {Substrate: alice.address});
-
-    const priority = 112;
-    await helper.getSudo().scheduler.changePriority(alice, scheduledId, priority);
-
-    const priorityChanged = await helper.wait.event(
-      waitForBlocks,
-      'scheduler',
-      'PriorityChanged',
-    );
-
-    expect(priorityChanged !== null).to.be.true;
-    expect(priorityChanged!.event.data[2].toString()).to.be.equal(priority.toString());
-  });
-
-  itSub.ifWithPallets('Prioritized operations executes in valid order', [Pallets.Scheduler], async ({helper}) => {
-    const [
-      scheduledFirstId,
-      scheduledSecondId,
-    ] = await helper.arrange.makeScheduledIds(2);
-
-    const currentBlockNumber = await helper.chain.getLatestBlockNumber();
-    const blocksBeforeExecution = 4;
-    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;
-
-    const prioHigh = 0;
-    const prioLow = 255;
-
-    const periodic = {
-      period: 6,
-      repetitions: 2,
-    };
-
-    const amount = 1n * helper.balance.getOneTokenNominal();
-
-    // Scheduler a task with a lower priority first, then with a higher priority
-    await helper.getSudo().scheduler.scheduleAt(scheduledFirstId, firstExecutionBlockNumber, {priority: prioLow, periodic})
-      .balance.forceTransferToSubstrate(alice, alice.address, bob.address, amount);
-
-    await helper.getSudo().scheduler.scheduleAt(scheduledSecondId, firstExecutionBlockNumber, {priority: prioHigh, periodic})
-      .balance.forceTransferToSubstrate(alice, alice.address, bob.address, amount);
-
-    const capture = await helper.arrange.captureEvents('scheduler', 'Dispatched');
-
-    await helper.wait.newBlocks(blocksBeforeExecution);
-
-    // Flip priorities
-    await helper.getSudo().scheduler.changePriority(alice, scheduledFirstId, prioHigh);
-    await helper.getSudo().scheduler.changePriority(alice, scheduledSecondId, prioLow);
-
-    await helper.wait.newBlocks(periodic.period);
-
-    const dispatchEvents = capture.extractCapturedEvents();
-    expect(dispatchEvents.length).to.be.equal(4);
-
-    const dispatchedIds = dispatchEvents.map(r => r.event.data[1].toString());
-
-    const firstExecuctionIds = [dispatchedIds[0], dispatchedIds[1]];
-    const secondExecuctionIds = [dispatchedIds[2], dispatchedIds[3]];
-
-    expect(firstExecuctionIds[0]).to.be.equal(scheduledSecondId);
-    expect(firstExecuctionIds[1]).to.be.equal(scheduledFirstId);
-
-    expect(secondExecuctionIds[0]).to.be.equal(scheduledFirstId);
-    expect(secondExecuctionIds[1]).to.be.equal(scheduledSecondId);
-  });
-});
-
-describe('Negative Test: Scheduling', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKeyWrapper) => {
-      alice = await privateKeyWrapper('//Alice');
-      bob = await privateKeyWrapper('//Bob');
-
-      await helper.testUtils.enable();
-    });
-  });
-
-  itSub.ifWithPallets("Can't overwrite a scheduled ID", [Pallets.Scheduler], async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(alice);
-
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    await token.scheduleAfter(scheduledId, waitForBlocks)
-      .transfer(alice, {Substrate: bob.address});
-
-    const scheduled = helper.scheduler.scheduleAfter(scheduledId, waitForBlocks);
-    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, 1n * helper.balance.getOneTokenNominal()))
-      .to.be.rejectedWith(/scheduler\.FailedToSchedule/);
-
-    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-
-    const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
-    expect(bobsBalanceBefore).to.be.equal(bobsBalanceAfter);
-  });
-
-  itSub.ifWithPallets("Can't cancel an operation which is not scheduled", [Pallets.Scheduler], async ({helper}) => {
-    const scheduledId = await helper.arrange.makeScheduledId();
-    await expect(helper.scheduler.cancelScheduled(alice, scheduledId))
-      .to.be.rejectedWith(/scheduler\.NotFound/);
-  });
-
-  itSub.ifWithPallets("Can't cancel a non-owned scheduled operation", [Pallets.Scheduler], async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(alice);
-
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    await token.scheduleAfter(scheduledId, waitForBlocks)
-      .transfer(alice, {Substrate: bob.address});
-
-    await expect(helper.scheduler.cancelScheduled(bob, scheduledId))
-      .to.be.rejectedWith(/BadOrigin/);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
-  });
-
-  itSub.ifWithPallets("Regular user can't set prioritized scheduled operation", [Pallets.Scheduler], async ({helper}) => {
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    const amount = 42n * helper.balance.getOneTokenNominal();
-
-    const balanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    const scheduled = helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {priority: 42});
-    
-    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, amount))
-      .to.be.rejectedWith(/BadOrigin/);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-
-    const balanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(balanceAfter).to.be.equal(balanceBefore);
-  });
-
-  itSub.ifWithPallets("Regular user can't change scheduled operation's priority", [Pallets.Scheduler], async ({helper}) => {
-    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(bob);
-
-    const scheduledId = await helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    await token.scheduleAfter(scheduledId, waitForBlocks)
-      .transfer(bob, {Substrate: alice.address});
-
-    const priority = 112;
-    await expect(helper.scheduler.changePriority(alice, scheduledId, priority))
-      .to.be.rejectedWith(/BadOrigin/);
-
-    const priorityChanged = await helper.wait.event(
-      waitForBlocks,
-      'scheduler',
-      'PriorityChanged',
-    );
-
-    expect(priorityChanged === null).to.be.true;
-  });
-});
-
-// Implementation of the functionality tested here was postponed/shelved
-describe.skip('Sponsoring scheduling', () => {
-  // let alice: IKeyringPair;
-  // let bob: IKeyringPair;
-
-  // before(async() => {
-  //   await usingApi(async (_, privateKeyWrapper) => {
-  //     alice = privateKeyWrapper('//Alice');
-  //     bob = privateKeyWrapper('//Bob');
-  //   });
-  // });
-
-  it('Can sponsor scheduling a transaction', async () => {
-    // const collectionId = await createCollectionExpectSuccess();
-    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);
-    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
-
-    // await usingApi(async api => {
-    //   const scheduledId = await makeScheduledId();
-    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
-
-    //   const bobBalanceBefore = await getFreeBalance(bob);
-    //   const waitForBlocks = 4;
-    //   // no need to wait to check, fees must be deducted on scheduling, immediately
-    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, scheduledId);
-    //   const bobBalanceAfter = await getFreeBalance(bob);
-    //   // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;
-    //   expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
-    //   // wait for sequentiality matters
-    //   await waitNewBlocks(waitForBlocks - 1);
-    // });
-  });
-
-  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {
-    // await usingApi(async (api, privateKeyWrapper) => {
-    //   // Find an empty, unused account
-    //   const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
-
-    //   const collectionId = await createCollectionExpectSuccess();
-
-    //   // Add zeroBalance address to allow list
-    //   await enablePublicMintingExpectSuccess(alice, collectionId);
-    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
-
-    //   // Grace zeroBalance with money, enough to cover future transactions
-    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
-    //   await submitTransactionAsync(alice, balanceTx);
-
-    //   // Mint a fresh NFT
-    //   const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');
-    //   const scheduledId = await makeScheduledId();
-
-    //   // Schedule transfer of the NFT a few blocks ahead
-    //   const waitForBlocks = 5;
-    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, scheduledId);
-
-    //   // Get rid of the account's funds before the scheduled transaction takes place
-    //   const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);
-    //   const events = await submitTransactionAsync(zeroBalance, balanceTx2);
-    //   expect(getGenericResult(events).success).to.be.true;
-    //   /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?
-    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);
-    //   const events = await submitTransactionAsync(alice, sudoTx);
-    //   expect(getGenericResult(events).success).to.be.true;*/
-
-    //   // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions
-    //   await waitNewBlocks(waitForBlocks - 3);
-
-    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));
-    // });
-  });
-
-  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {
-    // const collectionId = await createCollectionExpectSuccess();
-
-    // await usingApi(async (api, privateKeyWrapper) => {
-    //   const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
-    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
-    //   await submitTransactionAsync(alice, balanceTx);
-
-    //   await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);
-    //   await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);
-
-    //   const scheduledId = await makeScheduledId();
-    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
-
-    //   const waitForBlocks = 5;
-    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, scheduledId);
-
-    //   const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);
-    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);
-    //   const events = await submitTransactionAsync(alice, sudoTx);
-    //   expect(getGenericResult(events).success).to.be.true;
-
-    //   // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions
-    //   await waitNewBlocks(waitForBlocks - 3);
-
-    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));
-    // });
-  });
-
-  it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {
-    // const collectionId = await createCollectionExpectSuccess();
-    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);
-    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
-
-    // await usingApi(async (api, privateKeyWrapper) => {
-    //   const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
-
-    //   await enablePublicMintingExpectSuccess(alice, collectionId);
-    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
-
-    //   const bobBalanceBefore = await getFreeBalance(bob);
-
-    //   const createData = {nft: {const_data: [], variable_data: []}};
-    //   const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any);
-    //   const scheduledId = await makeScheduledId();
-
-    //   /*const badTransaction = async function () {
-    //     await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
-    //   };
-    //   await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/
-
-    //   await expect(scheduleAfter(api, creationTx, zeroBalance, 3, scheduledId, 1, 3)).to.be.rejectedWith(/Inability to pay some fees/);
-
-    //   expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);
-    // });
-  });
-});
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -422,8 +422,8 @@
     return promise;
   }
 
-  async forParachainBlockNumber(blockNumber: bigint, timeout?: number) {
-    timeout = timeout ?? 300_000;
+  async forParachainBlockNumber(blockNumber: bigint | number, timeout?: number) {
+    timeout = timeout ?? 30 * 60 * 1000;
     // eslint-disable-next-line no-async-promise-executor
     const promise = new Promise<void>(async (resolve) => {
       const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => {
@@ -437,8 +437,8 @@
     return promise;
   }
   
-  async forRelayBlockNumber(blockNumber: bigint, timeout?: number) {
-    timeout = timeout ?? 300_000;
+  async forRelayBlockNumber(blockNumber: bigint | number, timeout?: number) {
+    timeout = timeout ?? 30 * 60 * 1000;
     // eslint-disable-next-line no-async-promise-executor
     const promise = new Promise<void>(async (resolve) => {
       const unsubscribe = await this.helper.getApi().query.parachainSystem.validationData((data: any) => {