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

difftreelog

tests(eth): NFT, RFT, FT tests refactored for playgrounds

Fahrrader2022-09-27parent: #edc6ac8.patch.diff
in: master

6 files changed

modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -87,8 +87,11 @@
     "testLimits": "mocha --timeout 9999999 -r ts-node/register ./**/limits.test.ts",
     "testEthCreateNFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createNFTCollection.test.ts",
     "testEthCreateRFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createRFTCollection.test.ts",
+    "testEthNFT": "mocha --timeout 9999999 -r ts-node/register ./**/eth/nonFungible.test.ts",
     "testRFT": "mocha --timeout 9999999 -r ts-node/register ./**/refungible.test.ts",
+    "testEthRFT": "mocha --timeout 9999999 -r ts-node/register ./**/eth/reFungible.test.ts ./**/eth/reFungibleToken.test.ts",
     "testFT": "mocha --timeout 9999999 -r ts-node/register ./**/fungible.test.ts",
+    "testEthFT": "mocha --timeout 9999999 -r ts-node/register ./**/eth/fungible.test.ts",
     "testRPC": "mocha --timeout 9999999 -r ts-node/register ./**/rpc.test.ts",
     "testPromotion": "mocha --timeout 9999999 -r ts-node/register ./**/app-promotion.test.ts",
     "polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json",
modifiedtests/src/eth/fungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -14,204 +14,132 @@
 // 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 {approveExpectSuccess, createCollection, createCollectionExpectSuccess, createFungibleItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, UNIQUE} from '../util/helpers';
-import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, transferBalanceToEth} from './util/helpers';
-import fungibleAbi from './fungibleAbi.json';
-import {expect} from 'chai';
-import {submitTransactionAsync} from '../substrate/substrate-api';
+import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds';
+import {IKeyringPair} from '@polkadot/types/types';
 
 describe('Fungible: Information getting', () => {
-  itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'Fungible', decimalPoints: 0},
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice] = await helper.arrange.createAccounts([20n], donor);
     });
-    const alice = privateKeyWrapper('//Alice');
+  });
 
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  itEth('totalSupply', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n);
 
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Substrate: alice.address});
-
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'ft', caller);
     const totalSupply = await contract.methods.totalSupply().call();
-
     expect(totalSupply).to.equal('200');
   });
 
-  itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
-    const alice = privateKeyWrapper('//Alice');
+  itEth('balanceOf', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: caller});
 
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: caller});
-
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'ft', caller);
     const balance = await contract.methods.balanceOf(caller).call();
-
     expect(balance).to.equal('200');
   });
 });
 
 describe('Fungible: Plain calls', () => {
-  itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {
-    const alice = privateKeyWrapper('//Alice');
-    const collection = await createCollection(api, alice, {
-      name: 'token name',
-      mode: {type: 'Fungible', decimalPoints: 0},
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice] = await helper.arrange.createAccounts([20n], donor);
     });
+  });
 
-    const receiver = createEthAccount(web3);
+  itEth('Can perform mint()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.addAdmin(alice, {Ethereum: owner});
 
-    const collectionIdAddress = collectionIdToAddress(collection.collectionId);
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const changeAdminTx = api.tx.unique.addCollectionAdmin(collection.collectionId, {Ethereum: owner});
-    await submitTransactionAsync(alice, changeAdminTx);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
 
-    const collectionContract = evmCollection(web3, owner, collectionIdAddress, {type: 'Fungible', decimalPoints: 0});
-    const result = await collectionContract.methods.mint(receiver, 100).send();
-    const events = normalizeEvents(result.events);
+    const result = await contract.methods.mint(receiver, 100).send();
     
-    expect(events).to.be.deep.equal([
-      {
-        address: collectionIdAddress,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: receiver,
-          value: '100',
-        },
-      },
-    ]);
+    const event = result.events.Transfer;
+    expect(event.address).to.equal(collectionAddress);
+    expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.equal(receiver);
+    expect(event.returnValues.value).to.equal('100');
   });
-
-  itWeb3('Can perform mintBulk()', async ({web3, api, privateKeyWrapper}) => {
-    const alice = privateKeyWrapper('//Alice');
-    const collection = await createCollection(api, alice, {
-      name: 'token name',
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
 
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const receiver1 = createEthAccount(web3);
-    const receiver2 = createEthAccount(web3);
-    const receiver3 = createEthAccount(web3);
-
-    const collectionIdAddress = collectionIdToAddress(collection.collectionId);
-    const changeAdminTx = api.tx.unique.addCollectionAdmin(collection.collectionId, {Ethereum: owner});
-    await submitTransactionAsync(alice, changeAdminTx);
+  itEth('Can perform mintBulk()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const bulkSize = 3;
+    const receivers = [...new Array(bulkSize)].map(() => helper.eth.createAccount());
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.addAdmin(alice, {Ethereum: owner});
 
-    const collectionContract = evmCollection(web3, owner, collectionIdAddress, {type: 'Fungible', decimalPoints: 0});
-    const result = await collectionContract.methods.mintBulk([
-      [receiver1, 10],
-      [receiver2, 20],
-      [receiver3, 30],
-    ]).send();
-    const events = normalizeEvents(result.events);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
 
-    expect(events).to.be.deep.contain({
-      address:collectionIdAddress,
-      event: 'Transfer',
-      args: {
-        from: '0x0000000000000000000000000000000000000000',
-        to: receiver1,
-        value: '10',
-      },
-    });
-    
-    expect(events).to.be.deep.contain({
-      address:collectionIdAddress,
-      event: 'Transfer',
-      args: {
-        from: '0x0000000000000000000000000000000000000000',
-        to: receiver2,
-        value: '20',
-      },
-    });
-    
-    expect(events).to.be.deep.contain({
-      address:collectionIdAddress,
-      event: 'Transfer',
-      args: {
-        from: '0x0000000000000000000000000000000000000000',
-        to: receiver3,
-        value: '30',
-      },
-    });
+    const result = await contract.methods.mintBulk(Array.from({length: bulkSize}, (_, i) => (
+      [receivers[i], (i + 1) * 10]
+    ))).send();
+    const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.value - b.returnValues.value);
+    for (let i = 0; i < bulkSize; i++) {
+      const event = events[i];
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.equal(receivers[i]);
+      expect(event.returnValues.value).to.equal(String(10 * (i + 1)));
+    }
   });
 
-  itWeb3('Can perform burn()', async ({web3, api, privateKeyWrapper}) => {
-    const alice = privateKeyWrapper('//Alice');
-    const collection = await createCollection(api, alice, {
-      name: 'token name',
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
-
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const changeAdminTx = api.tx.unique.addCollectionAdmin(collection.collectionId, {Ethereum: owner});
-    await submitTransactionAsync(alice, changeAdminTx);
-    const receiver = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  itEth('Can perform burn()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.addAdmin(alice, {Ethereum: owner});
 
-    const collectionIdAddress = collectionIdToAddress(collection.collectionId);
-    const collectionContract = evmCollection(web3, owner, collectionIdAddress, {type: 'Fungible', decimalPoints: 0});
-    await collectionContract.methods.mint(receiver, 100).send();
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+    await contract.methods.mint(receiver, 100).send();
 
-    const result = await collectionContract.methods.burnFrom(receiver, 49).send({from: receiver});
+    const result = await contract.methods.burnFrom(receiver, 49).send({from: receiver});
     
-    const events = normalizeEvents(result.events);
-
-    expect(events).to.be.deep.equal([
-      {
-        address: collectionIdAddress,
-        event: 'Transfer',
-        args: {
-          from: receiver,
-          to: '0x0000000000000000000000000000000000000000',
-          value: '49',
-        },
-      },
-    ]);
+    const event = result.events.Transfer;
+    expect(event.address).to.equal(collectionAddress);
+    expect(event.returnValues.from).to.equal(receiver);
+    expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.value).to.equal('49');
 
-    const balance = await collectionContract.methods.balanceOf(receiver).call();
+    const balance = await contract.methods.balanceOf(receiver).call();
     expect(balance).to.equal('51');
   });
 
-  itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
-    const alice = privateKeyWrapper('//Alice');
+  itEth('Can perform approve()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
 
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
-
-    const spender = createEthAccount(web3);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
-
     {
       const result = await contract.methods.approve(spender, 100).send({from: owner});
-      const events = normalizeEvents(result.events);
 
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Approval',
-          args: {
-            owner,
-            spender,
-            value: '100',
-          },
-        },
-      ]);
+      const event = result.events.Approval;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.spender).to.be.equal(spender);
+      expect(event.returnValues.value).to.be.equal('100');
     }
 
     {
@@ -220,51 +148,32 @@
     }
   });
 
-  itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
-    const alice = privateKeyWrapper('//Alice');
-
-    const owner = createEthAccount(web3);
-    await transferBalanceToEth(api, alice, owner);
-
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
-
-    const spender = createEthAccount(web3);
-    await transferBalanceToEth(api, alice, spender);
-
-    const receiver = createEthAccount(web3);
+  itEth('Can perform transferFrom()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
 
     await contract.methods.approve(spender, 100).send();
 
     {
       const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});
-      const events = normalizeEvents(result.events);
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: owner,
-            to: receiver,
-            value: '49',
-          },
-        },
-        {
-          address,
-          event: 'Approval',
-          args: {
-            owner,
-            spender,
-            value: '51',
-          },
-        },
-      ]);
+      
+      let event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(owner);
+      expect(event.returnValues.to).to.be.equal(receiver);
+      expect(event.returnValues.value).to.be.equal('49');
+
+      event = result.events.Approval;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.spender).to.be.equal(spender);
+      expect(event.returnValues.value).to.be.equal('51');
     }
 
     {
@@ -278,38 +187,23 @@
     }
   });
 
