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

difftreelog

refactor changed arch of bechmarks

PraetorP2023-02-06parent: #f64e9cf.patch.diff
in: master
added export to `csv` ops fees

6 files changed

modifiedtests/.gitignorediffbeforeafterboth
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,2 +1,4 @@
 /node_modules/
 properties.csv
+erc721.csv
+erc20.csv
\ No newline at end of file
deletedtests/src/benchmarks/mintFee/benchmark.tsdiffbeforeafterboth
--- a/tests/src/benchmarks/mintFee/benchmark.ts
+++ /dev/null
@@ -1,1323 +0,0 @@
-import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';
-import {readFile} from 'fs/promises';
-import {CollectionLimitField, ContractImports, TokenPermissionField} from '../../eth/util/playgrounds/types';
-import {
-  ICrossAccountId,
-  ITokenPropertyPermission,
-  TCollectionMode,
-} from '../../util/playgrounds/types';
-import {IKeyringPair} from '@polkadot/types/types';
-import {UniqueFTCollection, UniqueNFTCollection, UniqueRFTCollection} from '../../util/playgrounds/unique';
-import {Contract} from 'web3-eth-contract';
-import {createObjectCsvWriter} from 'csv-writer';
-import {FunctionFeeVM, IFunctionFee} from './types';
-
-const CONTRACT_IMPORT: ContractImports[] = [
-  {
-    fsPath: `${__dirname}/../../eth/api/CollectionHelpers.sol`,
-    solPath: 'eth/api/CollectionHelpers.sol',
-  },
-  {
-    fsPath: `${__dirname}/../../eth/api/ContractHelpers.sol`,
-    solPath: 'eth/api/ContractHelpers.sol',
-  },
-  {
-    fsPath: `${__dirname}/../../eth/api/UniqueRefungibleToken.sol`,
-    solPath: 'eth/api/UniqueRefungibleToken.sol',
-  },
-  {
-    fsPath: `${__dirname}/../../eth/api/UniqueRefungible.sol`,
-    solPath: 'eth/api/UniqueRefungible.sol',
-  },
-  {
-    fsPath: `${__dirname}/../../eth/api/UniqueNFT.sol`,
-    solPath: 'eth/api/UniqueNFT.sol',
-  },
-];
-
-const PROPERTIES = Array(40)
-  .fill(0)
-  .map((_, i) => {
-    return {
-      key: `key_${i}`,
-      value: Uint8Array.from(Buffer.from(`value_${i}`)),
-    };
-  });
-
-const SUBS_PROPERTIES = Array(40)
-  .fill(0)
-  .map((_, i) => {
-    return {
-      key: `key_${i}`,
-      value: `value_${i}`,
-    };
-  });
-
-const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => {
-  return {
-    key: p.key,
-    permission: {
-      tokenOwner: true,
-      collectionAdmin: true,
-      mutable: true,
-    },
-  };
-});
-
-interface IBenchmarkResultForProp {
-	propertiesNumber: number;
-	substrateFee: number;
-	ethFee: number;
-	ethBulkFee: number;
-  ethMintCrossFee: number;
-	evmProxyContractFee: number;
-	evmProxyContractBulkFee: number;
-}
-
-const main = async () => {
-  const benchmarks = [
-    'substrateFee',
-    'ethFee',
-    'ethBulkFee',
-    'ethMintCrossFee',
-    'evmProxyContractFee',
-    'evmProxyContractBulkFee',
-  ];
-  const headers = [
-    'propertiesNumber',
-    ...benchmarks,
-  ];
-
-
-  const csvWriter = createObjectCsvWriter({
-    path: 'properties.csv',
-    header: headers,
-  });
-
-  await usingEthPlaygrounds(async (helper, privateKey) => {
-    const NOMINAL = helper.balance.getOneTokenNominal();
-    const CONTRACT_SOURCE = (
-      await readFile(`${__dirname}/proxyContract.sol`)
-    ).toString();
-
-    console.log('\n ERC20 ops fees');
-    console.table(FunctionFeeVM.fromModel(await erc20CalculateFeeGas(helper, privateKey)));
-
-    console.log('\n ERC721 ops fees');
-    const erc721 = await erc721CalculateFeeGas(helper, privateKey);
-    console.table(FunctionFeeVM.fromModel(erc721));
-
-    const donor = await privateKey('//Alice'); // Seed from account with balance on this network
-    const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
-
-    const contract = await helper.ethContract.deployByCode(
-      ethSigner,
-      'ProxyMint',
-      CONTRACT_SOURCE,
-      CONTRACT_IMPORT,
-    );
-
-    const fees = await benchMintFee(helper, privateKey, contract);
-    console.log('Minting without properties');
-    console.table(fees);
-
-    const result: IBenchmarkResultForProp[] = [];
-    const csvResult: IBenchmarkResultForProp[] = [];
-
-    for (let i = 1; i <= 20; i++) {
-      const benchResult = await benchMintWithProperties(helper, privateKey, contract, {
-        propertiesNumber: i,
-      }) as any;
-
-      csvResult.push(benchResult);
-
-      const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));
-      for(const key of benchmarks) {
-        const keyPercent = Math.round((benchResult[key] / minFee) * 100);
-        benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;
-      }
-
-      result.push(benchResult);
-    }
-
-    await csvWriter.writeRecords(csvResult);
-
-    console.log('Minting with properties');
-    console.table(result, headers);
-  });
-};
-
-main()
-  .then(() => process.exit(0))
-  .catch((e) => {
-    console.log(e);
-    process.exit(1);
-  });
-
-async function createCollectionForBenchmarks(
-  mode : TCollectionMode,
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-  ethSigner: string,
-  proxyContract: string | null,
-  permissions: ITokenPropertyPermission[],
-) {
-  const donor = await privateKey('//Alice');
-
-  const collection = await helper[mode].mintCollection(donor, {
-    name: 'test mintToSubstrate',
-    description: 'EVMHelpers',
-    tokenPrefix: mode,
-    ...(mode != 'ft' ? {
-      tokenPropertyPermissions: [
-        {
-          key: 'url',
-          permission: {
-            tokenOwner: true,
-            collectionAdmin: true,
-            mutable: true,
-          },
-        },
-        {
-          key: 'URI',
-          permission: {
-            tokenOwner: true,
-            collectionAdmin: true,
-            mutable: true,
-          },
-        },
-      ],
-    } : {}),
-    limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0},
-    permissions: {mintMode: true},
-  });
-
-  await collection.addToAllowList(donor, {
-    Ethereum: helper.address.substrateToEth(donor.address),
-  });
-  await collection.addToAllowList(donor, {Substrate: donor.address});
-  await collection.addAdmin(donor, {Ethereum: ethSigner});
-  await collection.addAdmin(donor, {
-    Ethereum: helper.address.substrateToEth(donor.address),
-  });
-
-  if (proxyContract) {
-    await collection.addToAllowList(donor, {Ethereum: proxyContract});
-    await collection.addAdmin(donor, {Ethereum: proxyContract});
-  }
-  if (collection instanceof UniqueNFTCollection || collection instanceof UniqueRFTCollection)
-    await collection.setTokenPropertyPermissions(donor, permissions);
-
-  return collection;
-}
-
-async function benchMintFee(
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-  proxyContract: Contract,
-): Promise<{
-	substrateFee: number;
-	ethFee: number;
-	evmProxyContractFee: number;
-}> {
-  const donor = await privateKey('//Alice');
-  const substrateReceiver = await privateKey('//Bob');
-  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
-
-  const nominal = helper.balance.getOneTokenNominal();
-
-  await helper.eth.transferBalanceFromSubstrate(
-    donor,
-    proxyContract.options.address,
-    100n,
-  );
-
-  const collection = (await createCollectionForBenchmarks(
-    'nft',
-    helper,
-    privateKey,
-    ethSigner,
-    proxyContract.options.address,
-    PERMISSIONS,
-  )) as UniqueNFTCollection;
-
-  const substrateFee = await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),
-  );
-
-  const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-  const collectionContract = await helper.ethNativeContract.collection(
-    collectionEthAddress,
-    'nft',
-  );
-
-  const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);
-
-  const encodedCall = collectionContract.methods
-    .mint(receiverEthAddress)
-    .encodeABI();
-
-  const ethFee = await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    async () => {
-      await helper.eth.sendEVM(
-        donor,
-        collectionContract.options.address,
-        encodedCall,
-        '0',
-      );
-    },
-  );
-
-  const evmProxyContractFee = await helper.arrange.calculcateFee(
-    {Ethereum: ethSigner},
-    async () => {
-      await proxyContract.methods
-        .mintToSubstrate(
-          helper.ethAddress.fromCollectionId(collection.collectionId),
-          substrateReceiver.addressRaw,
-        )
-        .send({from: ethSigner});
-    },
-  );
-
-  return {
-    substrateFee: convertToTokens(substrateFee, nominal),
-    ethFee: convertToTokens(ethFee, nominal),
-    evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),
-  };
-}
-
-async function benchMintWithProperties(
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-  proxyContract: Contract,
-  setup: { propertiesNumber: number },
-): Promise<IBenchmarkResultForProp> {
-  const donor = await privateKey('//Alice'); // Seed from account with balance on this network
-  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
-
-  const susbstrateReceiver = await privateKey('//Bob');
-  const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);
-
-  const nominal = helper.balance.getOneTokenNominal();
-
-  const substrateFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Substrate: donor.address},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      await collection.mintToken(
-        donor,
-        {Substrate: susbstrateReceiver.address},
-        PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {
-          return {key: p.key, value: Buffer.from(p.value).toString()};
-        }),
-      );
-    },
-  );
-
-  const ethFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Substrate: donor.address},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      const evmContract = await helper.ethNativeContract.collection(
-        helper.ethAddress.fromCollectionId(collection.collectionId),
-        'nft',
-        undefined,
-        true,
-      );
-
-      const subTokenId = await evmContract.methods.nextTokenId().call();
-
-      let encodedCall = evmContract.methods
-        .mint(receiverEthAddress)
-        .encodeABI();
-
-      await helper.eth.sendEVM(
-        donor,
-        evmContract.options.address,
-        encodedCall,
-        '0',
-      );
-
-      for (const val of PROPERTIES.slice(0, setup.propertiesNumber)) {
-        encodedCall = await evmContract.methods
-          .setProperty(subTokenId, val.key, Buffer.from(val.value))
-          .encodeABI();
-
-        await helper.eth.sendEVM(
-          donor,
-          evmContract.options.address,
-          encodedCall,
-          '0',
-        );
-      }
-    },
-  );
-
-  const ethBulkFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Substrate: donor.address},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      const evmContract = await helper.ethNativeContract.collection(
-        helper.ethAddress.fromCollectionId(collection.collectionId),
-        'nft',
-      );
-
-      const subTokenId = await evmContract.methods.nextTokenId().call();
-
-      let encodedCall = evmContract.methods
-        .mint(receiverEthAddress)
-        .encodeABI();
-
-      await helper.eth.sendEVM(
-        donor,
-        evmContract.options.address,
-        encodedCall,
-        '0',
-      );
-
-      encodedCall = await evmContract.methods
-        .setProperties(
-          subTokenId,
-          PROPERTIES.slice(0, setup.propertiesNumber),
-        )
-        .encodeABI();
-
-      await helper.eth.sendEVM(
-        donor,
-        evmContract.options.address,
-        encodedCall,
-        '0',
-      );
-    },
-  );
-
-  const ethMintCrossFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Ethereum: ethSigner},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      const evmContract = await helper.ethNativeContract.collection(
-        helper.ethAddress.fromCollectionId(collection.collectionId),
-        'nft',
-      );
-
-      await evmContract.methods.mintCross(
-        helper.ethCrossAccount.fromAddress(receiverEthAddress),
-        PROPERTIES.slice(0, setup.propertiesNumber),
-      )
-        .send({from: ethSigner});
-    },
-  );
-
-  const proxyContractFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Ethereum: ethSigner},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      await proxyContract.methods
-        .mintToSubstrateWithProperty(
-          helper.ethAddress.fromCollectionId(collection.collectionId),
-          susbstrateReceiver.addressRaw,
-          PROPERTIES.slice(0, setup.propertiesNumber),
-        )
-        .send({from: ethSigner});
-    },
-  );
-
-  const proxyContractBulkFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Ethereum: ethSigner},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      await proxyContract.methods
-        .mintToSubstrateBulkProperty(
-          helper.ethAddress.fromCollectionId(collection.collectionId),
-          susbstrateReceiver.addressRaw,
-          PROPERTIES.slice(0, setup.propertiesNumber),
-        )
-        .send({from: ethSigner, gas: 25_000_000});
-    },
-  );
-
-  return {
-    propertiesNumber: setup.propertiesNumber,
-    substrateFee: convertToTokens(substrateFee, nominal),
-    ethFee: convertToTokens(ethFee, nominal),
-    ethBulkFee: convertToTokens(ethBulkFee, nominal),
-    ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal),
-    evmProxyContractFee: convertToTokens(proxyContractFee, nominal),
-    evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),
-  };
-}
-
-async function calculateFeeNftMintWithProperties(
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-  payer: ICrossAccountId,
-  ethSigner: string,
-  proxyContractAddress: string,
-  calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,
-): Promise<bigint> {
-  const collection = (await createCollectionForBenchmarks(
-    'nft',
-    helper,
-    privateKey,
-    ethSigner,
-    proxyContractAddress,
-    PERMISSIONS,
-  )) as UniqueNFTCollection;
-  return helper.arrange.calculcateFee(payer, async () => {
-    await calculatedCall(collection);
-  });
-}
-
-function convertToTokens(value: bigint, nominal = 1000_000_000_000_000_000n): number {
-  return Number((value * 1000n) / nominal) / 1000;
-}
-
-async function erc721CalculateFeeGas(
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-
-): Promise<IFunctionFee> {
-  const res: IFunctionFee = {};
-  const donor = await privateKey('//Alice');
-  const [subReceiver] = await helper.arrange.createAccounts([10n], donor);
-  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
-  const ethReceiver = await helper.eth.createAccountWithBalance(donor, 10n);
-  const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);
-  const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);
-  const collection = (await createCollectionForBenchmarks(
-    'nft',
-    helper,
-    privateKey,
-    ethSigner,
-    null,
-    PERMISSIONS,
-  )) as UniqueNFTCollection;
-
-  const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);
-  let zeppelelinContract: Contract | null = null;
-  const ZEPPELIN_OBJECT = '0x' + (await readFile(`${__dirname}/openZeppelin/ERC721/bin/ZeppelinContract.bin`)).toString();
-  const ZEPPELIN_ABI = JSON.parse((await readFile(`${__dirname}/openZeppelin/ERC721/bin/ZeppelinContract.abi`)).toString());
-
-  const evmContract = await helper.ethNativeContract.collection(
-    helper.ethAddress.fromCollectionId(collection.collectionId),
-    'nft',
-    ethSigner,
-    true,
-  );
-
-  res['createCollection'] = await helper.arrange.calculcateFeeGas(
-    {Ethereum: ethSigner},
-    () => helperContract.methods.createNFTCollection('test','test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}),
-  );
-
-  res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => helper.nft.mintCollection(
-      donor,
-      {name: 'test', description: 'test', tokenPrefix: 'test'},
-    ),
-  )));
-
-  res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas(
-    {Ethereum: ethSigner},
-    async () => {
-      zeppelelinContract = await helper.ethContract.deployByAbi(
-        ethSigner,
-        ZEPPELIN_ABI,
-        ZEPPELIN_OBJECT,
-      );
-    },
-  );
-
-  res['mint'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mint(ethSigner).send(),
-    );
-
-  res['mint'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.safeMint(ethSigner, 'test').send({from: ethSigner}),
-    );
-
-  res['mintCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mintCross(crossSigner, []).send(),
-    );
-
-  res['mint'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.mintToken(
-      donor,
-      {Substrate: donor.address},
-    ),
-  )));
-
-  res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.mintMultipleTokens(donor, [{
-      owner: {Substrate: donor.address},
-    }]),
-  )));
-
-  res['mintWithTokenURI'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mintWithTokenURI(ethSigner, 'Test URI').send(),
-    );
-
-  res['mintWithTokenURI'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.mintToken(
-      donor,
-      {Ethereum: ethSigner},
-      [{key: 'URI', value: 'Test URI'}],
-    ),
-  )));
-
-  res['setProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.setProperties(1, PROPERTIES.slice(0,1)).send(),
-    );
-
-  res['deleteProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.deleteProperties(1, [PROPERTIES[0].key]).send(),
-    );
-
-  res['setProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setTokenProperties(
-      donor,
-      1,
-      SUBS_PROPERTIES.slice(0, 1),
-    ),
-  )));
-
-  res['deleteProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.deleteTokenProperties(
-      donor,
-      1,
-      SUBS_PROPERTIES.slice(0, 1)
-        .map(p => p.key),
-    ),
-  )));
-
-  res['transfer'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.transfer(ethReceiver, 1).send(),
-    );
-
-  res['safeTransferFrom*'] = {
-    zeppelin:
-      await helper.arrange.calculcateFeeGas(
-        {Ethereum: ethSigner},
-        () => zeppelelinContract!.methods.safeTransferFrom(ethSigner, ethReceiver, 0).send({from: ethSigner}),
-      ),
-  };
-
-  res['transferCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),
-    );
-
-  res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.transferToken(
-      donor,
-      3,
-      {Substrate: subReceiver.address},
-    ),
-  )));
-  await collection.approveToken(subReceiver, 3, {Substrate: donor.address});
-
-  res['transferFrom*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),
-    );
-
-  res['transferFrom*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}),
-    );
-
-
-  res['transferFromCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),
-    );
-
-  res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.transferTokenFrom(donor, 3, {Substrate: subReceiver.address}, {Substrate: donor.address}),
-  )));
-
-  res['burn'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.burn(1).send(),
-    );
-
-  res['burn'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.burnToken(donor, 3),
-  )));
-
-  res['approve*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.approve(ethReceiver, 2).send(),
-    );
-
-  res['approve*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.approve(ethReceiver, 0).send({from: ethSigner}),
-    );
-
-  res['approveCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.approveCross(crossReceiver, 2).send(),
-    );
-
-  res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.approveToken(donor, 4, {Substrate: subReceiver.address}),
-  )));
-
-  res['setApprovalForAll*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.setApprovalForAll(ethReceiver, true).send(),
-    );
-
-  res['setApprovalForAll*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.setApprovalForAll(ethReceiver, true).send({from: ethSigner}),
-    );
-
-  res['setApprovalForAll*'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => helper.nft.setAllowanceForAll(donor, collection.collectionId, {Substrate: subReceiver.address}, true),
-  )));
-
-  res['burnFromCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.burnFromCross(crossSigner, 2).send({from:ethReceiver}),
-    );
-
-  res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: subReceiver.address},
-    () => collection.burnTokenFrom(subReceiver, 4, {Substrate: donor.address}),
-  )));
-
-  res['setTokenPropertyPermissions'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setTokenPropertyPermissions([
-        ['url', [
-          [TokenPermissionField.Mutable, true],
-          [TokenPermissionField.TokenOwner, true],
-          [TokenPermissionField.CollectionAdmin, true]],
-        ],
-      ]).send(),
-    );
-
-  res['setTokenPropertyPermissions'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setTokenPropertyPermissions(donor, [{key: 'url', permission: {
-      tokenOwner: true,
-      collectionAdmin: true,
-      mutable: true,
-    }}]),
-  )));
-
-  res['setCollectionSponsorCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),
-    );
-
-  res['confirmCollectionSponsorship'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () =>  evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),
-    );
-
-  res['removeCollectionSponsor'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.removeCollectionSponsor().send(),
-    );
-
-  res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setSponsor(donor, subReceiver.address),
-  )));
-
-  res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: subReceiver.address},
-    () => collection.confirmSponsorship(subReceiver),
-  )));
-
-  res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeSponsor(donor),
-  )));
-
-  res['setCollectionProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),
-    );
-
-  res['deleteCollectionProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),
-    );
-  res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setProperties(donor, PROPERTIES.slice(0, 1)
-      .map(p => { return {key: p.key, value: p.value.toString()}; })),
-  )));
-
-  res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)
-      .map(p => p.key)),
-  )));
-
-  res['setCollectionLimit'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),
-    );
-
-  res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),
-  )));
-
-  const {collectionAddress} = await helper.eth.createCollection('nft', ethSigner, 'A', 'B', 'C');
-  const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);
-
-
-  res['addCollectionAdminCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),
-    );
-
-  res['removeCollectionAdminCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),
-    );
-
-  res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.addAdmin(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['setCollectionNesting'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionNesting(true).send(),
-    );
-
-  res['setCollectionNesting[]'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),
-    );
-
-  res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.disableNesting(donor),
-  )));
-
-  res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(
-      donor,
-      {
-        nesting: {
-          tokenOwner: true,
-          restricted: [collection.collectionId],
-        },
-      },
-    ),
-  )));
-
-  res['setCollectionAccess'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionAccess(1).send(),
-    );
-
-  res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(donor, {access: 'AllowList'}),
-  )));
-
-  res['addToCollectionAllowListCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),
-    );
-
-  res['removeFromCollectionAllowListCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),
-    );
-
-  res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['setCollectionMintMode'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionMintMode(true).send(),
-    );
-
-  res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(donor, {mintMode: false}),
-  )));
-
-  res['changeCollectionOwnerCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),
-    );
-
-  res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.changeOwner(donor, subReceiver.address),
-  )));
-
-  return res;
-}
-
-async function erc20CalculateFeeGas(
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-
-): Promise<IFunctionFee> {
-  const res: IFunctionFee = {};
-  const donor = await privateKey('//Alice');
-  const [subReceiver] = await helper.arrange.createAccounts([10n], donor);
-  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
-  const ethReceiver = await helper.eth.createAccountWithBalance(donor, 10n);
-  const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);
-  const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);
-  const collection = (await createCollectionForBenchmarks(
-    'ft',
-    helper,
-    privateKey,
-    ethSigner,
-    null,
-    PERMISSIONS,
-  )) as UniqueFTCollection;
-
-  const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);
-  let zeppelelinContract: Contract | null = null;
-  const ZEPPELIN_OBJECT = '0x' + (await readFile(`${__dirname}/openZeppelin/ERC20/bin/ZeppelinContract.bin`)).toString();
-  const ZEPPELIN_ABI = JSON.parse((await readFile(`${__dirname}/openZeppelin/ERC20/bin/ZeppelinContract.abi`)).toString());
-
-  const evmContract = await helper.ethNativeContract.collection(
-    helper.ethAddress.fromCollectionId(collection.collectionId),
-    'ft',
-    ethSigner,
-    true,
-  );
-
-  res['createCollection'] = await helper.arrange.calculcateFeeGas(
-    {Ethereum: ethSigner},
-    () => helperContract.methods.createFTCollection('test', 18,'test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}),
-  );
-
-  res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => helper.ft.mintCollection(
-      donor,
-      {name: 'test', description: 'test', tokenPrefix: 'test'},
-      18,
-    ),
-  )));
-
-  res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas(
-    {Ethereum: ethSigner},
-    async () => {
-      zeppelelinContract = await helper.ethContract.deployByAbi(
-        ethSigner,
-        ZEPPELIN_ABI,
-        ZEPPELIN_OBJECT,
-      );
-    },
-  );
-
-  res['mint'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mint(ethSigner, 1).send(),
-    );
-
-  res['mint'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.mint(ethSigner, 1).send(),
-    );
-
-  res['mintCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mintCross(crossSigner, 1).send(),
-    );
-
-  res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.mint(
-      donor,
-      10n,
-      {Substrate: donor.address},
-    ),
-  )));
-
-  res['mintBulk'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mintBulk([{to: ethSigner, amount: 1}]).send(),
-    );
-
-  res['mintBulk'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => helper.executeExtrinsic(donor, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, {
-      Fungible: new Map([
-        [JSON.stringify({Ethereum: ethSigner}), 1],
-
-      ]),
-    }], true),
-  )));
-
-  res['transfer*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.transfer(ethReceiver, 1).send(),
-    );
-
-  res['transfer*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.transfer(ethReceiver, 1).send(),
-    );
-
-  res['transferCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),
-    );
-
-  res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.transfer(
-      donor,
-      {Substrate: subReceiver.address},
-      1n,
-    ),
-  )));
-  await collection.approveTokens(subReceiver, {Substrate: donor.address}, 1n);
-
-  res['transferFrom*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),
-    );
-
-  res['transferFrom*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}),
-    );
-
-  res['transferFromCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),
-    );
-
-  res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.transferFrom(donor, {Substrate: subReceiver.address}, {Substrate: donor.address}, 1n),
-  )));
-
-
-  res['burnTokens'] = {fee: 0n, gas: 0n};
-  res['burnTokens'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.burnTokens(donor, 1n),
-  )));
-
-
-  res['approve*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.approve(ethReceiver, 2).send(),
-    );
-
-  res['approve*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.approve(ethReceiver, 0).send(),
-    );
-
-  res['approveCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.approveCross(crossReceiver, 2).send(),
-    );
-
-  res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.approveTokens(donor, {Substrate: subReceiver.address}, 1n),
-  )));
-
-  res['burnFromCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.burnFromCross(crossSigner, 1).send({from:ethReceiver}),
-    );
-
-  res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: subReceiver.address},
-    () => collection.burnTokensFrom(subReceiver, {Substrate: donor.address}, 1n),
-  )));
-
-  res['setCollectionSponsorCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),
-    );
-
-  res['confirmCollectionSponsorship'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () =>  evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),
-    );
-
-  res['removeCollectionSponsor'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.removeCollectionSponsor().send(),
-    );
-
-  res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setSponsor(donor, subReceiver.address),
-  )));
-
-  res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: subReceiver.address},
-    () => collection.confirmSponsorship(subReceiver),
-  )));
-
-  res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeSponsor(donor),
-  )));
-
-  res['setCollectionProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),
-    );
-
-  res['deleteCollectionProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),
-    );
-  res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setProperties(donor, PROPERTIES.slice(0, 1)
-      .map(p => { return {key: p.key, value: p.value.toString()}; })),
-  )));
-
-  res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)
-      .map(p => p.key)),
-  )));
-
-  res['setCollectionLimit'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),
-    );
-
-  res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),
-  )));
-
-  const {collectionAddress} = await helper.eth.createCollection('nft', ethSigner, 'A', 'B', 'C');
-  const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);
-
-
-  res['addCollectionAdminCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),
-    );
-
-  res['removeCollectionAdminCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),
-    );
-
-  res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.addAdmin(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['setCollectionNesting'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionNesting(true).send(),
-    );
-
-  res['setCollectionNesting[]'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),
-    );
-
-  res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.disableNesting(donor),
-  )));
-
-  res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(
-      donor,
-      {
-        nesting: {
-          tokenOwner: true,
-          restricted: [collection.collectionId],
-        },
-      },
-    ),
-  )));
-
-  res['setCollectionAccess'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionAccess(1).send(),
-    );
-
-  res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(donor, {access: 'AllowList'}),
-  )));
-
-  res['addToCollectionAllowListCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),
-    );
-
-  res['removeFromCollectionAllowListCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),
-    );
-
-  res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['setCollectionMintMode'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionMintMode(true).send(),
-    );
-
-  res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(donor, {mintMode: false}),
-  )));
-
-  res['changeCollectionOwnerCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),
-    );
-
-  res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.changeOwner(donor, subReceiver.address),
-  )));
-
-  return res;
-}
\ No newline at end of file
addedtests/src/benchmarks/mintFee/common.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/benchmarks/mintFee/common.ts
@@ -0,0 +1,118 @@
+import {EthUniqueHelper} from '../../eth/util';
+import {ITokenPropertyPermission, TCollectionMode} from '../../util/playgrounds/types';
+import {UniqueNFTCollection, UniqueRFTCollection} from '../../util/playgrounds/unique';
+import {IKeyringPair} from '@polkadot/types/types';
+import {ContractImports} from '../../eth/util/playgrounds/types';
+
+export const CONTRACT_IMPORT: ContractImports[] = [
+  {
+    fsPath: `${__dirname}/../../eth/api/CollectionHelpers.sol`,
+    solPath: 'eth/api/CollectionHelpers.sol',
+  },
+  {
+    fsPath: `${__dirname}/../../eth/api/ContractHelpers.sol`,
+    solPath: 'eth/api/ContractHelpers.sol',
+  },
+  {
+    fsPath: `${__dirname}/../../eth/api/UniqueRefungibleToken.sol`,
+    solPath: 'eth/api/UniqueRefungibleToken.sol',
+  },
+  {
+    fsPath: `${__dirname}/../../eth/api/UniqueRefungible.sol`,
+    solPath: 'eth/api/UniqueRefungible.sol',
+  },
+  {
+    fsPath: `${__dirname}/../../eth/api/UniqueNFT.sol`,
+    solPath: 'eth/api/UniqueNFT.sol',
+  },
+];
+
+export const PROPERTIES = Array(40)
+  .fill(0)
+  .map((_, i) => {
+    return {
+      key: `key_${i}`,
+      value: Uint8Array.from(Buffer.from(`value_${i}`)),
+    };
+  });
+
+export const SUBS_PROPERTIES = Array(40)
+  .fill(0)
+  .map((_, i) => {
+    return {
+      key: `key_${i}`,
+      value: `value_${i}`,
+    };
+  });
+
+export const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => {
+  return {
+    key: p.key,
+    permission: {
+      tokenOwner: true,
+      collectionAdmin: true,
+      mutable: true,
+    },
+  };
+});
+
+export function convertToTokens(value: bigint, nominal = 1000_000_000_000_000_000n): number {
+  return Number((value * 1000n) / nominal) / 1000;
+}
+
+export async function createCollectionForBenchmarks(
+  mode : TCollectionMode,
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  ethSigner: string,
+  proxyContract: string | null,
+  permissions: ITokenPropertyPermission[],
+) {
+  const donor = await privateKey('//Alice');
+
+  const collection = await helper[mode].mintCollection(donor, {
+    name: 'test mintToSubstrate',
+    description: 'EVMHelpers',
+    tokenPrefix: mode,
+    ...(mode != 'ft' ? {
+      tokenPropertyPermissions: [
+        {
+          key: 'url',
+          permission: {
+            tokenOwner: true,
+            collectionAdmin: true,
+            mutable: true,
+          },
+        },
+        {
+          key: 'URI',
+          permission: {
+            tokenOwner: true,
+            collectionAdmin: true,
+            mutable: true,
+          },
+        },
+      ],
+    } : {}),
+    limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0},
+    permissions: {mintMode: true},
+  });
+
+  await collection.addToAllowList(donor, {
+    Ethereum: helper.address.substrateToEth(donor.address),
+  });
+  await collection.addToAllowList(donor, {Substrate: donor.address});
+  await collection.addAdmin(donor, {Ethereum: ethSigner});
+  await collection.addAdmin(donor, {
+    Ethereum: helper.address.substrateToEth(donor.address),
+  });
+
+  if (proxyContract) {
+    await collection.addToAllowList(donor, {Ethereum: proxyContract});
+    await collection.addAdmin(donor, {Ethereum: proxyContract});
+  }
+  if (collection instanceof UniqueNFTCollection || collection instanceof UniqueRFTCollection)
+    await collection.setTokenPropertyPermissions(donor, permissions);
+
+  return collection;
+}
\ No newline at end of file
addedtests/src/benchmarks/mintFee/mintFee.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/benchmarks/mintFee/mintFee.ts
@@ -0,0 +1,377 @@
+import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';
+import {readFile} from 'fs/promises';
+import {
+  ICrossAccountId,
+} from '../../util/playgrounds/types';
+import {IKeyringPair} from '@polkadot/types/types';
+import {UniqueNFTCollection} from '../../util/playgrounds/unique';
+import {Contract} from 'web3-eth-contract';
+import {createObjectCsvWriter} from 'csv-writer';
+import {CONTRACT_IMPORT, convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES} from './common';
+
+
+
+interface IBenchmarkResultForProp {
+	propertiesNumber: number;
+	substrateFee: number;
+	ethFee: number;
+	ethBulkFee: number;
+  ethMintCrossFee: number;
+	evmProxyContractFee: number;
+	evmProxyContractBulkFee: number;
+}
+
+const main = async () => {
+  const benchmarks = [
+    'substrateFee',
+    'ethFee',
+    'ethBulkFee',
+    'ethMintCrossFee',
+    'evmProxyContractFee',
+    'evmProxyContractBulkFee',
+  ];
+  const headers = [
+    'propertiesNumber',
+    ...benchmarks,
+  ];
+
+
+  const csvWriter = createObjectCsvWriter({
+    path: 'properties.csv',
+    header: headers,
+  });
+
+  await usingEthPlaygrounds(async (helper, privateKey) => {
+    const CONTRACT_SOURCE = (
+      await readFile(`${__dirname}/proxyContract.sol`)
+    ).toString();
+
+    const donor = await privateKey('//Alice'); // Seed from account with balance on this network
+    const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
+
+    const contract = await helper.ethContract.deployByCode(
+      ethSigner,
+      'ProxyMint',
+      CONTRACT_SOURCE,
+      CONTRACT_IMPORT,
+    );
+
+    const fees = await benchMintFee(helper, privateKey, contract);
+    console.log('Minting without properties');
+    console.table(fees);
+
+    const result: IBenchmarkResultForProp[] = [];
+    const csvResult: IBenchmarkResultForProp[] = [];
+
+    for (let i = 1; i <= 20; i++) {
+      const benchResult = await benchMintWithProperties(helper, privateKey, contract, {
+        propertiesNumber: i,
+      }) as any;
+
+      csvResult.push(benchResult);
+
+      const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));
+      for(const key of benchmarks) {
+        const keyPercent = Math.round((benchResult[key] / minFee) * 100);
+        benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;
+      }
+
+      result.push(benchResult);
+    }
+
+    await csvWriter.writeRecords(csvResult);
+
+    console.log('Minting with properties');
+    console.table(result, headers);
+  });
+};
+
+main()
+  .then(() => process.exit(0))
+  .catch((e) => {
+    console.log(e);
+    process.exit(1);
+  });
+
+
+
+async function benchMintFee(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  proxyContract: Contract,
+): Promise<{
+	substrateFee: number;
+	ethFee: number;
+	evmProxyContractFee: number;
+}> {
+  const donor = await privateKey('//Alice');
+  const substrateReceiver = await privateKey('//Bob');
+  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
+
+  const nominal = helper.balance.getOneTokenNominal();
+
+  await helper.eth.transferBalanceFromSubstrate(
+    donor,
+    proxyContract.options.address,
+    100n,
+  );
+
+  const collection = (await createCollectionForBenchmarks(
+    'nft',
+    helper,
+    privateKey,
+    ethSigner,
+    proxyContract.options.address,
+    PERMISSIONS,
+  )) as UniqueNFTCollection;
+
+  const substrateFee = await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),
+  );
+
+  const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+  const collectionContract = await helper.ethNativeContract.collection(
+    collectionEthAddress,
+    'nft',
+  );
+
+  const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);
+
+  const encodedCall = collectionContract.methods
+    .mint(receiverEthAddress)
+    .encodeABI();
+
+  const ethFee = await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    async () => {
+      await helper.eth.sendEVM(
+        donor,
+        collectionContract.options.address,
+        encodedCall,
+        '0',
+      );
+    },
+  );
+
+  const evmProxyContractFee = await helper.arrange.calculcateFee(
+    {Ethereum: ethSigner},
+    async () => {
+      await proxyContract.methods
+        .mintToSubstrate(
+          helper.ethAddress.fromCollectionId(collection.collectionId),
+          substrateReceiver.addressRaw,
+        )
+        .send({from: ethSigner});
+    },
+  );
+
+  return {
+    substrateFee: convertToTokens(substrateFee, nominal),
+    ethFee: convertToTokens(ethFee, nominal),
+    evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),
+  };
+}
+
+async function benchMintWithProperties(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  proxyContract: Contract,
+  setup: { propertiesNumber: number },
+): Promise<IBenchmarkResultForProp> {
+  const donor = await privateKey('//Alice'); // Seed from account with balance on this network
+  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
+
+  const susbstrateReceiver = await privateKey('//Bob');
+  const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);
+
+  const nominal = helper.balance.getOneTokenNominal();
+
+  const substrateFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Substrate: donor.address},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      await collection.mintToken(
+        donor,
+        {Substrate: susbstrateReceiver.address},
+        PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {
+          return {key: p.key, value: Buffer.from(p.value).toString()};
+        }),
+      );
+    },
+  );
+
+  const ethFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Substrate: donor.address},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      const evmContract = await helper.ethNativeContract.collection(
+        helper.ethAddress.fromCollectionId(collection.collectionId),
+        'nft',
+        undefined,
+        true,
+      );
+
+      const subTokenId = await evmContract.methods.nextTokenId().call();
+
+      let encodedCall = evmContract.methods
+        .mint(receiverEthAddress)
+        .encodeABI();
+
+      await helper.eth.sendEVM(
+        donor,
+        evmContract.options.address,
+        encodedCall,
+        '0',
+      );
+
+      for (const val of PROPERTIES.slice(0, setup.propertiesNumber)) {
+        encodedCall = await evmContract.methods
+          .setProperty(subTokenId, val.key, Buffer.from(val.value))
+          .encodeABI();
+
+        await helper.eth.sendEVM(
+          donor,
+          evmContract.options.address,
+          encodedCall,
+          '0',
+        );
+      }
+    },
+  );
+
+  const ethBulkFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Substrate: donor.address},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      const evmContract = await helper.ethNativeContract.collection(
+        helper.ethAddress.fromCollectionId(collection.collectionId),
+        'nft',
+      );
+
+      const subTokenId = await evmContract.methods.nextTokenId().call();
+
+      let encodedCall = evmContract.methods
+        .mint(receiverEthAddress)
+        .encodeABI();
+
+      await helper.eth.sendEVM(
+        donor,
+        evmContract.options.address,
+        encodedCall,
+        '0',
+      );
+
+      encodedCall = await evmContract.methods
+        .setProperties(
+          subTokenId,
+          PROPERTIES.slice(0, setup.propertiesNumber),
+        )
+        .encodeABI();
+
+      await helper.eth.sendEVM(
+        donor,
+        evmContract.options.address,
+        encodedCall,
+        '0',
+      );
+    },
+  );
+
+  const ethMintCrossFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Ethereum: ethSigner},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      const evmContract = await helper.ethNativeContract.collection(
+        helper.ethAddress.fromCollectionId(collection.collectionId),
+        'nft',
+      );
+
+      await evmContract.methods.mintCross(
+        helper.ethCrossAccount.fromAddress(receiverEthAddress),
+        PROPERTIES.slice(0, setup.propertiesNumber),
+      )
+        .send({from: ethSigner});
+    },
+  );
+
+  const proxyContractFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Ethereum: ethSigner},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      await proxyContract.methods
+        .mintToSubstrateWithProperty(
+          helper.ethAddress.fromCollectionId(collection.collectionId),
+          susbstrateReceiver.addressRaw,
+          PROPERTIES.slice(0, setup.propertiesNumber),
+        )
+        .send({from: ethSigner});
+    },
+  );
+
+  const proxyContractBulkFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Ethereum: ethSigner},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      await proxyContract.methods
+        .mintToSubstrateBulkProperty(
+          helper.ethAddress.fromCollectionId(collection.collectionId),
+          susbstrateReceiver.addressRaw,
+          PROPERTIES.slice(0, setup.propertiesNumber),
+        )
+        .send({from: ethSigner, gas: 25_000_000});
+    },
+  );
+
+  return {
+    propertiesNumber: setup.propertiesNumber,
+    substrateFee: convertToTokens(substrateFee, nominal),
+    ethFee: convertToTokens(ethFee, nominal),
+    ethBulkFee: convertToTokens(ethBulkFee, nominal),
+    ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal),
+    evmProxyContractFee: convertToTokens(proxyContractFee, nominal),
+    evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),
+  };
+}
+
+async function calculateFeeNftMintWithProperties(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  payer: ICrossAccountId,
+  ethSigner: string,
+  proxyContractAddress: string,
+  calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,
+): Promise<bigint> {
+  const collection = (await createCollectionForBenchmarks(
+    'nft',
+    helper,
+    privateKey,
+    ethSigner,
+    proxyContractAddress,
+    PERMISSIONS,
+  )) as UniqueNFTCollection;
+  return helper.arrange.calculcateFee(payer, async () => {
+    await calculatedCall(collection);
+  });
+}
+
+
+
addedtests/src/benchmarks/mintFee/opsFee.tsdiffbeforeafterboth
after · tests/src/benchmarks/mintFee/opsFee.ts
1import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';2import {readFile} from 'fs/promises';3import {CollectionLimitField,  TokenPermissionField} from '../../eth/util/playgrounds/types';4import {IKeyringPair} from '@polkadot/types/types';5import {UniqueFTCollection, UniqueNFTCollection} from '../../util/playgrounds/unique';6import {Contract} from 'web3-eth-contract';7import {createObjectCsvWriter} from 'csv-writer';8import {FunctionFeeVM, IFunctionFee} from './types';9import {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES, SUBS_PROPERTIES} from './common';10111213const main = async () => {1415  const headers = [16    'function',17    'ethFee',18    'ethGas',19    'substrate',20    'zeppelinFee',21    'zeppelinGas',22  ];2324  const csvWriter20 = createObjectCsvWriter({25    path: 'erc20.csv',26    header: headers,27  });2829  const csvWriter721 = createObjectCsvWriter({30    path: 'erc721.csv',31    header: headers,32  });3334  await usingEthPlaygrounds(async (helper, privateKey) => {35    console.log('\n ERC20 ops fees');36    const erc20 = FunctionFeeVM.fromModel(await erc20CalculateFeeGas(helper, privateKey));37    console.table(erc20);38    await csvWriter20.writeRecords(FunctionFeeVM.toCsv(erc20));3940    console.log('\n ERC721 ops fees');41    const erc721 = FunctionFeeVM.fromModel(await erc721CalculateFeeGas(helper, privateKey));42    console.table(erc721);4344    await csvWriter721.writeRecords(FunctionFeeVM.toCsv(erc721));45  });46};4748main()49  .then(() => process.exit(0))50  .catch((e) => {51    console.log(e);52    process.exit(1);53  });5455async function erc721CalculateFeeGas(56  helper: EthUniqueHelper,57  privateKey: (seed: string) => Promise<IKeyringPair>,58): Promise<IFunctionFee> {59  const res: IFunctionFee = {};60  const donor = await privateKey('//Alice');61  const [subReceiver] = await helper.arrange.createAccounts([10n], donor);62  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);63  const ethReceiver = await helper.eth.createAccountWithBalance(donor, 10n);64  const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);65  const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);66  const collection = (await createCollectionForBenchmarks(67    'nft',68    helper,69    privateKey,70    ethSigner,71    null,72    PERMISSIONS,73  )) as UniqueNFTCollection;7475  const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);76  let zeppelelinContract: Contract | null = null;77  const ZEPPELIN_OBJECT = '0x' + (await readFile(`${__dirname}/openZeppelin/ERC721/bin/ZeppelinContract.bin`)).toString();78  const ZEPPELIN_ABI = JSON.parse((await readFile(`${__dirname}/openZeppelin/ERC721/bin/ZeppelinContract.abi`)).toString());7980  const evmContract = await helper.ethNativeContract.collection(81    helper.ethAddress.fromCollectionId(collection.collectionId),82    'nft',83    ethSigner,84    true,85  );8687  res['createCollection'] = await helper.arrange.calculcateFeeGas(88    {Ethereum: ethSigner},89    () => helperContract.methods.createNFTCollection('test','test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}),90  );9192  res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee(93    {Substrate: donor.address},94    () => helper.nft.mintCollection(95      donor,96      {name: 'test', description: 'test', tokenPrefix: 'test'},97    ),98  )));99100  res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas(101    {Ethereum: ethSigner},102    async () => {103      zeppelelinContract = await helper.ethContract.deployByAbi(104        ethSigner,105        ZEPPELIN_ABI,106        ZEPPELIN_OBJECT,107      );108    },109  );110111  res['mint'] =112    await helper.arrange.calculcateFeeGas(113      {Ethereum: ethSigner},114      () => evmContract.methods.mint(ethSigner).send(),115    );116117  res['mint'].zeppelin =118    await helper.arrange.calculcateFeeGas(119      {Ethereum: ethSigner},120      () => zeppelelinContract!.methods.safeMint(ethSigner, 'test').send({from: ethSigner}),121    );122123  res['mintCross'] =124    await helper.arrange.calculcateFeeGas(125      {Ethereum: ethSigner},126      () => evmContract.methods.mintCross(crossSigner, []).send(),127    );128129  res['mint'].substrate = convertToTokens((await helper.arrange.calculcateFee(130    {Substrate: donor.address},131    () => collection.mintToken(132      donor,133      {Substrate: donor.address},134    ),135  )));136137  res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(138    {Substrate: donor.address},139    () => collection.mintMultipleTokens(donor, [{140      owner: {Substrate: donor.address},141    }]),142  )));143144  res['mintWithTokenURI'] =145    await helper.arrange.calculcateFeeGas(146      {Ethereum: ethSigner},147      () => evmContract.methods.mintWithTokenURI(ethSigner, 'Test URI').send(),148    );149150  res['mintWithTokenURI'].substrate = convertToTokens((await helper.arrange.calculcateFee(151    {Substrate: donor.address},152    () => collection.mintToken(153      donor,154      {Ethereum: ethSigner},155      [{key: 'URI', value: 'Test URI'}],156    ),157  )));158159  res['setProperties'] =160    await helper.arrange.calculcateFeeGas(161      {Ethereum: ethSigner},162      () => evmContract.methods.setProperties(1, PROPERTIES.slice(0,1)).send(),163    );164165  res['deleteProperties'] =166    await helper.arrange.calculcateFeeGas(167      {Ethereum: ethSigner},168      () => evmContract.methods.deleteProperties(1, [PROPERTIES[0].key]).send(),169    );170171  res['setProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(172    {Substrate: donor.address},173    () => collection.setTokenProperties(174      donor,175      1,176      SUBS_PROPERTIES.slice(0, 1),177    ),178  )));179180  res['deleteProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(181    {Substrate: donor.address},182    () => collection.deleteTokenProperties(183      donor,184      1,185      SUBS_PROPERTIES.slice(0, 1)186        .map(p => p.key),187    ),188  )));189190  res['transfer'] =191    await helper.arrange.calculcateFeeGas(192      {Ethereum: ethSigner},193      () => evmContract.methods.transfer(ethReceiver, 1).send(),194    );195196  res['safeTransferFrom*'] = {197    zeppelin:198      await helper.arrange.calculcateFeeGas(199        {Ethereum: ethSigner},200        () => zeppelelinContract!.methods.safeTransferFrom(ethSigner, ethReceiver, 0).send({from: ethSigner}),201      ),202  };203204  res['transferCross'] =205    await helper.arrange.calculcateFeeGas(206      {Ethereum: ethReceiver},207      () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),208    );209210  res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(211    {Substrate: donor.address},212    () => collection.transferToken(213      donor,214      3,215      {Substrate: subReceiver.address},216    ),217  )));218  await collection.approveToken(subReceiver, 3, {Substrate: donor.address});219220  res['transferFrom*'] =221    await helper.arrange.calculcateFeeGas(222      {Ethereum: ethSigner},223      () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),224    );225226  res['transferFrom*'].zeppelin =227    await helper.arrange.calculcateFeeGas(228      {Ethereum: ethReceiver},229      () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}),230    );231232233  res['transferFromCross'] =234    await helper.arrange.calculcateFeeGas(235      {Ethereum: ethReceiver},236      () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),237    );238239  res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(240    {Substrate: donor.address},241    () => collection.transferTokenFrom(donor, 3, {Substrate: subReceiver.address}, {Substrate: donor.address}),242  )));243244  res['burn'] =245    await helper.arrange.calculcateFeeGas(246      {Ethereum: ethSigner},247      () => evmContract.methods.burn(1).send(),248    );249250  res['burn'].substrate = convertToTokens((await helper.arrange.calculcateFee(251    {Substrate: donor.address},252    () => collection.burnToken(donor, 3),253  )));254255  res['approve*'] =256    await helper.arrange.calculcateFeeGas(257      {Ethereum: ethSigner},258      () => evmContract.methods.approve(ethReceiver, 2).send(),259    );260261  res['approve*'].zeppelin =262    await helper.arrange.calculcateFeeGas(263      {Ethereum: ethSigner},264      () => zeppelelinContract!.methods.approve(ethReceiver, 0).send({from: ethSigner}),265    );266267  res['approveCross'] =268    await helper.arrange.calculcateFeeGas(269      {Ethereum: ethSigner},270      () => evmContract.methods.approveCross(crossReceiver, 2).send(),271    );272273  res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(274    {Substrate: donor.address},275    () => collection.approveToken(donor, 4, {Substrate: subReceiver.address}),276  )));277278  res['setApprovalForAll*'] =279    await helper.arrange.calculcateFeeGas(280      {Ethereum: ethSigner},281      () => evmContract.methods.setApprovalForAll(ethReceiver, true).send(),282    );283284  res['setApprovalForAll*'].zeppelin =285    await helper.arrange.calculcateFeeGas(286      {Ethereum: ethSigner},287      () => zeppelelinContract!.methods.setApprovalForAll(ethReceiver, true).send({from: ethSigner}),288    );289290  res['setApprovalForAll*'].substrate = convertToTokens((await helper.arrange.calculcateFee(291    {Substrate: donor.address},292    () => helper.nft.setAllowanceForAll(donor, collection.collectionId, {Substrate: subReceiver.address}, true),293  )));294295  res['burnFromCross'] =296    await helper.arrange.calculcateFeeGas(297      {Ethereum: ethReceiver},298      () => evmContract.methods.burnFromCross(crossSigner, 2).send({from:ethReceiver}),299    );300301  res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(302    {Substrate: subReceiver.address},303    () => collection.burnTokenFrom(subReceiver, 4, {Substrate: donor.address}),304  )));305306  res['setTokenPropertyPermissions'] =307    await helper.arrange.calculcateFeeGas(308      {Ethereum: ethSigner},309      () =>  evmContract.methods.setTokenPropertyPermissions([310        ['url', [311          [TokenPermissionField.Mutable, true],312          [TokenPermissionField.TokenOwner, true],313          [TokenPermissionField.CollectionAdmin, true]],314        ],315      ]).send(),316    );317318  res['setTokenPropertyPermissions'].substrate = convertToTokens((await helper.arrange.calculcateFee(319    {Substrate: donor.address},320    () => collection.setTokenPropertyPermissions(donor, [{key: 'url', permission: {321      tokenOwner: true,322      collectionAdmin: true,323      mutable: true,324    }}]),325  )));326327  res['setCollectionSponsorCross'] =328    await helper.arrange.calculcateFeeGas(329      {Ethereum: ethSigner},330      () =>  evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),331    );332333  res['confirmCollectionSponsorship'] =334    await helper.arrange.calculcateFeeGas(335      {Ethereum: ethReceiver},336      () =>  evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),337    );338339  res['removeCollectionSponsor'] =340    await helper.arrange.calculcateFeeGas(341      {Ethereum: ethSigner},342      () =>  evmContract.methods.removeCollectionSponsor().send(),343    );344345  res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(346    {Substrate: donor.address},347    () => collection.setSponsor(donor, subReceiver.address),348  )));349350  res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(351    {Substrate: subReceiver.address},352    () => collection.confirmSponsorship(subReceiver),353  )));354355  res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(356    {Substrate: donor.address},357    () => collection.removeSponsor(donor),358  )));359360  res['setCollectionProperties'] =361    await helper.arrange.calculcateFeeGas(362      {Ethereum: ethSigner},363      () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),364    );365366  res['deleteCollectionProperties'] =367    await helper.arrange.calculcateFeeGas(368      {Ethereum: ethSigner},369      () =>  evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),370    );371  res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(372    {Substrate: donor.address},373    () => collection.setProperties(donor, PROPERTIES.slice(0, 1)374      .map(p => { return {key: p.key, value: p.value.toString()}; })),375  )));376377  res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(378    {Substrate: donor.address},379    () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)380      .map(p => p.key)),381  )));382383  res['setCollectionLimit'] =384    await helper.arrange.calculcateFeeGas(385      {Ethereum: ethSigner},386      () =>  evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),387    );388389  res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(390    {Substrate: donor.address},391    () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),392  )));393394  const {collectionAddress} = await helper.eth.createCollection('nft', ethSigner, 'A', 'B', 'C');395  const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);396397398  res['addCollectionAdminCross'] =399    await helper.arrange.calculcateFeeGas(400      {Ethereum: ethSigner},401      () =>  collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),402    );403404  res['removeCollectionAdminCross'] =405    await helper.arrange.calculcateFeeGas(406      {Ethereum: ethSigner},407      () =>  collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),408    );409410  res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(411    {Substrate: donor.address},412    () => collection.addAdmin(donor, {Ethereum: ethReceiver}),413  )));414415  res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(416    {Substrate: donor.address},417    () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),418  )));419420  res['setCollectionNesting'] =421    await helper.arrange.calculcateFeeGas(422      {Ethereum: ethSigner},423      () =>  evmContract.methods.setCollectionNesting(true).send(),424    );425426  res['setCollectionNesting[]'] =427    await helper.arrange.calculcateFeeGas(428      {Ethereum: ethSigner},429      () =>  evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),430    );431432  res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(433    {Substrate: donor.address},434    () => collection.disableNesting(donor),435  )));436437  res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(438    {Substrate: donor.address},439    () => collection.setPermissions(440      donor,441      {442        nesting: {443          tokenOwner: true,444          restricted: [collection.collectionId],445        },446      },447    ),448  )));449450  res['setCollectionAccess'] =451    await helper.arrange.calculcateFeeGas(452      {Ethereum: ethSigner},453      () =>  evmContract.methods.setCollectionAccess(1).send(),454    );455456  res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(457    {Substrate: donor.address},458    () => collection.setPermissions(donor, {access: 'AllowList'}),459  )));460461  res['addToCollectionAllowListCross'] =462    await helper.arrange.calculcateFeeGas(463      {Ethereum: ethSigner},464      () =>  evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),465    );466467  res['removeFromCollectionAllowListCross'] =468    await helper.arrange.calculcateFeeGas(469      {Ethereum: ethSigner},470      () =>  evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),471    );472473  res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(474    {Substrate: donor.address},475    () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),476  )));477478  res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(479    {Substrate: donor.address},480    () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),481  )));482483  res['setCollectionMintMode'] =484    await helper.arrange.calculcateFeeGas(485      {Ethereum: ethSigner},486      () =>  evmContract.methods.setCollectionMintMode(true).send(),487    );488489  res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(490    {Substrate: donor.address},491    () => collection.setPermissions(donor, {mintMode: false}),492  )));493494  res['changeCollectionOwnerCross'] =495    await helper.arrange.calculcateFeeGas(496      {Ethereum: ethSigner},497      () =>  collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),498    );499500  res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(501    {Substrate: donor.address},502    () => collection.changeOwner(donor, subReceiver.address),503  )));504505  return res;506}507508async function erc20CalculateFeeGas(509  helper: EthUniqueHelper,510  privateKey: (seed: string) => Promise<IKeyringPair>,511512): Promise<IFunctionFee> {513  const res: IFunctionFee = {};514  const donor = await privateKey('//Alice');515  const [subReceiver] = await helper.arrange.createAccounts([10n], donor);516  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);517  const ethReceiver = await helper.eth.createAccountWithBalance(donor, 10n);518  const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);519  const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);520  const collection = (await createCollectionForBenchmarks(521    'ft',522    helper,523    privateKey,524    ethSigner,525    null,526    PERMISSIONS,527  )) as UniqueFTCollection;528529  const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);530  let zeppelelinContract: Contract | null = null;531  const ZEPPELIN_OBJECT = '0x' + (await readFile(`${__dirname}/openZeppelin/ERC20/bin/ZeppelinContract.bin`)).toString();532  const ZEPPELIN_ABI = JSON.parse((await readFile(`${__dirname}/openZeppelin/ERC20/bin/ZeppelinContract.abi`)).toString());533534  const evmContract = await helper.ethNativeContract.collection(535    helper.ethAddress.fromCollectionId(collection.collectionId),536    'ft',537    ethSigner,538    true,539  );540541  res['createCollection'] = await helper.arrange.calculcateFeeGas(542    {Ethereum: ethSigner},543    () => helperContract.methods.createFTCollection('test', 18,'test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}),544  );545546  res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee(547    {Substrate: donor.address},548    () => helper.ft.mintCollection(549      donor,550      {name: 'test', description: 'test', tokenPrefix: 'test'},551      18,552    ),553  )));554555  res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas(556    {Ethereum: ethSigner},557    async () => {558      zeppelelinContract = await helper.ethContract.deployByAbi(559        ethSigner,560        ZEPPELIN_ABI,561        ZEPPELIN_OBJECT,562      );563    },564  );565566  res['mint'] =567    await helper.arrange.calculcateFeeGas(568      {Ethereum: ethSigner},569      () => evmContract.methods.mint(ethSigner, 1).send(),570    );571572  res['mint'].zeppelin =573    await helper.arrange.calculcateFeeGas(574      {Ethereum: ethSigner},575      () => zeppelelinContract!.methods.mint(ethSigner, 1).send(),576    );577578  res['mintCross'] =579    await helper.arrange.calculcateFeeGas(580      {Ethereum: ethSigner},581      () => evmContract.methods.mintCross(crossSigner, 1).send(),582    );583584  res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(585    {Substrate: donor.address},586    () => collection.mint(587      donor,588      10n,589      {Substrate: donor.address},590    ),591  )));592593  res['mintBulk'] =594    await helper.arrange.calculcateFeeGas(595      {Ethereum: ethSigner},596      () => evmContract.methods.mintBulk([{to: ethSigner, amount: 1}]).send(),597    );598599  res['mintBulk'].substrate = convertToTokens((await helper.arrange.calculcateFee(600    {Substrate: donor.address},601    () => helper.executeExtrinsic(donor, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, {602      Fungible: new Map([603        [JSON.stringify({Ethereum: ethSigner}), 1],604605      ]),606    }], true),607  )));608609  res['transfer*'] =610    await helper.arrange.calculcateFeeGas(611      {Ethereum: ethSigner},612      () => evmContract.methods.transfer(ethReceiver, 1).send(),613    );614615  res['transfer*'].zeppelin =616    await helper.arrange.calculcateFeeGas(617      {Ethereum: ethSigner},618      () => zeppelelinContract!.methods.transfer(ethReceiver, 1).send(),619    );620621  res['transferCross'] =622    await helper.arrange.calculcateFeeGas(623      {Ethereum: ethReceiver},624      () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),625    );626627  res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(628    {Substrate: donor.address},629    () => collection.transfer(630      donor,631      {Substrate: subReceiver.address},632      1n,633    ),634  )));635  await collection.approveTokens(subReceiver, {Substrate: donor.address}, 1n);636637  res['transferFrom*'] =638    await helper.arrange.calculcateFeeGas(639      {Ethereum: ethSigner},640      () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),641    );642643  res['transferFrom*'].zeppelin =644    await helper.arrange.calculcateFeeGas(645      {Ethereum: ethReceiver},646      () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}),647    );648649  res['transferFromCross'] =650    await helper.arrange.calculcateFeeGas(651      {Ethereum: ethReceiver},652      () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),653    );654655  res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(656    {Substrate: donor.address},657    () => collection.transferFrom(donor, {Substrate: subReceiver.address}, {Substrate: donor.address}, 1n),658  )));659660661  res['burnTokens'] = {fee: 0n, gas: 0n};662  res['burnTokens'].substrate = convertToTokens((await helper.arrange.calculcateFee(663    {Substrate: donor.address},664    () => collection.burnTokens(donor, 1n),665  )));666667668  res['approve*'] =669    await helper.arrange.calculcateFeeGas(670      {Ethereum: ethSigner},671      () => evmContract.methods.approve(ethReceiver, 2).send(),672    );673674  res['approve*'].zeppelin =675    await helper.arrange.calculcateFeeGas(676      {Ethereum: ethSigner},677      () => zeppelelinContract!.methods.approve(ethReceiver, 0).send(),678    );679680  res['approveCross'] =681    await helper.arrange.calculcateFeeGas(682      {Ethereum: ethSigner},683      () => evmContract.methods.approveCross(crossReceiver, 2).send(),684    );685686  res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(687    {Substrate: donor.address},688    () => collection.approveTokens(donor, {Substrate: subReceiver.address}, 1n),689  )));690691  res['burnFromCross'] =692    await helper.arrange.calculcateFeeGas(693      {Ethereum: ethReceiver},694      () => evmContract.methods.burnFromCross(crossSigner, 1).send({from:ethReceiver}),695    );696697  res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(698    {Substrate: subReceiver.address},699    () => collection.burnTokensFrom(subReceiver, {Substrate: donor.address}, 1n),700  )));701702  res['setCollectionSponsorCross'] =703    await helper.arrange.calculcateFeeGas(704      {Ethereum: ethSigner},705      () =>  evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),706    );707708  res['confirmCollectionSponsorship'] =709    await helper.arrange.calculcateFeeGas(710      {Ethereum: ethReceiver},711      () =>  evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),712    );713714  res['removeCollectionSponsor'] =715    await helper.arrange.calculcateFeeGas(716      {Ethereum: ethSigner},717      () =>  evmContract.methods.removeCollectionSponsor().send(),718    );719720  res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(721    {Substrate: donor.address},722    () => collection.setSponsor(donor, subReceiver.address),723  )));724725  res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(726    {Substrate: subReceiver.address},727    () => collection.confirmSponsorship(subReceiver),728  )));729730  res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(731    {Substrate: donor.address},732    () => collection.removeSponsor(donor),733  )));734735  res['setCollectionProperties'] =736    await helper.arrange.calculcateFeeGas(737      {Ethereum: ethSigner},738      () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),739    );740741  res['deleteCollectionProperties'] =742    await helper.arrange.calculcateFeeGas(743      {Ethereum: ethSigner},744      () =>  evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),745    );746  res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(747    {Substrate: donor.address},748    () => collection.setProperties(donor, PROPERTIES.slice(0, 1)749      .map(p => { return {key: p.key, value: p.value.toString()}; })),750  )));751752  res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(753    {Substrate: donor.address},754    () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)755      .map(p => p.key)),756  )));757758  res['setCollectionLimit'] =759    await helper.arrange.calculcateFeeGas(760      {Ethereum: ethSigner},761      () =>  evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),762    );763764  res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(765    {Substrate: donor.address},766    () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),767  )));768769  const {collectionAddress} = await helper.eth.createCollection('nft', ethSigner, 'A', 'B', 'C');770  const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);771772773  res['addCollectionAdminCross'] =774    await helper.arrange.calculcateFeeGas(775      {Ethereum: ethSigner},776      () =>  collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),777    );778779  res['removeCollectionAdminCross'] =780    await helper.arrange.calculcateFeeGas(781      {Ethereum: ethSigner},782      () =>  collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),783    );784785  res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(786    {Substrate: donor.address},787    () => collection.addAdmin(donor, {Ethereum: ethReceiver}),788  )));789790  res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(791    {Substrate: donor.address},792    () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),793  )));794795  res['setCollectionNesting'] =796    await helper.arrange.calculcateFeeGas(797      {Ethereum: ethSigner},798      () =>  evmContract.methods.setCollectionNesting(true).send(),799    );800801  res['setCollectionNesting[]'] =802    await helper.arrange.calculcateFeeGas(803      {Ethereum: ethSigner},804      () =>  evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),805    );806807  res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(808    {Substrate: donor.address},809    () => collection.disableNesting(donor),810  )));811812  res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(813    {Substrate: donor.address},814    () => collection.setPermissions(815      donor,816      {817        nesting: {818          tokenOwner: true,819          restricted: [collection.collectionId],820        },821      },822    ),823  )));824825  res['setCollectionAccess'] =826    await helper.arrange.calculcateFeeGas(827      {Ethereum: ethSigner},828      () =>  evmContract.methods.setCollectionAccess(1).send(),829    );830831  res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(832    {Substrate: donor.address},833    () => collection.setPermissions(donor, {access: 'AllowList'}),834  )));835836  res['addToCollectionAllowListCross'] =837    await helper.arrange.calculcateFeeGas(838      {Ethereum: ethSigner},839      () =>  evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),840    );841842  res['removeFromCollectionAllowListCross'] =843    await helper.arrange.calculcateFeeGas(844      {Ethereum: ethSigner},845      () =>  evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),846    );847848  res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(849    {Substrate: donor.address},850    () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),851  )));852853  res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(854    {Substrate: donor.address},855    () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),856  )));857858  res['setCollectionMintMode'] =859    await helper.arrange.calculcateFeeGas(860      {Ethereum: ethSigner},861      () =>  evmContract.methods.setCollectionMintMode(true).send(),862    );863864  res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(865    {Substrate: donor.address},866    () => collection.setPermissions(donor, {mintMode: false}),867  )));868869  res['changeCollectionOwnerCross'] =870    await helper.arrange.calculcateFeeGas(871      {Ethereum: ethSigner},872      () =>  collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),873    );874875  res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(876    {Substrate: donor.address},877    () => collection.changeOwner(donor, subReceiver.address),878  )));879880  return res;881}
modifiedtests/src/benchmarks/mintFee/types.tsdiffbeforeafterboth
--- a/tests/src/benchmarks/mintFee/types.ts
+++ b/tests/src/benchmarks/mintFee/types.ts
@@ -8,19 +8,38 @@
   }
 
 }
-export interface IFunctionFee {
-  [name: string]: IFeeGas
+export interface IFeeGasCsv extends IFeeGasVm {
+  function: string,
 }
-
-export abstract class FunctionFeeVM {
-  [name: string]: {
+export interface IFeeGasVm{
     ethFee?: number | bigint,
     ethGas?: number | bigint,
     substrate?: number,
     zeppelinFee?: number | bigint,
     zeppelinGas?: number | bigint
-  };
+}
+export interface IFunctionFee {
+  [name: string]: IFeeGas
+}
+
 
+export abstract class FunctionFeeVM {
+  [name: string]: IFeeGasVm
+
+  static toCsv(model: FunctionFeeVM): IFeeGasCsv[]{
+    const res: IFeeGasCsv[] = [];
+    Object.keys(model).forEach(key => {
+      res.push({
+        function: key,
+        ethFee: model[key].ethFee,
+        ethGas: model[key].ethGas,
+        substrate: model[key].substrate,
+        zeppelinFee: model[key].zeppelinFee,
+        zeppelinGas: model[key].zeppelinGas,
+      });
+    });
+    return res;
+  }
   static fromModel(model: IFunctionFee): FunctionFeeVM {
     const res: FunctionFeeVM = {};