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

difftreelog

Mint tests generalization

Max Andreev2023-01-13parent: #8a76dd7.patch.diff
in: master

8 files changed

deletedtests/src/eth/collections/callMethodsERC20.test.tsdiffbeforeafterboth
--- a/tests/src/eth/collections/callMethodsERC20.test.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-import {Pallets, requirePalletsOrSkip} from '../../util';
-import {expect, itEth, usingEthPlaygrounds} from '../util';
-import {IKeyringPair} from '@polkadot/types/types';
-
-[
-  {mode: 'ft' as const, requiredPallets: []},
-  {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-].map(testCase => {
-  describe(`${testCase.mode.toUpperCase()}: ERC-20 call methods`, () => {
-    let donor: IKeyringPair;
-
-    before(async function() {
-      await usingEthPlaygrounds(async (helper, privateKey) => {
-        requirePalletsOrSkip(this, helper, testCase.requiredPallets);
-        donor = await privateKey({filename: __filename});
-      });
-    });
-
-    itEth('totalSupply', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller];
-
-      const {collection, collectionId} = await helper.eth.createCollection(testCase.mode, caller, 'TotalSupply', '6', '6');
-      if (testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
-
-      // Use collection contract for FT or token contract for RFT:
-      const contract = testCase.mode === 'ft'
-        ? collection
-        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
-
-      // Mint tokens:
-      testCase.mode === 'ft'
-        ? await contract.methods.mint(...mintingParams).send({from: caller})
-        : await contract.methods.repartition(200).send({from: caller});
-
-      const totalSupply = await contract.methods.totalSupply().call();
-      expect(totalSupply).to.equal('200');
-    });
-
-    itEth('balanceOf', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller];
-
-      const {collection, collectionId} = await helper.eth.createCollection(testCase.mode, caller, 'BalanceOf', 'Descroption', 'Prefix');
-      if (testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
-
-      // Use collection contract for FT or token contract for RFT:
-      const contract = testCase.mode === 'ft'
-        ? collection
-        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
-
-      // Mint tokens:
-      testCase.mode === 'ft'
-        ? await contract.methods.mint(...mintingParams).send({from: caller})
-        : await contract.methods.repartition(200).send({from: caller});
-
-      const balance = await contract.methods.balanceOf(caller).call();
-      expect(balance).to.equal('200');
-    });
-
-    itEth('decimals', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const {collection, collectionId} = await helper.eth.createCollection(testCase.mode, caller, 'BalanceOf', 'Descroption', 'Prefix');
-      if (testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
-
-      // Use collection contract for FT or token contract for RFT:
-      const contract = testCase.mode === 'ft'
-        ? collection
-        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
-
-      const decimals = await contract.methods.decimals().call();
-      expect(decimals).to.equal(testCase.mode === 'rft' ? '0' : '18');
-    });
-  });
-});
\ No newline at end of file
deletedtests/src/eth/collections/callMethodsERC721.test.tsdiffbeforeafterboth
--- a/tests/src/eth/collections/callMethodsERC721.test.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-import {Pallets, requirePalletsOrSkip} from '../../util';
-import {expect, itEth, usingEthPlaygrounds} from '../util';
-import {IKeyringPair} from '@polkadot/types/types';
-
-[
-  {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  {mode: 'nft' as const, requiredPallets: []},
-].map(testCase => {
-  describe(`${testCase.mode.toUpperCase()}: ERC-721 call methods`, () => {
-    let donor: IKeyringPair;
-
-    before(async function() {
-      await usingEthPlaygrounds(async (helper, privateKey) => {
-        requirePalletsOrSkip(this, helper, testCase.requiredPallets);
-
-        donor = await privateKey({filename: __filename});
-      });
-    });
-
-    itEth('name/symbol/description', async ({helper}) => {
-      const callerEth = await helper.eth.createAccountWithBalance(donor);
-      const [callerSub] = await helper.arrange.createAccounts([100n], donor);
-      const [name, description, tokenPrefix] = ['Name', 'Description', 'Symbol'];
-
-      const {collection: collectionEth} = await helper.eth.createCollection(testCase.mode, callerEth, name, description, tokenPrefix);
-      await collectionEth.methods.mint(callerEth).send({from: callerEth});
-      const {collectionId} = await helper[testCase.mode].mintCollection(callerSub, {name, description, tokenPrefix});
-      const collectionSub = await helper.ethNativeContract.collectionById(collectionId, testCase.mode, callerEth);
-
-      // Can get name/symbol/description for Eth collection
-      expect(await collectionEth.methods.name().call()).to.eq(name);
-      expect(await collectionEth.methods.symbol().call()).to.eq(tokenPrefix);
-      expect(await collectionEth.methods.description().call()).to.eq(description);
-      // Can get name/symbol/description for Sub collection
-      expect(await collectionSub.methods.name().call()).to.eq(name);
-      expect(await collectionSub.methods.symbol().call()).to.eq(tokenPrefix);
-      expect(await collectionSub.methods.description().call()).to.eq(description);
-    });
-
-    itEth('totalSupply', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-
-      const {collection} = await helper.eth.createCollection(testCase.mode, caller, 'TotalSupply', '6', '6');
-      await collection.methods.mint(caller).send({from: caller});
-
-      const totalSupply = await collection.methods.totalSupply().call();
-      expect(totalSupply).to.equal('1');
-    });
-
-    itEth('balanceOf', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-
-      const {collection} = await helper.eth.createCollection(testCase.mode, caller, 'BalanceOf', 'Descroption', 'Prefix');
-      await collection.methods.mint(caller).send({from: caller});
-      await collection.methods.mint(caller).send({from: caller});
-      await collection.methods.mint(caller).send({from: caller});
-
-      const balance = await collection.methods.balanceOf(caller).call();
-      expect(balance).to.equal('3');
-    });
-
-    itEth('ownerOf', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const {collection} = await helper.eth.createCollection(testCase.mode, caller, 'OwnerOf', '6', '6');
-
-      const result = await collection.methods.mint(caller).send();
-      const tokenId = result.events.Transfer.returnValues.tokenId;
-
-      const owner = await collection.methods.ownerOf(tokenId).call();
-      expect(owner).to.equal(caller);
-    });
-
-    // TODO move to rft tests:
-    // itEth('ownerOf after burn', async ({helper}) => {
-    //   const caller = await helper.eth.createAccountWithBalance(donor);
-    //   const receiver = helper.eth.createAccount();
-    //   const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf-AfterBurn', '6', '6');
-    //   const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    //   const result = await contract.methods.mint(caller).send();
-    //   const tokenId = result.events.Transfer.returnValues.tokenId;
-    //   const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
-
-    //   await tokenContract.methods.repartition(2).send();
-    //   await tokenContract.methods.transfer(receiver, 1).send();
-
-    //   await tokenContract.methods.burnFrom(caller, 1).send();
-
-    //   const owner = await contract.methods.ownerOf(tokenId).call();
-    //   expect(owner).to.equal(receiver);
-    // });
-
-    // TODO move to rft tests:
-    // 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.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6');
-    //   const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    //   const result = await contract.methods.mint(caller).send();
-    //   const tokenId = result.events.Transfer.returnValues.tokenId;
-    //   const tokenContract = await 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');
-    // });
-  });
-});
modifiedtests/src/eth/fungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -29,24 +29,6 @@
     });
   });
 