-  itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
-    const alice = privateKeyWrapper('//Alice');
-
-    const owner = createEthAccount(web3);
-    await transferBalanceToEth(api, alice, owner);
-
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
-
-    const receiver = createEthAccount(web3);
-    await transferBalanceToEth(api, alice, receiver);
+  itEth('Can perform transfer()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
 
     {
       const result = await contract.methods.transfer(receiver, 50).send({from: owner});
-      const events = normalizeEvents(result.events);
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: owner,
-            to: receiver,
-            value: '50',
-          },
-        },
-      ]);
+      
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(owner);
+      expect(event.returnValues.to).to.be.equal(receiver);
+      expect(event.returnValues.value).to.be.equal('50');
     }
 
     {
@@ -325,162 +219,141 @@
 });
 
 describe('Fungible: Fees', () => {
-  itWeb3('approve() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'Fungible', decimalPoints: 0},
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice] = await helper.arrange.createAccounts([20n], donor);
     });
-    const alice = privateKeyWrapper('//Alice');
+  });
+  
+  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
 
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const spender = createEthAccount(web3);
-
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
-
-    const cost = await recordEthFee(api, owner, () => contract.methods.approve(spender, 100).send({from: owner}));
-    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
   });
 
-  itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
-    const alice = privateKeyWrapper('//Alice');
-
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const spender = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
+  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
 
     await contract.methods.approve(spender, 100).send({from: owner});
 
-    const cost = await recordEthFee(api, spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
-    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
   });
-
-  itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
-    const alice = privateKeyWrapper('//Alice');
 
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const receiver = createEthAccount(web3);
-
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
+  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
 
-    const cost = await recordEthFee(api, owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
-    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
   });
 });
 
 describe('Fungible: Substrate calls', () => {
-  itWeb3('Events emitted for approve()', async ({web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'Fungible', decimalPoints: 0},
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice] = await helper.arrange.createAccounts([20n], donor);
     });
-    const alice = privateKeyWrapper('//Alice');
+  });
 
-    const receiver = createEthAccount(web3);
+  itEth('Events emitted for approve()', async ({helper}) => {
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n);
 
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address);
-
-    const events = await recordEvents(contract, async () => {
-      await approveExpectSuccess(collection, 0, alice, {Ethereum: receiver}, 100);
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
     });
+    await collection.approveTokens(alice, {Ethereum: receiver}, 100n);
 
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Approval',
-        args: {
-          owner: subToEth(alice.address),
-          spender: receiver,
-          value: '100',
-        },
-      },
-    ]);
+    const event = events[0];
+    expect(event.event).to.be.equal('Approval');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.spender).to.be.equal(receiver);
+    expect(event.returnValues.value).to.be.equal('100');
   });
 
-  itWeb3('Events emitted for transferFrom()', async ({web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
-    const alice = privateKeyWrapper('//Alice');
-    const bob = privateKeyWrapper('//Bob');
+  itEth('Events emitted for transferFrom()', async ({helper}) => {
+    const [bob] = await helper.arrange.createAccounts([10n], donor);
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n);
+    await collection.approveTokens(alice, {Substrate: bob.address}, 100n);
 
-    const receiver = createEthAccount(web3);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');
 
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n});
-    await approveExpectSuccess(collection, 0, alice, bob.address, 100);
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+    await collection.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n);
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address);
+    let event = events[0];
+    expect(event.event).to.be.equal('Transfer');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.value).to.be.equal('51');
 
-    const events = await recordEvents(contract, async () => {
-      await transferFromExpectSuccess(collection, 0, bob, alice, {Ethereum: receiver}, 51, 'Fungible');
-    });
-
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: subToEth(alice.address),
-          to: receiver,
-          value: '51',
-        },
-      },
-      {
-        address,
-        event: 'Approval',
-        args: {
-          owner: subToEth(alice.address),
-          spender: subToEth(bob.address),
-          value: '49',
-        },
-      },
-    ]);
+    event = events[1];
+    expect(event.event).to.be.equal('Approval');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));
+    expect(event.returnValues.value).to.be.equal('49');
   });
 
-  itWeb3('Events emitted for transfer()', async ({web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
-    const alice = privateKeyWrapper('//Alice');
+  itEth('Events emitted for transfer()', async ({helper}) => {
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n);
 
-    const receiver = createEthAccount(web3);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'ft');
 
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n});
-
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address);
-
-    const events = await recordEvents(contract, async () => {
-      await transferExpectSuccess(collection, 0, alice, {Ethereum:receiver}, 51, 'Fungible');
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
     });
+    await collection.transfer(alice, {Ethereum:receiver}, 51n);
 
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: subToEth(alice.address),
-          to: receiver,
-          value: '51',
-        },
-      },
-    ]);
+    const event = events[0];
+    expect(event.event).to.be.equal('Transfer');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.value).to.be.equal('51');
   });
 });
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -14,72 +14,78 @@
 // 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 {approveExpectSuccess, burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, UNIQUE} from '../util/helpers';
-import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, evmCollectionHelpers, GAS_ARGS, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, transferBalanceToEth} from './util/helpers';
-import nonFungibleAbi from './nonFungibleAbi.json';
-import {expect} from 'chai';
-import {submitTransactionAsync} from '../substrate/substrate-api';
+import {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util/playgrounds';
+import {IKeyringPair} from '@polkadot/types/types';
+import {Contract} from 'web3-eth-contract';
 
 describe('NFT: Information getting', () => {
-  itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice] = await helper.arrange.createAccounts([10n], donor);
     });
-    const alice = privateKeyWrapper('//Alice');
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  });
+  
+  itEth('totalSupply', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    await collection.mintToken(alice);
 
-    await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});
+    const caller = await helper.eth.createAccountWithBalance(donor);
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
     const totalSupply = await contract.methods.totalSupply().call();
 
     expect(totalSupply).to.equal('1');
   });
 
-  itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
+  itEth('balanceOf', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    const caller = await helper.eth.createAccountWithBalance(donor);
 
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum:caller});
-    await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
-    await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
+    await collection.mintToken(alice, {Ethereum: caller});
+    await collection.mintToken(alice, {Ethereum: caller});
+    await collection.mintToken(alice, {Ethereum: caller});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
     const balance = await contract.methods.balanceOf(caller).call();
 
     expect(balance).to.equal('3');
   });
 
-  itWeb3('ownerOf', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
+  itEth('ownerOf', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    const caller = await helper.eth.createAccountWithBalance(donor);
 
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
+    const token = await collection.mintToken(alice, {Ethereum: caller});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
-    const owner = await contract.methods.ownerOf(tokenId).call();
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
+
+    const owner = await contract.methods.ownerOf(token.tokenId).call();
 
     expect(owner).to.equal(caller);
   });
 });
 
 describe('Check ERC721 token URI for NFT', () => {
-  itWeb3('Empty tokenURI', async ({web3, api, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, owner);
-    let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', '').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const receiver = createEthAccount(web3);
-    const contract = evmCollection(web3, owner, collectionIdAddress);
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = privateKey('//Alice');
+    });
+  });
+
+  async function setup(helper: EthUniqueHelper, tokenPrefix: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+    let result = await collectionHelper.methods.createERC721MetadataCompatibleCollection('Mint collection', 'a', 'b', tokenPrefix).send();
+    const collectionAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
     
     const nextTokenId = await contract.methods.nextTokenId().call();
     expect(nextTokenId).to.be.equal('1');
@@ -88,162 +94,73 @@
       nextTokenId,
     ).send();
 
-    const events = normalizeEvents(result.events);
-    const address = collectionIdToAddress(collectionId);
+    if (propertyKey && propertyValue) {
+      // Set URL or suffix
+      await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();
+    }
 
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: receiver,
-          tokenId: nextTokenId,
-        },
-      },
-    ]);
+    const event = result.events.Transfer;
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.tokenId).to.be.equal(nextTokenId);
 
+    return {contract, nextTokenId};
+  }
+
+  itEth('Empty tokenURI', async ({helper}) => {
+    const {contract, nextTokenId} = await setup(helper, '');
     expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');
   });
 
-  itWeb3('TokenURI from url', async ({web3, api, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, owner);
-    let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const receiver = createEthAccount(web3);
-    const contract = evmCollection(web3, owner, collectionIdAddress);
-    
-    const nextTokenId = await contract.methods.nextTokenId().call();
-    expect(nextTokenId).to.be.equal('1');
-    result = await contract.methods.mint(
-      receiver,
-      nextTokenId,
-    ).send();
-    
-    // Set URL
-    await contract.methods.setProperty(nextTokenId, 'url', Buffer.from('Token URI')).send();
-      
-    const events = normalizeEvents(result.events);
-    const address = collectionIdToAddress(collectionId);
-
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: receiver,
-          tokenId: nextTokenId,
-        },
-      },
-    ]);
-
+  itEth('TokenURI from url', async ({helper}) => {
+    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'url', 'Token URI');
     expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');
   });
 
-  itWeb3('TokenURI from baseURI + tokenId', async ({web3, api, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, owner);
-    let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const receiver = createEthAccount(web3);
-    const contract = evmCollection(web3, owner, collectionIdAddress);
-    
-    const nextTokenId = await contract.methods.nextTokenId().call();
-    expect(nextTokenId).to.be.equal('1');
-    result = await contract.methods.mint(
-      receiver,
-      nextTokenId,
-    ).send();
-          
-    const events = normalizeEvents(result.events);
-    const address = collectionIdToAddress(collectionId);
-
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: receiver,
-          tokenId: nextTokenId,
-        },
-      },
-    ]);
-
+  itEth('TokenURI from baseURI + tokenId', async ({helper}) => {
+    const {contract, nextTokenId} = await setup(helper, 'BaseURI_');
     expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + nextTokenId);
   });
 
-  itWeb3('TokenURI from baseURI + suffix', async ({web3, api, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, owner);
-    let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const receiver = createEthAccount(web3);
-    const contract = evmCollection(web3, owner, collectionIdAddress);
-    
-    const nextTokenId = await contract.methods.nextTokenId().call();
-    expect(nextTokenId).to.be.equal('1');
-    result = await contract.methods.mint(
-      receiver,
-      nextTokenId,
-    ).send();
-          
-    // Set suffix
+  itEth('TokenURI from baseURI + suffix', async ({helper}) => {
     const suffix = '/some/suffix';
-    await contract.methods.setProperty(nextTokenId, 'suffix', Buffer.from(suffix)).send();
-
-    const events = normalizeEvents(result.events);
-    const address = collectionIdToAddress(collectionId);
-
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: receiver,
-          tokenId: nextTokenId,
-        },
-      },
-    ]);
-
+    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'suffix', suffix);
     expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);
   });
 });
 
 describe('NFT: Plain calls', () => {
-  itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, owner);
-    let result = await helper.methods.createNonfungibleCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const receiver = createEthAccount(web3);
-    const contract = evmCollection(web3, owner, collectionIdAddress);
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+
+  itEth('Can perform mint()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Minty', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
     const nextTokenId = await contract.methods.nextTokenId().call();
 
     expect(nextTokenId).to.be.equal('1');
-    result = await contract.methods.mintWithTokenURI(
+    const result = await contract.methods.mintWithTokenURI(
       receiver,
       nextTokenId,
       'Test URI',
     ).send();
 
-    const events = normalizeEvents(result.events);
-    const address = collectionIdToAddress(collectionId);
-
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: receiver,
-          tokenId: nextTokenId,
-        },
-      },
-    ]);
+    const event = result.events.Transfer;
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.tokenId).to.be.equal(nextTokenId);
 
     expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
 
@@ -254,7 +171,8 @@
   });
 
   //TODO: CORE-302 add eth methods
-  itWeb3.skip('Can perform mintBulk()', async ({web3, api, privateKeyWrapper}) => {
+  /* todo:playgrounds skipped test!
+  itWeb3.skip('Can perform mintBulk()', async ({helper}) => {
     const collection = await createCollectionExpectSuccess({
       mode: {type: 'NFT'},
     });
@@ -316,107 +234,70 @@
       expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');
     }
   });
-
-  itWeb3('Can perform burn()', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
+  */
 
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  itEth('Can perform burn()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
 
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+    const collection = await helper.nft.mintCollection(alice, {});
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: caller});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
 
     {
-      const result = await contract.methods.burn(tokenId).send({from: owner});
-      const events = normalizeEvents(result.events);
-
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: owner,
-            to: '0x0000000000000000000000000000000000000000',
-            tokenId: tokenId.toString(),
-          },
-        },
-      ]);
+      const result = await contract.methods.burn(tokenId).send({from: caller});
+      
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(caller);
+      expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
     }
   });
 
-  itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
-
-    const owner = createEthAccount(web3);
-    await transferBalanceToEth(api, alice, owner);
-
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+  itEth('Can perform approve()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
 
-    const spender = createEthAccount(web3);
+    const collection = await helper.nft.mintCollection(alice, {});
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
 
     {
-      const result = await contract.methods.approve(spender, tokenId).send({from: owner, ...GAS_ARGS});
-      const events = normalizeEvents(result.events);
+      const result = await contract.methods.approve(spender, tokenId).send({from: owner});
 
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Approval',
-          args: {
-            owner,
-            approved: spender,
-            tokenId: tokenId.toString(),
-          },
-        },
-      ]);
+      const event = result.events.Approval;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.approved).to.be.equal(spender);
+      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
     }
   });
 
-  itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
-
-    const owner = createEthAccount(web3);
-    await transferBalanceToEth(api, alice, owner);
+  itEth('Can perform transferFrom()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
 
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
-
-    const spender = createEthAccount(web3);
-    await transferBalanceToEth(api, alice, spender);
-
-    const receiver = createEthAccount(web3);
+    const collection = await helper.nft.mintCollection(alice, {});
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
 
     await contract.methods.approve(spender, tokenId).send({from: owner});
 
     {
       const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender});
-      const events = normalizeEvents(result.events);
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: owner,
-            to: receiver,
-            tokenId: tokenId.toString(),
-          },
-        },
-      ]);
+
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(owner);
+      expect(event.returnValues.to).to.be.equal(receiver);
+      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
     }
 
     {
@@ -430,37 +311,24 @@
     }
   });
 
-  itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
-
-    const owner = createEthAccount(web3);
-    await transferBalanceToEth(api, alice, owner);
-
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+  itEth('Can perform transfer()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
 
-    const receiver = createEthAccount(web3);
-    await transferBalanceToEth(api, alice, receiver);
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
 
     {
       const result = await contract.methods.transfer(receiver, tokenId).send({from: owner});
-      const events = normalizeEvents(result.events);
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: owner,
-            to: receiver,
-            tokenId: tokenId.toString(),
-          },
-        },
-      ]);
+
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(owner);
+      expect(event.returnValues.to).to.be.equal(receiver);
+      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
     }
 
     {
@@ -476,237 +344,209 @@
 });
 
 describe('NFT: Fees', () => {
-  itWeb3('approve() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice] = await helper.arrange.createAccounts([10n], donor);
     });
-    const alice = privateKeyWrapper('//Alice');
+  });
+  
+  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
 
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const spender = createEthAccount(web3);
+    const collection = await helper.nft.mintCollection(alice, {});
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
 
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
-
-    const cost = await recordEthFee(api, owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));
-    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
   });
 
-  itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
-
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const spender = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
 
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+    const collection = await helper.nft.mintCollection(alice, {});
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
 
     await contract.methods.approve(spender, tokenId).send({from: owner});
 
-    const cost = await recordEthFee(api, spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));
-    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
   });
 
-  itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
+  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
 
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const receiver = createEthAccount(web3);
+    const collection = await helper.nft.mintCollection(alice, {});
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
 
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
-
-    const cost = await recordEthFee(api, owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));
-    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
   });
 });
 
 describe('NFT: Substrate calls', () => {
-  itWeb3('Events emitted for mint()', async ({web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice] = await helper.arrange.createAccounts([20n], donor);
     });
-    const alice = privateKeyWrapper('//Alice');
+  });
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+  itEth('Events emitted for mint()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
 
-    let tokenId: number;
-    const events = await recordEvents(contract, async () => {
-      tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
     });
+    const {tokenId} = await collection.mintToken(alice);
 
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: subToEth(alice.address),
-          tokenId: tokenId!.toString(),
-        },
-      },
-    ]);
+    const event = events[0];
+    expect(event.event).to.be.equal('Transfer');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.tokenId).to.be.equal(tokenId.toString());
   });
 
-  itWeb3('Events emitted for burn()', async ({web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
+  itEth('Events emitted for burn()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice);
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+    
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
     });
-    const alice = privateKeyWrapper('//Alice');
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+    await token.burn(alice);
 
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
-    const events = await recordEvents(contract, async () => {
-      await burnItemExpectSuccess(alice, collection, tokenId);
-    });
-
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: subToEth(alice.address),
-          to: '0x0000000000000000000000000000000000000000',
-          tokenId: tokenId.toString(),
-        },
-      },
-    ]);
+    const event = events[0];
+    expect(event.event).to.be.equal('Transfer');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());
   });
 
-  itWeb3('Events emitted for approve()', async ({web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
+  itEth('Events emitted for approve()', async ({helper}) => {
+    const receiver = helper.eth.createAccount();
 
-    const receiver = createEthAccount(web3);
-
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
-
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+    const collection = await helper.nft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice);
 
-    const events = await recordEvents(contract, async () => {
-      await approveExpectSuccess(collection, tokenId, alice, {Ethereum: receiver}, 1);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+    
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
     });
 
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Approval',
-        args: {
-          owner: subToEth(alice.address),
-          approved: receiver,
-          tokenId: tokenId.toString(),
-        },
-      },
-    ]);
+    await token.approve(alice, {Ethereum: receiver});
+
+    const event = events[0];
+    expect(event.event).to.be.equal('Approval');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.approved).to.be.equal(receiver);
+    expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());
   });
 
-  itWeb3('Events emitted for transferFrom()', async ({web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
-    const bob = privateKeyWrapper('//Bob');
+  itEth('Events emitted for transferFrom()', async ({helper}) => {
+    const [bob] = await helper.arrange.createAccounts([10n], donor);
+    const receiver = helper.eth.createAccount();
 
-    const receiver = createEthAccount(web3);
-
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
-    await approveExpectSuccess(collection, tokenId, alice, bob.address, 1);
-
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
+    const collection = await helper.nft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice);
+    await token.approve(alice, {Substrate: bob.address});
 
-    const events = await recordEvents(contract, async () => {
-      await transferFromExpectSuccess(collection, tokenId, bob, alice, {Ethereum: receiver}, 1, 'NFT');
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+    
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
     });
 
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: subToEth(alice.address),
-          to: receiver,
-          tokenId: tokenId.toString(),
-        },
-      },
-    ]);
+    await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver});
+    
+    const event = events[0];
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);
   });
 