-  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 collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const result = await contract.methods.mint(receiver, 100).send();
-
-    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');
-  });
-
   [
     'substrate' as const,
     'ethereum' as const,
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -88,30 +88,6 @@
     });
   });
 
-  itEth('Can perform mint() & get crossOwner()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', '');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-    expect(tokenId).to.be.equal('1');
-
-    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(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
-    expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
-    // TODO: this wont work right now, need release 919000 first
-    // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();
-    // const tokenUri = await contract.methods.tokenURI(nextTokenId).call();
-    // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);
-  });
-
   // TODO combine all minting tests in one place
   [
     'substrate' as const,
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
before · tests/src/eth/reFungible.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {Pallets, requirePalletsOrSkip} from '../util';18import {expect, itEth, usingEthPlaygrounds} from './util';19import {IKeyringPair} from '@polkadot/types/types';20import {ITokenPropertyPermission} from '../util/playgrounds/types';2122describe('Refungible: Plain calls', () => {23  let donor: IKeyringPair;24  let minter: IKeyringPair;25  let bob: IKeyringPair;26  let charlie: IKeyringPair;2728  before(async function() {29    await usingEthPlaygrounds(async (helper, privateKey) => {30      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);3132      donor = await privateKey({filename: __filename});33      [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);34    });35  });3637  itEth('Can perform mint() & crossOwnerOf()', async ({helper}) => {38    const owner = await helper.eth.createAccountWithBalance(donor);39    const receiver = helper.eth.createAccount();40    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Minty', '6', '6', '');41    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);4243    const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();4445    const event = result.events.Transfer;46    expect(event.address).to.equal(collectionAddress);47    expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');48    expect(event.returnValues.to).to.equal(receiver);49    const tokenId = event.returnValues.tokenId;50    expect(tokenId).to.be.equal('1');5152    expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);53    expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');54  });5556  [57    'substrate' as const,58    'ethereum' as const,59  ].map(testCase => {60    itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {61      const collectionAdmin = await helper.eth.createAccountWithBalance(donor);6263      const receiverEth = helper.eth.createAccount();64      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);65      const receiverSub = bob;66      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);6768      const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });69      const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {70        tokenOwner: false,71        collectionAdmin: true,72        mutable: false}};73      });747576      const collection = await helper.rft.mintCollection(minter, {77        tokenPrefix: 'ethp',78        tokenPropertyPermissions: permissions,79      });80      await collection.addAdmin(minter, {Ethereum: collectionAdmin});8182      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);83      const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', collectionAdmin, true);84      let expectedTokenId = await contract.methods.nextTokenId().call();85      let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send();86      let tokenId = result.events.Transfer.returnValues.tokenId;87      expect(tokenId).to.be.equal(expectedTokenId);8889      let event = result.events.Transfer;90      expect(event.address).to.be.equal(collectionAddress);91      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');92      expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));93      expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);9495      expectedTokenId = await contract.methods.nextTokenId().call();96      result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send();97      event = result.events.Transfer;98      expect(event.address).to.be.equal(collectionAddress);99      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');100      expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));101      expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);102103      tokenId = result.events.Transfer.returnValues.tokenId;104105      expect(tokenId).to.be.equal(expectedTokenId);106107      expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties108        .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));109110      expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))111        .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});112    });113  });114115  itEth.skip('Can perform mintBulk()', async ({helper}) => {116    const owner = await helper.eth.createAccountWithBalance(donor);117    const receiver = helper.eth.createAccount();118    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'MintBulky', '6', '6', '');119    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);120121    {122      const nextTokenId = await contract.methods.nextTokenId().call();123      expect(nextTokenId).to.be.equal('1');124      const result = await contract.methods.mintBulkWithTokenURI(125        receiver,126        [127          [nextTokenId, 'Test URI 0'],128          [+nextTokenId + 1, 'Test URI 1'],129          [+nextTokenId + 2, 'Test URI 2'],130        ],131      ).send();132133      const events = result.events.Transfer;134      for (let i = 0; i < 2; i++) {135        const event = events[i];136        expect(event.address).to.equal(collectionAddress);137        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');138        expect(event.returnValues.to).to.equal(receiver);139        expect(event.returnValues.tokenId).to.equal(String(+nextTokenId + i));140      }141142      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');143      expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');144      expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');145    }146  });147148  itEth('Can perform setApprovalForAll()', async ({helper}) => {149    const owner = await helper.eth.createAccountWithBalance(donor);150    const operator = helper.eth.createAccount();151152    const collection = await helper.rft.mintCollection(minter, {});153154    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);155    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);156157    const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();158    expect(approvedBefore).to.be.equal(false);159160    {161      const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});162163      expect(result.events.ApprovalForAll).to.be.like({164        address: collectionAddress,165        event: 'ApprovalForAll',166        returnValues: {167          owner,168          operator,169          approved: true,170        },171      });172173      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();174      expect(approvedAfter).to.be.equal(true);175    }176177    {178      const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});179180      expect(result.events.ApprovalForAll).to.be.like({181        address: collectionAddress,182        event: 'ApprovalForAll',183        returnValues: {184          owner,185          operator,186          approved: false,187        },188      });189190      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();191      expect(approvedAfter).to.be.equal(false);192    }193  });194195  itEth('Can perform burn with ApprovalForAll', async ({helper}) => {196    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});197198    const owner = await helper.eth.createAccountWithBalance(donor);199    const operator = await helper.eth.createAccountWithBalance(donor, 100n);200201    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});202203    const address = helper.ethAddress.fromCollectionId(collection.collectionId);204    const contract = await helper.ethNativeContract.collection(address, 'rft');205206    {207      await contract.methods.setApprovalForAll(operator, true).send({from: owner});208      const ownerCross = helper.ethCrossAccount.fromAddress(owner);209      const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});210      const events = result.events.Transfer;211212      expect(events).to.be.like({213        address,214        event: 'Transfer',215        returnValues: {216          from: owner,217          to: '0x0000000000000000000000000000000000000000',218          tokenId: token.tokenId.toString(),219        },220      });221    }222  });223224  itEth('Can perform burn with approve and approvalForAll', async ({helper}) => {225    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});226227    const owner = await helper.eth.createAccountWithBalance(donor);228    const operator = await helper.eth.createAccountWithBalance(donor, 100n);229230    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});231232    const address = helper.ethAddress.fromCollectionId(collection.collectionId);233    const contract = await helper.ethNativeContract.collection(address, 'rft');234235    const rftToken = await helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner);236237    {238      await rftToken.methods.approve(operator, 15n).send({from: owner});239      await contract.methods.setApprovalForAll(operator, true).send({from: owner});240      await rftToken.methods.burnFrom(owner, 10n).send({from: operator});241      const allowance = await rftToken.methods.allowance(owner, operator).call();242      expect(allowance).to.be.equal('5');243    }244  });245246  itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {247    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});248249    const owner = await helper.eth.createAccountWithBalance(donor);250    const operator = await helper.eth.createAccountWithBalance(donor);251    const receiver = charlie;252253    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});254255    const address = helper.ethAddress.fromCollectionId(collection.collectionId);256    const contract = await helper.ethNativeContract.collection(address, 'rft');257258    {259      await contract.methods.setApprovalForAll(operator, true).send({from: owner});260      const ownerCross = helper.ethCrossAccount.fromAddress(owner);261      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);262      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});263      const event = result.events.Transfer;264      expect(event).to.be.like({265        address: helper.ethAddress.fromCollectionId(collection.collectionId),266        event: 'Transfer',267        returnValues: {268          from: owner,269          to: helper.address.substrateToEth(receiver.address),270          tokenId: token.tokenId.toString(),271        },272      });273    }274275    expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);276  });277278  itEth('Can perform burn()', async ({helper}) => {279    const caller = await helper.eth.createAccountWithBalance(donor);280    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6');281    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);282283    const result = await contract.methods.mint(caller).send();284    const tokenId = result.events.Transfer.returnValues.tokenId;285    {286      const result = await contract.methods.burn(tokenId).send();287      const event = result.events.Transfer;288      expect(event.address).to.equal(collectionAddress);289      expect(event.returnValues.from).to.equal(caller);290      expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');291      expect(event.returnValues.tokenId).to.equal(tokenId.toString());292    }293  });294295  itEth('Can perform transferFrom()', async ({helper}) => {296    const caller = await helper.eth.createAccountWithBalance(donor);297    const receiver = helper.eth.createAccount();298    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'TransferFromy', '6', '6');299    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);300301    const result = await contract.methods.mint(caller).send();302    const tokenId = result.events.Transfer.returnValues.tokenId;303304    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);305306    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, caller);307    await tokenContract.methods.repartition(15).send();308309    {310      const tokenEvents: any = [];311      tokenContract.events.allEvents((_: any, event: any) => {312        tokenEvents.push(event);313      });314      const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();315      if (tokenEvents.length == 0) await helper.wait.newBlocks(1);316317      let event = result.events.Transfer;318      expect(event.address).to.equal(collectionAddress);319      expect(event.returnValues.from).to.equal(caller);320      expect(event.returnValues.to).to.equal(receiver);321      expect(event.returnValues.tokenId).to.equal(tokenId.toString());322323      event = tokenEvents[0];324      expect(event.address).to.equal(tokenAddress);325      expect(event.returnValues.from).to.equal(caller);326      expect(event.returnValues.to).to.equal(receiver);327      expect(event.returnValues.value).to.equal('15');328    }329330    {331      const balance = await contract.methods.balanceOf(receiver).call();332      expect(+balance).to.equal(1);333    }334335    {336      const balance = await contract.methods.balanceOf(caller).call();337      expect(+balance).to.equal(0);338    }339  });340341  // Soft-deprecated342  itEth('Can perform burnFrom()', async ({helper}) => {343    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});344345    const owner = await helper.eth.createAccountWithBalance(donor, 100n);346    const spender = await helper.eth.createAccountWithBalance(donor, 100n);347348    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});349350    const address = helper.ethAddress.fromCollectionId(collection.collectionId);351    const contract = await helper.ethNativeContract.collection(address, 'rft', spender, true);352353    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);354    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner);355    await tokenContract.methods.repartition(15).send();356    await tokenContract.methods.approve(spender, 15).send();357358    {359      const result = await contract.methods.burnFrom(owner, token.tokenId).send();360      const event = result.events.Transfer;361      expect(event).to.be.like({362        address: helper.ethAddress.fromCollectionId(collection.collectionId),363        event: 'Transfer',364        returnValues: {365          from: owner,366          to: '0x0000000000000000000000000000000000000000',367          tokenId: token.tokenId.toString(),368        },369      });370    }371372    expect(await collection.getTokenBalance(token.tokenId, {Ethereum: owner})).to.be.eq(0n);373  });374375  itEth('Can perform burnFromCross()', async ({helper}) => {376    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});377378    const owner = bob;379    const spender = await helper.eth.createAccountWithBalance(donor, 100n);380381    const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});382383    const address = helper.ethAddress.fromCollectionId(collection.collectionId);384    const contract = await helper.ethNativeContract.collection(address, 'rft');385386    await token.repartition(owner, 15n);387    await token.approve(owner, {Ethereum: spender}, 15n);388389    {390      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);391      const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender});392      const event = result.events.Transfer;393      expect(event).to.be.like({394        address: helper.ethAddress.fromCollectionId(collection.collectionId),395        event: 'Transfer',396        returnValues: {397          from: helper.address.substrateToEth(owner.address),398          to: '0x0000000000000000000000000000000000000000',399          tokenId: token.tokenId.toString(),400        },401      });402    }403404    expect(await collection.getTokenBalance(token.tokenId, {Substrate: owner.address})).to.be.eq(0n);405  });406407  itEth('Can perform transferFromCross()', async ({helper}) => {408    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});409410    const owner = bob;411    const spender = await helper.eth.createAccountWithBalance(donor, 100n);412    const receiver = charlie;413414    const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});415416    const address = helper.ethAddress.fromCollectionId(collection.collectionId);417    const contract = await helper.ethNativeContract.collection(address, 'rft');418419    await token.repartition(owner, 15n);420    await token.approve(owner, {Ethereum: spender}, 15n);421422    {423      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);424      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);425      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});426      const event = result.events.Transfer;427      expect(event).to.be.like({428        address: helper.ethAddress.fromCollectionId(collection.collectionId),429        event: 'Transfer',430        returnValues: {431          from: helper.address.substrateToEth(owner.address),432          to: helper.address.substrateToEth(receiver.address),433          tokenId: token.tokenId.toString(),434        },435      });436    }437438    expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);439  });440441  itEth('Can perform transfer()', async ({helper}) => {442    const caller = await helper.eth.createAccountWithBalance(donor);443    const receiver = helper.eth.createAccount();444    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6');445    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);446447    const result = await contract.methods.mint(caller).send();448    const tokenId = result.events.Transfer.returnValues.tokenId;449450    {451      const result = await contract.methods.transfer(receiver, tokenId).send();452453      const event = result.events.Transfer;454      expect(event.address).to.equal(collectionAddress);455      expect(event.returnValues.from).to.equal(caller);456      expect(event.returnValues.to).to.equal(receiver);457      expect(event.returnValues.tokenId).to.equal(tokenId.toString());458    }459460    {461      const balance = await contract.methods.balanceOf(caller).call();462      expect(+balance).to.equal(0);463    }464465    {466      const balance = await contract.methods.balanceOf(receiver).call();467      expect(+balance).to.equal(1);468    }469  });470471  itEth('Can perform transferCross()', async ({helper}) => {472    const sender = await helper.eth.createAccountWithBalance(donor);473    const receiverEth = await helper.eth.createAccountWithBalance(donor);474    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);475    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);476477    const collection = await helper.rft.mintCollection(minter, {});478    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);479    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', sender);480481    const token = await collection.mintToken(minter, 50n, {Ethereum: sender});482483    {484      // Can transferCross to ethereum address:485      const result = await collectionEvm.methods.transferCross(receiverCrossEth, token.tokenId).send({from: sender});486      // Check events:487      const event = result.events.Transfer;488      expect(event.address).to.equal(collectionAddress);489      expect(event.returnValues.from).to.equal(sender);490      expect(event.returnValues.to).to.equal(receiverEth);491      expect(event.returnValues.tokenId).to.equal(token.tokenId.toString());492      // Sender's balance decreased:493      const senderBalance = await collectionEvm.methods.balanceOf(sender).call();494      expect(+senderBalance).to.equal(0);495      expect(await token.getBalance({Ethereum: sender})).to.eq(0n);496      // Receiver's balance increased:497      const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();498      expect(+receiverBalance).to.equal(1);499      expect(await token.getBalance({Ethereum: receiverEth})).to.eq(50n);500    }501502    {503      // Can transferCross to substrate address:504      const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, token.tokenId).send({from: receiverEth});505      // Check events:506      const event = substrateResult.events.Transfer;507      expect(event.address).to.be.equal(collectionAddress);508      expect(event.returnValues.from).to.be.equal(receiverEth);509      expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));510      expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);511      // Sender's balance decreased:512      const senderBalance = await collectionEvm.methods.balanceOf(receiverEth).call();513      expect(+senderBalance).to.equal(0);514      expect(await token.getBalance({Ethereum: receiverEth})).to.eq(0n);515      // Receiver's balance increased:516      const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});517      expect(receiverBalance).to.contain(token.tokenId);518      expect(await token.getBalance({Substrate: minter.address})).to.eq(50n);519    }520  });521522  ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {523    const sender = await helper.eth.createAccountWithBalance(donor);524    const tokenOwner = await helper.eth.createAccountWithBalance(donor);525    const receiverSub = minter;526    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);527528    const collection = await helper.rft.mintCollection(minter, {});529    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);530    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', sender);531532    await collection.mintToken(minter, 50n, {Ethereum: sender});533    const nonSendersToken = await collection.mintToken(minter, 50n, {Ethereum: tokenOwner});534535    // Cannot transferCross someone else's token:536    const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;537    await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;538    // Cannot transfer token if it does not exist:539    await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;540  }));541542  itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {543    const caller = await helper.eth.createAccountWithBalance(donor);544    const receiver = helper.eth.createAccount();545    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Partial-to-Full', '6', '6');546    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);547548    const result = await contract.methods.mint(caller).send();549    const tokenId = result.events.Transfer.returnValues.tokenId;550551    const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);552553    await tokenContract.methods.repartition(2).send();554    await tokenContract.methods.transfer(receiver, 1).send();555556    const events: any = [];557    contract.events.allEvents((_: any, event: any) => {558      events.push(event);559    });560561    await tokenContract.methods.transfer(receiver, 1).send();562    if (events.length == 0) await helper.wait.newBlocks(1);563    const event = events[0];564565    expect(event.address).to.equal(collectionAddress);566    expect(event.returnValues.from).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');567    expect(event.returnValues.to).to.equal(receiver);568    expect(event.returnValues.tokenId).to.equal(tokenId.toString());569  });570571  itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {572    const caller = await helper.eth.createAccountWithBalance(donor);573    const receiver = helper.eth.createAccount();574    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Full-to-Partial', '6', '6');575    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);576577    const result = await contract.methods.mint(caller).send();578    const tokenId = result.events.Transfer.returnValues.tokenId;579580    const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);581582    await tokenContract.methods.repartition(2).send();583584    const events: any = [];585    contract.events.allEvents((_: any, event: any) => {586      events.push(event);587    });588589    await tokenContract.methods.transfer(receiver, 1).send();590    if (events.length == 0) await helper.wait.newBlocks(1);591    const event = events[0];592593    expect(event.address).to.equal(collectionAddress);594    expect(event.returnValues.from).to.equal(caller);595    expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');596    expect(event.returnValues.tokenId).to.equal(tokenId.toString());597  });598});599600describe('RFT: Fees', () => {601  let donor: IKeyringPair;602603  before(async function() {604    await usingEthPlaygrounds(async (helper, privateKey) => {605      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);606607      donor = await privateKey({filename: __filename});608    });609  });610611  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {612    const caller = await helper.eth.createAccountWithBalance(donor);613    const receiver = helper.eth.createAccount();614    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer-From', '6', '6');615    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);616617    const result = await contract.methods.mint(caller).send();618    const tokenId = result.events.Transfer.returnValues.tokenId;619620    const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());621    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));622    expect(cost > 0n);623  });624625  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {626    const caller = await helper.eth.createAccountWithBalance(donor);627    const receiver = helper.eth.createAccount();628    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer', '6', '6');629    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);630631    const result = await contract.methods.mint(caller).send();632    const tokenId = result.events.Transfer.returnValues.tokenId;633634    const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send());635    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));636    expect(cost > 0n);637  });638});639640describe('Common metadata', () => {641  let donor: IKeyringPair;642  let alice: IKeyringPair;643644  before(async function() {645    await usingEthPlaygrounds(async (helper, privateKey) => {646      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);647648      donor = await privateKey({filename: __filename});649      [alice] = await helper.arrange.createAccounts([20n], donor);650    });651  });652653  itEth('Returns collection name', async ({helper}) => {654    const caller = helper.eth.createAccount();655    const tokenPropertyPermissions = [{656      key: 'URI',657      permission: {658        mutable: true,659        collectionAdmin: true,660        tokenOwner: false,661      },662    }];663    const collection = await helper.rft.mintCollection(664      alice,665      {666        name: 'Leviathan',667        tokenPrefix: '11',668        properties: [{key: 'ERC721Metadata', value: '1'}],669        tokenPropertyPermissions,670      },671    );672673    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);674    const name = await contract.methods.name().call();675    expect(name).to.equal('Leviathan');676  });677678  itEth('Returns symbol name', async ({helper}) => {679    const caller = await helper.eth.createAccountWithBalance(donor);680    const tokenPropertyPermissions = [{681      key: 'URI',682      permission: {683        mutable: true,684        collectionAdmin: true,685        tokenOwner: false,686      },687    }];688    const {collectionId} = await helper.rft.mintCollection(689      alice,690      {691        name: 'Leviathan',692        tokenPrefix: '12',693        properties: [{key: 'ERC721Metadata', value: '1'}],694        tokenPropertyPermissions,695      },696    );697698    const contract = await helper.ethNativeContract.collectionById(collectionId, 'rft', caller);699    const symbol = await contract.methods.symbol().call();700    expect(symbol).to.equal('12');701  });702});703704describe('Negative tests', () => {705  let donor: IKeyringPair;706  let minter: IKeyringPair;707  let alice: IKeyringPair;708709  before(async function() {710    await usingEthPlaygrounds(async (helper, privateKey) => {711      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);712713      donor = await privateKey({filename: __filename});714      [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);715    });716  });717718  itEth('[negative] Cant perform burn without approval', async ({helper}) => {719    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});720721    const owner = await helper.eth.createAccountWithBalance(donor, 100n);722    const spender = await helper.eth.createAccountWithBalance(donor, 100n);723724    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});725726    const address = helper.ethAddress.fromCollectionId(collection.collectionId);727    const contract = await helper.ethNativeContract.collection(address, 'rft');728729    const ownerCross = helper.ethCrossAccount.fromAddress(owner);730731    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;732733    await contract.methods.setApprovalForAll(spender, true).send({from: owner});734    await contract.methods.setApprovalForAll(spender, false).send({from: owner});735736    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;737  });738739  itEth('[negative] Cant perform transfer without approval', async ({helper}) => {740    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});741    const owner = await helper.eth.createAccountWithBalance(donor, 100n);742    const receiver = alice;743744    const spender = await helper.eth.createAccountWithBalance(donor, 100n);745746    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});747748    const address = helper.ethAddress.fromCollectionId(collection.collectionId);749    const contract = await helper.ethNativeContract.collection(address, 'rft');750751    const ownerCross = helper.ethCrossAccount.fromAddress(owner);752    const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);753754    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;755756    await contract.methods.setApprovalForAll(spender, true).send({from: owner});757    await contract.methods.setApprovalForAll(spender, false).send({from: owner});758759    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;760  });761});
addedtests/src/eth/tokens/callMethodsERC20.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/tokens/callMethodsERC20.test.ts
@@ -0,0 +1,91 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {Pallets, requirePalletsOrSkip} from '../../util';
+import {expect, itEth, usingEthPlaygrounds} from '../util';
+import {IKeyringPair} from '@polkadot/types/types';
+
+[
+  {mode: 'ft' as const, requiredPallets: []},
+  {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+].map(testCase => {
+  describe(`${testCase.mode.toUpperCase()}: ERC-20 call methods`, () => {
+    let donor: IKeyringPair;
+
+    before(async function() {
+      await usingEthPlaygrounds(async (helper, privateKey) => {
+        requirePalletsOrSkip(this, helper, testCase.requiredPallets);
+        donor = await privateKey({filename: __filename});
+      });
+    });
+
+    itEth('totalSupply', async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller];
+
+      const {collection, collectionId} = await helper.eth.createCollection(testCase.mode, caller, 'TotalSupply', '6', '6');
+      if (testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
+
+      // Use collection contract for FT or token contract for RFT:
+      const contract = testCase.mode === 'ft'
+        ? collection
+        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
+
+      // Mint tokens:
+      testCase.mode === 'ft'
+        ? await contract.methods.mint(...mintingParams).send({from: caller})
+        : await contract.methods.repartition(200).send({from: caller});
+
+      const totalSupply = await contract.methods.totalSupply().call();
+      expect(totalSupply).to.equal('200');
+    });
+
+    itEth('balanceOf', async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller];
+
+      const {collection, collectionId} = await helper.eth.createCollection(testCase.mode, caller, 'BalanceOf', 'Descroption', 'Prefix');
+      if (testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
+
+      // Use collection contract for FT or token contract for RFT:
+      const contract = testCase.mode === 'ft'
+        ? collection
+        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
+
+      // Mint tokens:
+      testCase.mode === 'ft'
+        ? await contract.methods.mint(...mintingParams).send({from: caller})
+        : await contract.methods.repartition(200).send({from: caller});
+
+      const balance = await contract.methods.balanceOf(caller).call();
+      expect(balance).to.equal('200');
+    });
+
+    itEth('decimals', async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const {collection, collectionId} = await helper.eth.createCollection(testCase.mode, caller, 'BalanceOf', 'Descroption', 'Prefix');
+      if (testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
+
+      // Use collection contract for FT or token contract for RFT:
+      const contract = testCase.mode === 'ft'
+        ? collection
+        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
+
+      const decimals = await contract.methods.decimals().call();
+      expect(decimals).to.equal(testCase.mode === 'rft' ? '0' : '18');
+    });
+  });
+});
\ No newline at end of file
addedtests/src/eth/tokens/callMethodsERC721.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/tokens/callMethodsERC721.test.ts
@@ -0,0 +1,143 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {Pallets} from '../../util';
+import {expect, itEth, usingEthPlaygrounds} from '../util';
+import {IKeyringPair} from '@polkadot/types/types';
+
+
+describe('ERC-721 call methods', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({filename: __filename});
+    });
+  });
+
+  [
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'nft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: name/symbol/description`, testCase.requiredPallets, async ({helper}) => {
+      const callerEth = await helper.eth.createAccountWithBalance(donor);
+      const [callerSub] = await helper.arrange.createAccounts([100n], donor);
+      const [name, description, tokenPrefix] = ['Name', 'Description', 'Symbol'];
+
+      const {collection: collectionEth} = await helper.eth.createCollection(testCase.mode, callerEth, name, description, tokenPrefix);
+      await collectionEth.methods.mint(callerEth).send({from: callerEth});
+      const {collectionId} = await helper[testCase.mode].mintCollection(callerSub, {name, description, tokenPrefix});
+      const collectionSub = await helper.ethNativeContract.collectionById(collectionId, testCase.mode, callerEth);
+
+      // Can get name/symbol/description for Eth collection
+      expect(await collectionEth.methods.name().call()).to.eq(name);
+      expect(await collectionEth.methods.symbol().call()).to.eq(tokenPrefix);
+      expect(await collectionEth.methods.description().call()).to.eq(description);
+      // Can get name/symbol/description for Sub collection
+      expect(await collectionSub.methods.name().call()).to.eq(name);
+      expect(await collectionSub.methods.symbol().call()).to.eq(tokenPrefix);
+      expect(await collectionSub.methods.description().call()).to.eq(description);
+    });
+  });
+
+  [
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'nft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: totalSupply`, testCase.requiredPallets, async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+
+      const {collection} = await helper.eth.createCollection(testCase.mode, caller, 'TotalSupply', '6', '6');
+      await collection.methods.mint(caller).send({from: caller});
+
+      const totalSupply = await collection.methods.totalSupply().call();
+      expect(totalSupply).to.equal('1');
+    });
+  });
+
+  [
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'nft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: balanceOf`, testCase.requiredPallets, async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+
+      const {collection} = await helper.eth.createCollection(testCase.mode, caller, 'BalanceOf', 'Descroption', 'Prefix');
+      await collection.methods.mint(caller).send({from: caller});
+      await collection.methods.mint(caller).send({from: caller});
+      await collection.methods.mint(caller).send({from: caller});
+
+      const balance = await collection.methods.balanceOf(caller).call();
+      expect(balance).to.equal('3');
+    });
+  });
+
+  [
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'nft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: ownerOf`, testCase.requiredPallets, async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const {collection} = await helper.eth.createCollection(testCase.mode, caller, 'OwnerOf', '6', '6');
+
+      const result = await collection.methods.mint(caller).send();
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+
+      const owner = await collection.methods.ownerOf(tokenId).call();
+      expect(owner).to.equal(caller);
+    });
+  });
+
+  [
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    // TODO {mode: 'nft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth(`${testCase.mode.toUpperCase()}: ownerOf after burn`, async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+      const {collection, collectionId} = await helper.eth.createCollection(testCase.mode, caller, 'OwnerOf-AfterBurn', '6', '6');
+
+      const result = await collection.methods.mint(caller).send();
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+      const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
+
+      await tokenContract.methods.repartition(2).send();
+      await tokenContract.methods.transfer(receiver, 1).send();
+
+      await tokenContract.methods.burnFrom(caller, 1).send();
+
+      const owner = await collection.methods.ownerOf(tokenId).call();
+      expect(owner).to.equal(receiver);
+    });
+  });
+
+  itEth('RFT: ownerOf for partial ownership', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+
+    const result = await contract.methods.mint(caller).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+    const tokenContract = await 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');
+  });
+});
addedtests/src/eth/tokens/minting.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/tokens/minting.test.ts
@@ -0,0 +1,167 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {Pallets} from '../../util';
+import {expect, itEth, usingEthPlaygrounds} from '../util';
+
+
+describe('Minting tokens', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({filename: __filename});
+      [alice] = await helper.arrange.createAccounts([30n, 20n], donor);
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'ft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth(`${testCase.mode.toUpperCase()}: Can mint() for Substrate collection`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+      const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver];
+
+      const collection = await helper[testCase.mode].mintCollection(alice);
+      await collection.addAdmin(alice, {Ethereum: owner});
+
+      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+      const result = await contract.methods.mint(...mintingParams).send({from: owner});
+
+      // Check events:
+      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);
+      if (testCase.mode === 'ft')
+        expect(event.returnValues.value).to.equal('100');
+
+      // Check token exist:
+      if(testCase.mode === 'ft') {
+        expect(await helper.ft.getBalance(collection.collectionId, {Ethereum: receiver})).to.eq(100n);
+      } else {
+        const tokenId = event.returnValues.tokenId;
+        expect(tokenId).to.be.equal('1');
+        expect(await helper.collection.getLastTokenId(collection.collectionId)).to.eq(1);
+        expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+      }
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'ft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth(`${testCase.mode.toUpperCase()}: Can mint() for Ethereum collection`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+      const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver];
+
+      const {collection, collectionAddress, collectionId} = await helper.eth.createCollection(testCase.mode, owner, 'Name', 'Desc', 'Prefix');
+
+      const result = await collection.methods.mint(...mintingParams).send({from: owner});
+
+      // Check events:
+      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);
+      if (testCase.mode === 'ft')
+        expect(event.returnValues.value).to.equal('100');
+
+      // Check token exist:
+      if(testCase.mode === 'ft') {
+        expect(await helper.ft.getBalance(collectionId, {Ethereum: receiver})).to.eq(100n);
+      } else {
+        const tokenId = event.returnValues.tokenId;
+        expect(tokenId).to.be.equal('1');
+        expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1);
+        expect(await collection.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+      }
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'ft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth(`${testCase.mode.toUpperCase()}: Can mint() for Ethereum collection`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+      const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver];
+
+      const {collection, collectionAddress, collectionId} = await helper.eth.createCollection(testCase.mode, owner, 'Name', 'Desc', 'Prefix');
+
+      const result = await collection.methods.mint(...mintingParams).send({from: owner});
+
+      // Check events:
+      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);
+      if (testCase.mode === 'ft')
+        expect(event.returnValues.value).to.equal('100');
+
+      // Check token exist:
+      if(testCase.mode === 'ft') {
+        expect(await helper.ft.getBalance(collectionId, {Ethereum: receiver})).to.eq(100n);
+      } else {
+        const tokenId = event.returnValues.tokenId;
+        expect(tokenId).to.be.equal('1');
+        expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1);
+        expect(await collection.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+      }
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase => {
+    itEth(`${testCase.mode.toUpperCase()}: Can mintWithTokenURI() for Ethereum collection`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+
+      const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', '');
+      const contract = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+      const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+      expect(tokenId).to.be.equal('1');
+
+      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(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
+      expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+      // TODO: this wont work right now, need release 919000 first
+      // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();
+      // const tokenUri = await contract.methods.tokenURI(nextTokenId).call();
+      // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);
+    });
+  });
+});
\ No newline at end of file