-  itWeb3('Events emitted for transfer()', async ({web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
+  itEth('Events emitted for transfer()', async ({helper}) => {
+    const receiver = helper.eth.createAccount();
 
-    const receiver = createEthAccount(web3);
+    const collection = await helper.nft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice);
 
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
-
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
-
-    const events = await recordEvents(contract, async () => {
-      await transferExpectSuccess(collection, tokenId, alice, {Ethereum: receiver}, 1, 'NFT');
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+    
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
     });
 
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: subToEth(alice.address),
-          to: receiver,
-          tokenId: tokenId.toString(),
-        },
-      },
-    ]);
+    await token.transfer(alice, {Ethereum: receiver});
+    
+    const event = events[0];
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);
   });
 });
 
 describe('Common metadata', () => {
-  itWeb3('Returns collection name', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'NFT'},
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice] = await helper.arrange.createAccounts([20n], donor);
     });
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  });
+
+  itEth('Returns collection name', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE'});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
     const name = await contract.methods.name().call();
-
-    expect(name).to.equal('token name');
+    expect(name).to.equal('oh River');
   });
 
-  itWeb3('Returns symbol name', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      tokenPrefix: 'TOK',
-      mode: {type: 'NFT'},
-    });
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  itEth('Returns symbol name', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE'});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
     const symbol = await contract.methods.symbol().call();
-
-    expect(symbol).to.equal('TOK');
+    expect(symbol).to.equal('CHANGE');
   });
 });
\ No newline at end of file
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -14,33 +14,35 @@
 // 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 {createCollectionExpectSuccess, UNIQUE, requirePallets, Pallets} from '../util/helpers';
-import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, evmCollectionHelpers, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, tokenIdToAddress, uniqueRefungibleToken} from './util/helpers';
-import {expect} from 'chai';
+import {Pallets, requirePalletsOrSkip} from '../util/playgrounds';
+import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds';
+import {IKeyringPair} from '@polkadot/types/types';
 
 describe('Refungible: Information getting', () => {
+  let donor: IKeyringPair;
+
   before(async function() {
-    await requirePallets(this, [Pallets.ReFungible]);
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = privateKey('//Alice');
+    });
   });
 
-  itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+  itEth('totalSupply', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'TotalSupply', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
     const nextTokenId = await contract.methods.nextTokenId().call();
     await contract.methods.mint(caller, nextTokenId).send();
     const totalSupply = await contract.methods.totalSupply().call();
     expect(totalSupply).to.equal('1');
   });
 
-  itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+  itEth('balanceOf', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'BalanceOf', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     {
       const nextTokenId = await contract.methods.nextTokenId().call();
@@ -56,38 +58,30 @@
     }
 
     const balance = await contract.methods.balanceOf(caller).call();
-
     expect(balance).to.equal('3');
   });
 
-  itWeb3('ownerOf', async ({api, web3, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+  itEth('ownerOf', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'OwnerOf', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     const tokenId = await contract.methods.nextTokenId().call();
     await contract.methods.mint(caller, tokenId).send();
 
     const owner = await contract.methods.ownerOf(tokenId).call();
-
     expect(owner).to.equal(caller);
   });
 
-  itWeb3('ownerOf after burn', async ({api, web3, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const receiver = createEthAccount(web3);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+  itEth('ownerOf after burn', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'OwnerOf-AfterBurn', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     const tokenId = await contract.methods.nextTokenId().call();
     await contract.methods.mint(caller, tokenId).send();
-
-    const tokenAddress = tokenIdToAddress(collectionId, tokenId);
-    const tokenContract = uniqueRefungibleToken(web3, tokenAddress, caller);
+    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
 
     await tokenContract.methods.repartition(2).send();
     await tokenContract.methods.transfer(receiver, 1).send();
@@ -95,80 +89,67 @@
     await tokenContract.methods.burnFrom(caller, 1).send();
 
     const owner = await contract.methods.ownerOf(tokenId).call();
-
     expect(owner).to.equal(receiver);
   });
 
-  itWeb3('ownerOf for partial ownership', async ({api, web3, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const receiver = createEthAccount(web3);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+  itEth('ownerOf for partial ownership', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Partial-OwnerOf', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     const tokenId = await contract.methods.nextTokenId().call();
     await contract.methods.mint(caller, tokenId).send();
-
-    const tokenAddress = tokenIdToAddress(collectionId, tokenId);
-    const tokenContract = uniqueRefungibleToken(web3, tokenAddress, caller);
+    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
 
     await tokenContract.methods.repartition(2).send();
     await tokenContract.methods.transfer(receiver, 1).send();
 
     const owner = await contract.methods.ownerOf(tokenId).call();
-
     expect(owner).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
   });
 });
 
 describe('Refungible: Plain calls', () => {
+  let donor: IKeyringPair;
+
   before(async function() {
-    await requirePallets(this, [Pallets.ReFungible]);
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = privateKey('//Alice');
+    });
   });
 
-  itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, owner);
-    let result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const receiver = createEthAccount(web3);
-    const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
+  itEth('Can perform mint()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Minty', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+    
     const nextTokenId = await contract.methods.nextTokenId().call();
-
     expect(nextTokenId).to.be.equal('1');
-    result = await contract.methods.mintWithTokenURI(
+    const result = await contract.methods.mintWithTokenURI(
       receiver,
       nextTokenId,
       'Test URI',
     ).send();
 
-    const events = normalizeEvents(result.events);
-
-    expect(events).to.include.deep.members([
-      {
-        address: collectionIdAddress,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: receiver,
-          tokenId: nextTokenId,
-        },
-      },
-    ]);
+    const event = result.events.Transfer;
+    expect(event.address).to.equal(collectionAddress);
+    expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.equal(receiver);
+    expect(event.returnValues.tokenId).to.equal(nextTokenId);
 
     expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
   });
 
-  itWeb3('Can perform mintBulk()', async ({web3, api, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+  itEth('Can perform mintBulk()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'MintBulky', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
 
-    const receiver = createEthAccount(web3);
-
     {
       const nextTokenId = await contract.methods.nextTokenId().call();
       expect(nextTokenId).to.be.equal('1');
@@ -180,37 +161,15 @@
           [+nextTokenId + 2, 'Test URI 2'],
         ],
       ).send();
-      const events = normalizeEvents(result.events);
 
-      expect(events).to.include.deep.members([
-        {
-          address: collectionIdAddress,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: receiver,
-            tokenId: nextTokenId,
-          },
-        },
-        {
-          address: collectionIdAddress,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: receiver,
-            tokenId: String(+nextTokenId + 1),
-          },
-        },
-        {
-          address: collectionIdAddress,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: receiver,
-            tokenId: String(+nextTokenId + 2),
-          },
-        },
-      ]);
+      const events = result.events.Transfer;
+      for (let i = 0; i < 2; i++) {
+        const event = events[i];
+        expect(event.address).to.equal(collectionAddress);
+        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+        expect(event.returnValues.to).to.equal(receiver);
+        expect(event.returnValues.tokenId).to.equal(String(+nextTokenId + i));
+      }
 
       expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');
       expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');
@@ -218,76 +177,54 @@
     }
   });
 
-  itWeb3('Can perform burn()', async ({web3, api, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+  itEth('Can perform burn()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Burny', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     const tokenId = await contract.methods.nextTokenId().call();
     await contract.methods.mint(caller, tokenId).send();
     {
       const result = await contract.methods.burn(tokenId).send();
-      const events = normalizeEvents(result.events);
-      expect(events).to.include.deep.members([
-        {
-          address: collectionIdAddress,
-          event: 'Transfer',
-          args: {
-            from: caller,
-            to: '0x0000000000000000000000000000000000000000',
-            tokenId: tokenId.toString(),
-          },
-        },
-      ]);
+      const event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal(caller);
+      expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.tokenId).to.equal(tokenId.toString());
     }
   });
 
-  itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
-
-    const receiver = createEthAccount(web3);
+  itEth('Can perform transferFrom()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'TransferFromy', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     const tokenId = await contract.methods.nextTokenId().call();
+    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
     await contract.methods.mint(caller, tokenId).send();
 
-    const address = tokenIdToAddress(collectionId, tokenId);
-    const tokenContract = uniqueRefungibleToken(web3, address, caller);
+    const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);
     await tokenContract.methods.repartition(15).send();
 
     {
-      const erc20Events = await recordEvents(tokenContract, async () => {
-        const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();
-        const events = normalizeEvents(result.events);
-        expect(events).to.include.deep.members([
-          {
-            address: collectionIdAddress,
-            event: 'Transfer',
-            args: {
-              from: caller,
-              to: receiver,
-              tokenId: tokenId.toString(),
-            },
-          },
-        ]);
+      const tokenEvents: any = [];
+      tokenContract.events.allEvents((_: any, event: any) => {
+        tokenEvents.push(event);
       });
+      const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();
+
+      let event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal(caller);
+      expect(event.returnValues.to).to.equal(receiver);
+      expect(event.returnValues.tokenId).to.equal(tokenId.toString());
 
-      expect(erc20Events).to.include.deep.members([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: caller,
-            to: receiver,
-            value: '15',
-          },
-        },
-      ]);
+      event = tokenEvents[0];
+      expect(event.address).to.equal(tokenAddress);
+      expect(event.returnValues.from).to.equal(caller);
+      expect(event.returnValues.to).to.equal(receiver);
+      expect(event.returnValues.value).to.equal('15');
     }
 
     {
@@ -301,32 +238,23 @@
     }
   });
 
-  itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
-
-    const receiver = createEthAccount(web3);
+  itEth('Can perform transfer()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     const tokenId = await contract.methods.nextTokenId().call();
     await contract.methods.mint(caller, tokenId).send();
 
     {
       const result = await contract.methods.transfer(receiver, tokenId).send();
-      const events = normalizeEvents(result.events);
-      expect(events).to.include.deep.members([
-        {
-          address: collectionIdAddress,
-          event: 'Transfer',
-          args: {
-            from: caller,
-            to: receiver,
-            tokenId: tokenId.toString(),
-          },
-        },
-      ]);
+      
+      const event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal(caller);
+      expect(event.returnValues.to).to.equal(receiver);
+      expect(event.returnValues.tokenId).to.equal(tokenId.toString());
     }
 
     {
@@ -340,141 +268,127 @@
     }
   });
 
-  itWeb3('transfer event on transfer from partial ownership to full ownership', async ({api, web3, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const receiver = createEthAccount(web3);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+  itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry-Partial-to-Full', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     const tokenId = await contract.methods.nextTokenId().call();
     await contract.methods.mint(caller, tokenId).send();
 
-    const tokenAddress = tokenIdToAddress(collectionId, tokenId);
-    const tokenContract = uniqueRefungibleToken(web3, tokenAddress, caller);
+    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
 
     await tokenContract.methods.repartition(2).send();
     await tokenContract.methods.transfer(receiver, 1).send();
 
-    const events =  await recordEvents(contract, async () =>
-      await tokenContract.methods.transfer(receiver, 1).send());
-    expect(events).to.deep.equal([
-      {
-        address: collectionIdAddress,
-        event: 'Transfer',
-        args: {
-          from: '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF',
-          to: receiver,
-          tokenId: tokenId.toString(),
-        },
-      },
-    ]);
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+    await tokenContract.methods.transfer(receiver, 1).send();
+
+    const event = events[0];
+    expect(event.address).to.equal(collectionAddress);
+    expect(event.returnValues.from).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+    expect(event.returnValues.to).to.equal(receiver);
+    expect(event.returnValues.tokenId).to.equal(tokenId.toString());
   });
 
-  itWeb3('transfer event on transfer from full ownership to partial ownership', async ({api, web3, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const receiver = createEthAccount(web3);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
+  itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Transferry-Full-to-Partial', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     const tokenId = await contract.methods.nextTokenId().call();
     await contract.methods.mint(caller, tokenId).send();
 
-    const tokenAddress = tokenIdToAddress(collectionId, tokenId);
-    const tokenContract = uniqueRefungibleToken(web3, tokenAddress, caller);
+    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
 
     await tokenContract.methods.repartition(2).send();
 
-    const events =  await recordEvents(contract, async () =>
-      await tokenContract.methods.transfer(receiver, 1).send());
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+    await tokenContract.methods.transfer(receiver, 1).send();
 
-    expect(events).to.deep.equal([
-      {
-        address: collectionIdAddress,
-        event: 'Transfer',
-        args: {
-          from: caller,
-          to: '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF',
-          tokenId: tokenId.toString(),
-        },
-      },
-    ]);
+    const event = events[0];
+    expect(event.address).to.equal(collectionAddress);
+    expect(event.returnValues.from).to.equal(caller);
+    expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+    expect(event.returnValues.tokenId).to.equal(tokenId.toString());
   });
 });
 
 describe('RFT: Fees', () => {
+  let donor: IKeyringPair;
+
   before(async function() {
-    await requirePallets(this, [Pallets.ReFungible]);
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = privateKey('//Alice');
+    });
   });
 
-  itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
-
-    const receiver = createEthAccount(web3);
+  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Feeful-Transfer-From', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     const tokenId = await contract.methods.nextTokenId().call();
     await contract.methods.mint(caller, tokenId).send();
 
-    const cost = await recordEthFee(api, caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());
-    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+    const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
     expect(cost > 0n);
   });
 
-  itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, caller);
-    const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
-
-    const receiver = createEthAccount(web3);
+  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Feeful-Transfer', '6', '6');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
 
     const tokenId = await contract.methods.nextTokenId().call();
     await contract.methods.mint(caller, tokenId).send();
 
-    const cost = await recordEthFee(api, caller, () => contract.methods.transfer(receiver, tokenId).send());
-    expect(cost < BigInt(0.2 * Number(UNIQUE)));
+    const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send());
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
     expect(cost > 0n);
   });
 });
 
 describe('Common metadata', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
   before(async function() {
-    await requirePallets(this, [Pallets.ReFungible]);
-  });
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
 
-  itWeb3('Returns collection name', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'ReFungible'},
+      donor = privateKey('//Alice');
+      [alice] = await helper.arrange.createAccounts([20n], donor);
     });
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  });
 
-    const address = collectionIdToAddress(collection);
-    const contract = evmCollection(web3, caller, address, {type: 'ReFungible'});
+  itEth('Returns collection name', async ({helper}) => {
+    const caller = helper.eth.createAccount();
+    const collection = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '11'});
+    
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);
     const name = await contract.methods.name().call();
-
-    expect(name).to.equal('token name');
+    expect(name).to.equal('Leviathan');
   });
 
-  itWeb3('Returns symbol name', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      tokenPrefix: 'TOK',
-      mode: {type: 'ReFungible'},
-    });
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-
-    const address = collectionIdToAddress(collection);
-    const contract = evmCollection(web3, caller, address, {type: 'ReFungible'});
+  itEth('Returns symbol name', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Leviathan', '', '12');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
     const symbol = await contract.methods.symbol().call();
-
-    expect(symbol).to.equal('TOK');
+    expect(symbol).to.equal('12');
   });
 });
modifiedtests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {approve, createCollection, createRefungibleToken, transfer, transferFrom, UNIQUE, requirePallets, Pallets} from '../util/helpers';17import {Pallets, requirePalletsOrSkip} from '../util/playgrounds';
18import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, createRFTCollection, evmCollection, evmCollectionHelpers, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, tokenIdToAddress, transferBalanceToEth, uniqueRefungible, uniqueRefungibleToken} from './util/helpers';18import {EthUniqueHelper, expect, itEth, usingEthPlaygrounds} from './util/playgrounds';
19import {IKeyringPair} from '@polkadot/types/types';
20import {Contract} from 'web3-eth-contract';
1921
20import chai from 'chai';
21import chaiAsPromised from 'chai-as-promised';
22chai.use(chaiAsPromised);
23const expect = chai.expect;
24
25describe('Refungible token: Information getting', () => {22describe('Refungible token: Information getting', () => {
23 let donor: IKeyringPair;
24 let alice: IKeyringPair;
25
26 before(async function() {26 before(async function() {
27 await requirePallets(this, [Pallets.ReFungible]);27 await usingEthPlaygrounds(async (helper, privateKey) => {
28 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
29
30 donor = privateKey('//Alice');
31 [alice] = await helper.arrange.createAccounts([20n], donor);
32 });
28 });33 });
2934
30 itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {35 itEth('totalSupply', async ({helper}) => {
31 const alice = privateKeyWrapper('//Alice');36 const caller = await helper.eth.createAccountWithBalance(donor);
37 const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});
38 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});
3239
33 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;40 const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);
34
35 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
36
37 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;
38
39 const address = tokenIdToAddress(collectionId, tokenId);
40 const contract = uniqueRefungibleToken(web3, address, caller);
41 const totalSupply = await contract.methods.totalSupply().call();41 const totalSupply = await contract.methods.totalSupply().call();
42
43 expect(totalSupply).to.equal('200');42 expect(totalSupply).to.equal('200');
44 });43 });
4544
46 itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {45 itEth('balanceOf', async ({helper}) => {
47 const alice = privateKeyWrapper('//Alice');46 const caller = await helper.eth.createAccountWithBalance(donor);
47 const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});
48 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});
4849
49 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;50 const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);
50
51 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
52
53 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;
54
55 const address = tokenIdToAddress(collectionId, tokenId);
56 const contract = uniqueRefungibleToken(web3, address, caller);
57 const balance = await contract.methods.balanceOf(caller).call();51 const balance = await contract.methods.balanceOf(caller).call();
58
59 expect(balance).to.equal('200');52 expect(balance).to.equal('200');
60 });53 });
6154
62 itWeb3('decimals', async ({api, web3, privateKeyWrapper}) => {55 itEth('decimals', async ({helper}) => {
63 const alice = privateKeyWrapper('//Alice');56 const caller = await helper.eth.createAccountWithBalance(donor);
57 const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});
58 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});
6459
65 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;60 const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);
66
67 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
68
69 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;
70
71 const address = tokenIdToAddress(collectionId, tokenId);
72 const contract = uniqueRefungibleToken(web3, address, caller);
73 const decimals = await contract.methods.decimals().call();61 const decimals = await contract.methods.decimals().call();
74
75 expect(decimals).to.equal('0');62 expect(decimals).to.equal('0');
76 });63 });
77});64});
7865
79// FIXME: Need erc721 for ReFubgible.66// FIXME: Need erc721 for ReFubgible.
80describe('Check ERC721 token URI for ReFungible', () => {67describe('Check ERC721 token URI for ReFungible', () => {
68 let donor: IKeyringPair;
69
81 before(async function() {70 before(async function() {
82 await requirePallets(this, [Pallets.ReFungible]);71 await usingEthPlaygrounds(async (helper, privateKey) => {
72 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
73
74 donor = privateKey('//Alice');
75 });
83 });76 });
8477
85 itWeb3('Empty tokenURI', async ({web3, api, privateKeyWrapper}) => {78 async function setup(helper: EthUniqueHelper, tokenPrefix: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {
86 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
87 const helper = evmCollectionHelpers(web3, owner);79 const owner = await helper.eth.createAccountWithBalance(donor);
88 let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', '').send();
89 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);80 const receiver = helper.eth.createAccount();
90 const receiver = createEthAccount(web3);
91 const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
9281
82 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
83 let result = await collectionHelper.methods.createERC721MetadataCompatibleCollection('Mint collection', 'a', 'b', tokenPrefix).send();
84 const collectionAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
85 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
86
93 const nextTokenId = await contract.methods.nextTokenId().call();87 const nextTokenId = await contract.methods.nextTokenId().call();
94 expect(nextTokenId).to.be.equal('1');88 expect(nextTokenId).to.be.equal('1');
95 result = await contract.methods.mint(89 result = await contract.methods.mint(
96 receiver,90 receiver,
97 nextTokenId,91 nextTokenId,
98 ).send();92 ).send();
9993
100 const events = normalizeEvents(result.events);94 if (propertyKey && propertyValue) {
101 const address = collectionIdToAddress(collectionId);95 // Set URL or suffix
96 await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();
97 }
10298
103 expect(events).to.be.deep.equal([99 const event = result.events.Transfer;
100 expect(event.address).to.be.equal(collectionAddress);
104 {101 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
105 address,
106 event: 'Transfer',
107 args: {
108 from: '0x0000000000000000000000000000000000000000',
109 to: receiver,102 expect(event.returnValues.to).to.be.equal(receiver);
110 tokenId: nextTokenId,103 expect(event.returnValues.tokenId).to.be.equal(nextTokenId);
111 },
112 },
113 ]);
114104
105 return {contract, nextTokenId};
106 }
107
108 itEth('Empty tokenURI', async ({helper}) => {
109 const {contract, nextTokenId} = await setup(helper, '');
115 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');110 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');
116 });111 });
117112
118 itWeb3('TokenURI from url', async ({web3, api, privateKeyWrapper}) => {113 itEth('TokenURI from url', async ({helper}) => {
119 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);114 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'url', 'Token URI');
120 const helper = evmCollectionHelpers(web3, owner);
121 let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
122 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
123 const receiver = createEthAccount(web3);
124 const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
125
126 const nextTokenId = await contract.methods.nextTokenId().call();
127 expect(nextTokenId).to.be.equal('1');
128 result = await contract.methods.mint(
129 receiver,
130 nextTokenId,
131 ).send();
132
133 // Set URL
134 await contract.methods.setProperty(nextTokenId, 'url', Buffer.from('Token URI')).send();
135
136 const events = normalizeEvents(result.events);
137 const address = collectionIdToAddress(collectionId);
138
139 expect(events).to.be.deep.equal([
140 {
141 address,
142 event: 'Transfer',
143 args: {
144 from: '0x0000000000000000000000000000000000000000',
145 to: receiver,
146 tokenId: nextTokenId,
147 },
148 },
149 ]);
150
151 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');115 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');
152 });116 });
153117
154 itWeb3('TokenURI from baseURI + tokenId', async ({web3, api, privateKeyWrapper}) => {118 itEth('TokenURI from baseURI + tokenId', async ({helper}) => {
155 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);119 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');
156 const helper = evmCollectionHelpers(web3, owner);
157 let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
158 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
159 const receiver = createEthAccount(web3);
160 const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
161
162 const nextTokenId = await contract.methods.nextTokenId().call();
163 expect(nextTokenId).to.be.equal('1');
164 result = await contract.methods.mint(
165 receiver,
166 nextTokenId,
167 ).send();
168
169 const events = normalizeEvents(result.events);
170 const address = collectionIdToAddress(collectionId);
171
172 expect(events).to.be.deep.equal([
173 {
174 address,
175 event: 'Transfer',
176 args: {
177 from: '0x0000000000000000000000000000000000000000',
178 to: receiver,
179 tokenId: nextTokenId,
180 },
181 },
182 ]);
183
184 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + nextTokenId);120 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + nextTokenId);
185 });121 });
186122
187 itWeb3('TokenURI from baseURI + suffix', async ({web3, api, privateKeyWrapper}) => {123 itEth('TokenURI from baseURI + suffix', async ({helper}) => {
188 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
189 const helper = evmCollectionHelpers(web3, owner);
190 let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
191 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
192 const receiver = createEthAccount(web3);
193 const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
194
195 const nextTokenId = await contract.methods.nextTokenId().call();
196 expect(nextTokenId).to.be.equal('1');
197 result = await contract.methods.mint(
198 receiver,
199 nextTokenId,
200 ).send();
201
202 // Set suffix
203 const suffix = '/some/suffix';124 const suffix = '/some/suffix';
204 await contract.methods.setProperty(nextTokenId, 'suffix', Buffer.from(suffix)).send();125 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'suffix', suffix);
205
206 const events = normalizeEvents(result.events);
207 const address = collectionIdToAddress(collectionId);
208
209 expect(events).to.be.deep.equal([
210 {
211 address,
212 event: 'Transfer',
213 args: {
214 from: '0x0000000000000000000000000000000000000000',
215 to: receiver,
216 tokenId: nextTokenId,
217 },
218 },
219 ]);
220
221 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);126 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);
222 });127 });
223});128});
224129
225describe('Refungible: Plain calls', () => {130describe('Refungible: Plain calls', () => {
131 let donor: IKeyringPair;
132 let alice: IKeyringPair;
133
226 before(async function() {134 before(async function() {
227 await requirePallets(this, [Pallets.ReFungible]);135 await usingEthPlaygrounds(async (helper, privateKey) => {
136 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
137
138 donor = privateKey('//Alice');
139 [alice] = await helper.arrange.createAccounts([50n], donor);
140 });
228 });141 });
229142
230 itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {143 itEth('Can perform approve()', async ({helper}) => {
231 const alice = privateKeyWrapper('//Alice');144 const owner = await helper.eth.createAccountWithBalance(donor);
145 const spender = helper.eth.createAccount();
146 const collection = await helper.rft.mintCollection(alice);
147 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
232148
233 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;149 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
150 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
234151
235 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
236
237 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
238
239 const address = tokenIdToAddress(collectionId, tokenId);
240
241 const spender = createEthAccount(web3);
242
243 const contract = uniqueRefungibleToken(web3, address, owner);
244
245 {152 {
246 const result = await contract.methods.approve(spender, 100).send({from: owner});153 const result = await contract.methods.approve(spender, 100).send({from: owner});
247 const events = normalizeEvents(result.events);154 const event = result.events.Approval;
248
249 expect(events).to.be.deep.equal([155 expect(event.address).to.be.equal(tokenAddress);
250 {156 expect(event.returnValues.owner).to.be.equal(owner);
251 address,
252 event: 'Approval',
253 args: {
254 owner,
255 spender,157 expect(event.returnValues.spender).to.be.equal(spender);
256 value: '100',158 expect(event.returnValues.value).to.be.equal('100');
257 },
258 },
259 ]);
260 }159 }
261160
262 {161 {
265 }164 }
266 });165 });
267166
268 itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {167 itEth('Can perform transferFrom()', async ({helper}) => {
269 const alice = privateKeyWrapper('//Alice');168 const owner = await helper.eth.createAccountWithBalance(donor);
169 const spender = await helper.eth.createAccountWithBalance(donor);
170 const receiver = helper.eth.createAccount();
171 const collection = await helper.rft.mintCollection(alice);
172 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
270173
271 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;174 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
175 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
272176
273 const owner = createEthAccount(web3);
274 await transferBalanceToEth(api, alice, owner);
275
276 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
277
278 const spender = createEthAccount(web3);
279 await transferBalanceToEth(api, alice, spender);
280
281 const receiver = createEthAccount(web3);
282
283 const address = tokenIdToAddress(collectionId, tokenId);
284 const contract = uniqueRefungibleToken(web3, address, owner);
285
286 await contract.methods.approve(spender, 100).send();177 await contract.methods.approve(spender, 100).send();
287178
288 {179 {
289 const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});180 const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});
290 const events = normalizeEvents(result.events);181 let event = result.events.Transfer;
291 expect(events).to.include.deep.members([182 expect(event.address).to.be.equal(tokenAddress);
292 {183 expect(event.returnValues.from).to.be.equal(owner);
293 address,
294 event: 'Transfer',
295 args: {
296 from: owner,
297 to: receiver,184 expect(event.returnValues.to).to.be.equal(receiver);
298 value: '49',185 expect(event.returnValues.value).to.be.equal('49');
299 },186
300 },187 event = result.events.Approval;
301 {188 expect(event.address).to.be.equal(tokenAddress);
302 address,
303 event: 'Approval',189 expect(event.returnValues.owner).to.be.equal(owner);
304 args: {
305 owner,
306 spender,190 expect(event.returnValues.spender).to.be.equal(spender);
307 value: '51',191 expect(event.returnValues.value).to.be.equal('51');
308 },
309 },
310 ]);
311 }192 }
312193
313 {194 {
321 }202 }
322 });203 });
323204
324 itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {205 itEth('Can perform transfer()', async ({helper}) => {
325 const alice = privateKeyWrapper('//Alice');206 const owner = await helper.eth.createAccountWithBalance(donor);
207 const receiver = helper.eth.createAccount();
208 const collection = await helper.rft.mintCollection(alice);
209 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
326210
327 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;211 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
212 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
328213
329 const owner = createEthAccount(web3);
330 await transferBalanceToEth(api, alice, owner);
331
332 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
333
334 const receiver = createEthAccount(web3);
335 await transferBalanceToEth(api, alice, receiver);
336
337 const address = tokenIdToAddress(collectionId, tokenId);
338 const contract = uniqueRefungibleToken(web3, address, owner);
339
340 {214 {
341 const result = await contract.methods.transfer(receiver, 50).send({from: owner});215 const result = await contract.methods.transfer(receiver, 50).send({from: owner});
342 const events = normalizeEvents(result.events);216 const event = result.events.Transfer;
343 expect(events).to.include.deep.members([217 expect(event.address).to.be.equal(tokenAddress);
344 {218 expect(event.returnValues.from).to.be.equal(owner);
345 address,
346 event: 'Transfer',
347 args: {
348 from: owner,
349 to: receiver,219 expect(event.returnValues.to).to.be.equal(receiver);
350 value: '50',220 expect(event.returnValues.value).to.be.equal('50');
351 },
352 },
353 ]);
354 }221 }
355222
356 {223 {
364 }231 }
365 });232 });
366233
367 itWeb3('Can perform repartition()', async ({web3, api, privateKeyWrapper}) => {234 itEth('Can perform repartition()', async ({helper}) => {
368 const alice = privateKeyWrapper('//Alice');235 const owner = await helper.eth.createAccountWithBalance(donor);
236 const receiver = await helper.eth.createAccountWithBalance(donor);
237 const collection = await helper.rft.mintCollection(alice);
238 const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
369239
370 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;240 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
241 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
371242
372 const owner = createEthAccount(web3);
373 await transferBalanceToEth(api, alice, owner);
374
375 const receiver = createEthAccount(web3);
376 await transferBalanceToEth(api, alice, receiver);
377
378 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n, {Ethereum: owner})).itemId;
379
380 const address = tokenIdToAddress(collectionId, tokenId);
381 const contract = uniqueRefungibleToken(web3, address, owner);
382
383 await contract.methods.repartition(200).send({from: owner});243 await contract.methods.repartition(200).send({from: owner});
384 expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(200);244 expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(200);
385 await contract.methods.transfer(receiver, 110).send({from: owner});245 await contract.methods.transfer(receiver, 110).send({from: owner});
386 expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(90);246 expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(90);
387 expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(110);247 expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(110);
388248
389 await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected;249 await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected; // Transaction is reverted
390250
391 await contract.methods.transfer(receiver, 90).send({from: owner});251 await contract.methods.transfer(receiver, 90).send({from: owner});
392 expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(0);252 expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(0);
393 expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(200);253 expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(200);
394254
395 await contract.methods.repartition(150).send({from: receiver});255 await contract.methods.repartition(150).send({from: receiver});
396 await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected;256 await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected; // Transaction is reverted
397 expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(150);257 expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(150);
398 });258 });
399259
400 itWeb3('Can repartition with increased amount', async ({web3, api, privateKeyWrapper}) => {260 itEth('Can repartition with increased amount', async ({helper}) => {
401 const alice = privateKeyWrapper('//Alice');261 const owner = await helper.eth.createAccountWithBalance(donor);
262 const collection = await helper.rft.mintCollection(alice);
263 const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
402264
403 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;265 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
266 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
404267
405 const owner = createEthAccount(web3);
406 await transferBalanceToEth(api, alice, owner);
407
408 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n, {Ethereum: owner})).itemId;
409
410 const address = tokenIdToAddress(collectionId, tokenId);
411 const contract = uniqueRefungibleToken(web3, address, owner);
412
413 const result = await contract.methods.repartition(200).send();268 const result = await contract.methods.repartition(200).send();
414 const events = normalizeEvents(result.events);
415269
416 expect(events).to.deep.equal([270 const event = result.events.Transfer;
271 expect(event.address).to.be.equal(tokenAddress);
417 {272 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
418 address,
419 event: 'Transfer',
420 args: {
421 from: '0x0000000000000000000000000000000000000000',
422 to: owner,273 expect(event.returnValues.to).to.be.equal(owner);
423 value: '100',274 expect(event.returnValues.value).to.be.equal('100');
424 },
425 },
426 ]);
427 });275 });
428276
429 itWeb3('Can repartition with decreased amount', async ({web3, api, privateKeyWrapper}) => {277 itEth('Can repartition with decreased amount', async ({helper}) => {
430 const alice = privateKeyWrapper('//Alice');278 const owner = await helper.eth.createAccountWithBalance(donor);
279 const collection = await helper.rft.mintCollection(alice);
280 const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
431281
432 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;282 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
283 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
433284
434 const owner = createEthAccount(web3);
435 await transferBalanceToEth(api, alice, owner);
436
437 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n, {Ethereum: owner})).itemId;
438
439 const address = tokenIdToAddress(collectionId, tokenId);
440 const contract = uniqueRefungibleToken(web3, address, owner);
441
442 const result = await contract.methods.repartition(50).send();285 const result = await contract.methods.repartition(50).send();
443 const events = normalizeEvents(result.events);286 const event = result.events.Transfer;
444 expect(events).to.deep.equal([287 expect(event.address).to.be.equal(tokenAddress);
445 {288 expect(event.returnValues.from).to.be.equal(owner);
446 address,
447 event: 'Transfer',
448 args: {
449 from: owner,
450 to: '0x0000000000000000000000000000000000000000',289 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
451 value: '50',290 expect(event.returnValues.value).to.be.equal('50');
452 },
453 },
454 ]);
455 });291 });
456292
457 itWeb3('Receiving Transfer event on burning into full ownership', async ({web3, api, privateKeyWrapper}) => {293 itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => {
458 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);294 const caller = await helper.eth.createAccountWithBalance(donor);
459 const receiver = await createEthAccountWithBalance(api, web3, privateKeyWrapper);295 const receiver = await helper.eth.createAccountWithBalance(donor);
460 const helper = evmCollectionHelpers(web3, caller);296 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(caller, 'Devastation', '6', '6');
461 const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send();
462 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);297 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
463 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
464298
465 const tokenId = await contract.methods.nextTokenId().call();299 const tokenId = await contract.methods.nextTokenId().call();
466 await contract.methods.mint(caller, tokenId).send();300 await contract.methods.mint(caller, tokenId).send();
301 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
302 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);
467303
468 const address = tokenIdToAddress(collectionId, tokenId);
469
470 const tokenContract = uniqueRefungibleToken(web3, address, caller);
471 await tokenContract.methods.repartition(2).send();304 await tokenContract.methods.repartition(2).send();
472 await tokenContract.methods.transfer(receiver, 1).send();305 await tokenContract.methods.transfer(receiver, 1).send();
473306
474 const events = await recordEvents(contract, async () =>307 const events: any = [];
308 contract.events.allEvents((_: any, event: any) => {
475 await tokenContract.methods.burnFrom(caller, 1).send());309 events.push(event);
476 expect(events).to.deep.equal([310 });
311 await tokenContract.methods.burnFrom(caller, 1).send();
312
477 {313 const event = events[0];
478 address: collectionIdAddress,
479 event: 'Transfer',314 expect(event.address).to.be.equal(collectionAddress);
480 args: {315 expect(event.returnValues.from).to.be.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
481 from: '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF',
482 to: receiver,316 expect(event.returnValues.to).to.be.equal(receiver);
483 tokenId,317 expect(event.returnValues.tokenId).to.be.equal(tokenId);
484 },
485 },
486 ]);
487 });318 });
488});319});
489320
490describe('Refungible: Fees', () => {321describe('Refungible: Fees', () => {
322 let donor: IKeyringPair;
323 let alice: IKeyringPair;
324
491 before(async function() {325 before(async function() {
492 await requirePallets(this, [Pallets.ReFungible]);326 await usingEthPlaygrounds(async (helper, privateKey) => {
327 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
328
329 donor = privateKey('//Alice');
330 [alice] = await helper.arrange.createAccounts([50n], donor);
331 });
493 });332 });
494333
495 itWeb3('approve() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {334 itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
496 const alice = privateKeyWrapper('//Alice');335 const owner = await helper.eth.createAccountWithBalance(donor);
336 const spender = helper.eth.createAccount();
337 const collection = await helper.rft.mintCollection(alice);
338 const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
497339
498 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;340 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
341 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
499342
500 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);343 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));
501 const spender = createEthAccount(web3);
502
503 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
504
505 const address = tokenIdToAddress(collectionId, tokenId);
506 const contract = uniqueRefungibleToken(web3, address, owner);
507
508 const cost = await recordEthFee(api, owner, () => contract.methods.approve(spender, 100).send({from: owner}));
509 expect(cost < BigInt(0.2 * Number(UNIQUE)));344 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
510 });345 });
511346
512 itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {347 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
513 const alice = privateKeyWrapper('//Alice');348 const owner = await helper.eth.createAccountWithBalance(donor);
349 const spender = await helper.eth.createAccountWithBalance(donor);
350 const collection = await helper.rft.mintCollection(alice);
351 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
514352
515 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;353 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
354 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
516355
517 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
518 const spender = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
519
520 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
521
522 const address = tokenIdToAddress(collectionId, tokenId);
523 const contract = uniqueRefungibleToken(web3, address, owner);
524
525 await contract.methods.approve(spender, 100).send({from: owner});356 await contract.methods.approve(spender, 100).send({from: owner});
526357
527 const cost = await recordEthFee(api, spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));358 const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
528 expect(cost < BigInt(0.2 * Number(UNIQUE)));359 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
529 });360 });
530361
531 itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {362 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
532 const alice = privateKeyWrapper('//Alice');363 const owner = await helper.eth.createAccountWithBalance(donor);
364 const receiver = helper.eth.createAccount();
365 const collection = await helper.rft.mintCollection(alice);
366 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
533367
534 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;368 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
369 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);
535370
536 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);371 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
537 const receiver = createEthAccount(web3);
538
539 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;
540
541 const address = tokenIdToAddress(collectionId, tokenId);
542 const contract = uniqueRefungibleToken(web3, address, owner);
543
544 const cost = await recordEthFee(api, owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
545 expect(cost < BigInt(0.2 * Number(UNIQUE)));372 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
546 });373 });
547});374});
548375
549describe('Refungible: Substrate calls', () => {376describe('Refungible: Substrate calls', () => {
377 let donor: IKeyringPair;
378 let alice: IKeyringPair;
379
550 before(async function() {380 before(async function() {
551 await requirePallets(this, [Pallets.ReFungible]);381 await usingEthPlaygrounds(async (helper, privateKey) => {
382 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
383
384 donor = privateKey('//Alice');
385 [alice] = await helper.arrange.createAccounts([50n], donor);
386 });
552 });387 });
553388
554 itWeb3('Events emitted for approve()', async ({web3, api, privateKeyWrapper}) => {389 itEth('Events emitted for approve()', async ({helper}) => {
555 const alice = privateKeyWrapper('//Alice');390 const receiver = helper.eth.createAccount();
391 const collection = await helper.rft.mintCollection(alice);
392 const token = await collection.mintToken(alice, 200n);
556393
557 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;394 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
395 const contract = helper.ethNativeContract.rftToken(tokenAddress);
558396
559 const receiver = createEthAccount(web3);397 const events: any = [];
560
561 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;
562
563 const address = tokenIdToAddress(collectionId, tokenId);398 contract.events.allEvents((_: any, event: any) => {
564 const contract = uniqueRefungibleToken(web3, address);
565
566 const events = await recordEvents(contract, async () => {
567 expect(await approve(api, collectionId, tokenId, alice, {Ethereum: receiver}, 100n)).to.be.true;399 events.push(event);
568 });400 });
401 expect(await token.approve(alice, {Ethereum: receiver}, 100n)).to.be.true;
569402
570 expect(events).to.be.deep.equal([403 const event = events[0];
571 {
572 address,
573 event: 'Approval',404 expect(event.event).to.be.equal('Approval');
574 args: {405 expect(event.address).to.be.equal(tokenAddress);
575 owner: subToEth(alice.address),406 expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
576 spender: receiver,407 expect(event.returnValues.spender).to.be.equal(receiver);
577 value: '100',408 expect(event.returnValues.value).to.be.equal('100');
578 },
579 },
580 ]);
581 });409 });
582410
583 itWeb3('Events emitted for transferFrom()', async ({web3, api, privateKeyWrapper}) => {411 itEth('Events emitted for transferFrom()', async ({helper}) => {
584 const alice = privateKeyWrapper('//Alice');412 const [bob] = await helper.arrange.createAccounts([10n], donor);
413 const receiver = helper.eth.createAccount();
414 const collection = await helper.rft.mintCollection(alice);
415 const token = await collection.mintToken(alice, 200n);
416 await token.approve(alice, {Substrate: bob.address}, 100n);
585417
586 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;418 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
587 const bob = privateKeyWrapper('//Bob');419 const contract = helper.ethNativeContract.rftToken(tokenAddress);
588420
589 const receiver = createEthAccount(web3);421 const events: any = [];
422 contract.events.allEvents((_: any, event: any) => {
423 events.push(event);
424 });
590425
591 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;426 expect(await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n)).to.be.true;
592 expect(await approve(api, collectionId, tokenId, alice, bob.address, 100n)).to.be.true;
593427
594 const address = tokenIdToAddress(collectionId, tokenId);428 let event = events[0];
429 expect(event.event).to.be.equal('Transfer');
430 expect(event.address).to.be.equal(tokenAddress);
595 const contract = uniqueRefungibleToken(web3, address);431 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
432 expect(event.returnValues.to).to.be.equal(receiver);
433 expect(event.returnValues.value).to.be.equal('51');
596434
597 const events = await recordEvents(contract, async () => {435 event = events[1];
598 expect(await transferFrom(api, collectionId, tokenId, bob, alice, {Ethereum: receiver}, 51n)).to.be.true;436 expect(event.event).to.be.equal('Approval');
599 });
600
601 expect(events).to.be.deep.equal([437 expect(event.address).to.be.equal(tokenAddress);
602 {438 expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
603 address,
604 event: 'Transfer',
605 args: {
606 from: subToEth(alice.address),
607 to: receiver,
608 value: '51',
609 },
610 },
611 {
612 address,
613 event: 'Approval',
614 args: {
615 owner: subToEth(alice.address),
616 spender: subToEth(bob.address),439 expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));
617 value: '49',440 expect(event.returnValues.value).to.be.equal('49');
618 },
619 },
620 ]);
621 });441 });
622442
623 itWeb3('Events emitted for transfer()', async ({web3, api, privateKeyWrapper}) => {443 itEth('Events emitted for transfer()', async ({helper}) => {
624 const alice = privateKeyWrapper('//Alice');444 const receiver = helper.eth.createAccount();
445 const collection = await helper.rft.mintCollection(alice);
446 const token = await collection.mintToken(alice, 200n);
625447
626 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;448 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
449 const contract = helper.ethNativeContract.rftToken(tokenAddress);
627450
628 const receiver = createEthAccount(web3);451 const events: any = [];
629
630 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;
631
632 const address = tokenIdToAddress(collectionId, tokenId);452 contract.events.allEvents((_: any, event: any) => {
633 const contract = uniqueRefungibleToken(web3, address);
634
635 const events = await recordEvents(contract, async () => {
636 expect(await transfer(api, collectionId, tokenId, alice, {Ethereum: receiver}, 51n)).to.be.true;453 events.push(event);
637 });454 });
638455
639 expect(events).to.be.deep.equal([456 expect(await token.transfer(alice, {Ethereum: receiver}, 51n)).to.be.true;
640 {457
641 address,458 const event = events[0];
642 event: 'Transfer',459 expect(event.event).to.be.equal('Transfer');
643 args: {460 expect(event.address).to.be.equal(tokenAddress);
644 from: subToEth(alice.address),461 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
645 to: receiver,462 expect(event.returnValues.to).to.be.equal(receiver);
646 value: '51',463 expect(event.returnValues.value).to.be.equal('51');
647 },
648 },
649 ]);
650 });464 });
651});465});
652466
653describe('ERC 1633 implementation', () => {467describe('ERC 1633 implementation', () => {
468 let donor: IKeyringPair;
469
654 before(async function() {470 before(async function() {
655 await requirePallets(this, [Pallets.ReFungible]);471 await usingEthPlaygrounds(async (helper, privateKey) => {
656 });472 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
657473
658 itWeb3('Default parent token address and id', async ({api, web3, privateKeyWrapper}) => {474 donor = privateKey('//Alice');
475 });
659 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);476 });
660477
661 const {collectionIdAddress, collectionId} = await createRFTCollection(api, web3, owner);478 itEth('Default parent token address and id', async ({helper}) => {
662 const refungibleContract = uniqueRefungible(web3, collectionIdAddress, owner);
663 const refungibleTokenId = await refungibleContract.methods.nextTokenId().call();479 const owner = await helper.eth.createAccountWithBalance(donor);
664 await refungibleContract.methods.mint(owner, refungibleTokenId).send();
665480
666 const rftTokenAddress = tokenIdToAddress(collectionId, refungibleTokenId);481 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Sands', '', 'GRAIN');
667 const refungibleTokenContract = uniqueRefungibleToken(web3, rftTokenAddress, owner);482 const collectionContract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
483
484 const tokenId = await collectionContract.methods.nextTokenId().call();
485 await collectionContract.methods.mint(owner, tokenId).send();
486 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
487 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);
668488
669 const tokenAddress = await refungibleTokenContract.methods.parentToken().call();489 expect(await tokenContract.methods.parentToken().call()).to.be.equal(collectionAddress);
670 const tokenId = await refungibleTokenContract.methods.parentTokenId().call();490 expect(await tokenContract.methods.parentTokenId().call()).to.be.equal(tokenId);
671 expect(tokenAddress).to.be.equal(collectionIdAddress);
672 expect(tokenId).to.be.equal(refungibleTokenId);
673 });491 });
674});492});
675493
modifiedtests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -116,13 +116,17 @@
     return new web3.eth.Contract(abi as any, address, {gas: this.helper.eth.DEFAULT_GAS, ...(caller ? {from: caller} : {})});
   }
 
-  rftTokenByAddress(address: string, caller?: string): Contract {
+  collectionById(collectionId: number, mode: 'nft' | 'rft' | 'ft', caller?: string): Contract {
+    return this.collection(this.helper.ethAddress.fromCollectionId(collectionId), mode, caller);
+  }
+
+  rftToken(address: string, caller?: string): Contract {
     const web3 = this.helper.getWeb3();
     return new web3.eth.Contract(refungibleTokenAbi as any, address, {gas: this.helper.eth.DEFAULT_GAS, ...(caller ? {from: caller} : {})});
   }
 
-  rftToken(collectionId: number, tokenId: number, caller?: string): Contract {
-    return this.rftTokenByAddress(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller);
+  rftTokenById(collectionId: number, tokenId: number, caller?: string): Contract {
+    return this.rftToken(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller);
   }
 }
 
@@ -171,6 +175,17 @@
     return {collectionId, collectionAddress};
   }
 
+  async createRefungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {
+    const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
+        
+    const result = await collectionHelper.methods.createRFTCollection(name, description, tokenPrefix).send();
+
+    const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+    const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);
+
+    return {collectionId, collectionAddress};
+  }
+
   async deployCollectorContract(signer: string): Promise<Contract> {
     return await this.helper.ethContract.deployByCode(signer, 'Collector', `
     // SPDX-License-Identifier: UNLICENSED
@@ -215,6 +230,16 @@
     }
   `);
   }
+
+  async recordCallFee(user: string, call: () => Promise<any>): Promise<bigint> {
+    const before = await this.helper.balance.getEthereum(user);
+    await call();
+    // In dev mode, the transaction might not finish processing in time
+    await this.helper.wait.newBlocks(1);
+    const after = await this.helper.balance.getEthereum(user);
+
+    return before - after;
+  }
 }  
   
 class EthAddressGroup extends EthGroupBase {