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

difftreelog

source

tests/src/benchmarks/mintFee/benchmark.ts11.8 KiBsourcehistory
1import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';2import {readFile} from 'fs/promises';3import {ContractImports} from '../../eth/util/playgrounds/types';4import {5  ICrossAccountId,6  ITokenPropertyPermission,7} from '../../util/playgrounds/types';8import {IKeyringPair} from '@polkadot/types/types';9import {UniqueNFTCollection} from '../../util/playgrounds/unique';10import {Contract} from 'web3-eth-contract';11import {createObjectCsvWriter} from 'csv-writer';1213const CONTRACT_IMPORT: ContractImports[] = [14  {15    fsPath: `${__dirname}/../../eth/api/CollectionHelpers.sol`,16    solPath: 'eth/api/CollectionHelpers.sol',17  },18  {19    fsPath: `${__dirname}/../../eth/api/ContractHelpers.sol`,20    solPath: 'eth/api/ContractHelpers.sol',21  },22  {23    fsPath: `${__dirname}/../../eth/api/UniqueRefungibleToken.sol`,24    solPath: 'eth/api/UniqueRefungibleToken.sol',25  },26  {27    fsPath: `${__dirname}/../../eth/api/UniqueRefungible.sol`,28    solPath: 'eth/api/UniqueRefungible.sol',29  },30  {31    fsPath: `${__dirname}/../../eth/api/UniqueNFT.sol`,32    solPath: 'eth/api/UniqueNFT.sol',33  },34];3536const PROPERTIES = Array(40)37  .fill(0)38  .map((_, i) => {39    return {40      key: `key_${i}`,41      value: Uint8Array.from(Buffer.from(`value_${i}`)),42    };43  });4445const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => {46  return {47    key: p.key,48    permission: {49      tokenOwner: true,50      collectionAdmin: true,51      mutable: true,52    },53  };54});5556interface IBenchmarkResultForProp {57	propertiesNumber: number;58	substrateFee: number;59	ethFee: number;60	ethBulkFee: number;61	evmProxyContractFee: number;62	evmProxyContractBulkFee: number;63}6465const main = async () => {66  const benchmarks = [67    'substrateFee',68    'ethFee',69    'ethBulkFee',70    'evmProxyContractFee',71    'evmProxyContractBulkFee',72  ];73  const headers = [74    'propertiesNumber',75    ...benchmarks,76  ];777879  const csvWriter = createObjectCsvWriter({80    path: 'properties.csv',81    header: headers,82  });8384  await usingEthPlaygrounds(async (helper, privateKey) => {85    const CONTRACT_SOURCE = (86      await readFile(`${__dirname}/proxyContract.sol`)87    ).toString();8889    const donor = await privateKey('//Alice'); // Seed from account with balance on this network90    const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);9192    const contract = await helper.ethContract.deployByCode(93      ethSigner,94      'ProxyMint',95      CONTRACT_SOURCE,96      CONTRACT_IMPORT,97    );9899    const fees = await benchMintFee(helper, privateKey, contract);100    console.log('Minting without properties');101    console.table(fees);102103    const result: IBenchmarkResultForProp[] = [];104    const csvResult: IBenchmarkResultForProp[] = [];105106    for (let i = 1; i <= 20; i++) {107      const benchResult = await benchMintWithProperties(helper, privateKey, contract, {108        propertiesNumber: i,109      }) as any;110111      csvResult.push(benchResult);112113      const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));114      for(const key of benchmarks) {115        const keyPercent = Math.round((benchResult[key] / minFee) * 100);116        benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;117      }118119      result.push(benchResult);120    }121122    await csvWriter.writeRecords(csvResult);123124    console.log('Minting with properties');125    console.table(result, headers);126  });127};128129main()130  .then(() => process.exit(0))131  .catch((e) => {132    console.log(e);133    process.exit(1);134  });135136async function createCollectionForBenchmarks(137  helper: EthUniqueHelper,138  privateKey: (seed: string) => Promise<IKeyringPair>,139  ethSigner: string,140  proxyContract: string,141  permissions: ITokenPropertyPermission[],142) {143  const donor = await privateKey('//Alice');144145  const collection = await helper.nft.mintCollection(donor, {146    name: 'test mintToSubstrate',147    description: 'EVMHelpers',148    tokenPrefix: 'ap',149    tokenPropertyPermissions: [150      {151        key: 'url',152        permission: {153          tokenOwner: true,154          collectionAdmin: true,155          mutable: true,156        },157      },158    ],159    limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0},160    permissions: {mintMode: true},161  });162163  await collection.addToAllowList(donor, {164    Ethereum: helper.address.substrateToEth(donor.address),165  });166  await collection.addToAllowList(donor, {Substrate: donor.address});167  await collection.addAdmin(donor, {Ethereum: ethSigner});168  await collection.addAdmin(donor, {169    Ethereum: helper.address.substrateToEth(donor.address),170  });171  await collection.addToAllowList(donor, {Ethereum: proxyContract});172  await collection.addAdmin(donor, {Ethereum: proxyContract});173  await collection.setTokenPropertyPermissions(donor, permissions);174175  return collection;176}177178async function benchMintFee(179  helper: EthUniqueHelper,180  privateKey: (seed: string) => Promise<IKeyringPair>,181  proxyContract: Contract,182): Promise<{183	substrateFee: number;184	ethFee: number;185	evmProxyContractFee: number;186}> {187  const donor = await privateKey('//Alice');188  const substrateReceiver = await privateKey('//Bob');189  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);190191  const nominal = helper.balance.getOneTokenNominal();192193  await helper.eth.transferBalanceFromSubstrate(194    donor,195    proxyContract.options.address,196    100n,197  );198199  const collection = await createCollectionForBenchmarks(200    helper,201    privateKey,202    ethSigner,203    proxyContract.options.address,204    PERMISSIONS,205  );206207  const substrateFee = await helper.arrange.calculcateFee(208    {Substrate: donor.address},209    () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),210  );211212  const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);213  const collectionContract = await helper.ethNativeContract.collection(214    collectionEthAddress,215    'nft',216  );217218  const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);219220  const encodedCall = collectionContract.methods221    .mint(receiverEthAddress)222    .encodeABI();223224  const ethFee = await helper.arrange.calculcateFee(225    {Substrate: donor.address},226    async () => {227      await helper.eth.sendEVM(228        donor,229        collectionContract.options.address,230        encodedCall,231        '0',232      );233    },234  );235236  const evmProxyContractFee = await helper.arrange.calculcateFee(237    {Ethereum: ethSigner},238    async () => {239      await proxyContract.methods240        .mintToSubstrate(241          helper.ethAddress.fromCollectionId(collection.collectionId),242          substrateReceiver.addressRaw,243        )244        .send({from: ethSigner});245    },246  );247248  return {249    substrateFee: convertToTokens(substrateFee, nominal),250    ethFee: convertToTokens(ethFee, nominal),251    evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),252  };253}254255async function benchMintWithProperties(256  helper: EthUniqueHelper,257  privateKey: (seed: string) => Promise<IKeyringPair>,258  proxyContract: Contract,259  setup: { propertiesNumber: number },260): Promise<IBenchmarkResultForProp> {261  const donor = await privateKey('//Alice'); // Seed from account with balance on this network262  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);263264  const susbstrateReceiver = await privateKey('//Bob');265  const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);266267  const nominal = helper.balance.getOneTokenNominal();268269  const substrateFee = await calculateFeeNftMintWithProperties(270    helper,271    privateKey,272    {Substrate: donor.address},273    ethSigner,274    proxyContract.options.address,275    async (collection) => {276      await collection.mintToken(277        donor,278        {Substrate: susbstrateReceiver.address},279        PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {280          return {key: p.key, value: Buffer.from(p.value).toString()};281        }),282      );283    },284  );285286  const ethFee = await calculateFeeNftMintWithProperties(287    helper,288    privateKey,289    {Substrate: donor.address},290    ethSigner,291    proxyContract.options.address,292    async (collection) => {293      const evmContract = await helper.ethNativeContract.collection(294        helper.ethAddress.fromCollectionId(collection.collectionId),295        'nft',296        undefined,297        true,298      );299300      const subTokenId = await evmContract.methods.nextTokenId().call();301302      let encodedCall = evmContract.methods303        .mint(receiverEthAddress)304        .encodeABI();305306      await helper.eth.sendEVM(307        donor,308        evmContract.options.address,309        encodedCall,310        '0',311      );312313      for (const val of PROPERTIES.slice(0, setup.propertiesNumber)) {314        encodedCall = await evmContract.methods315          .setProperty(subTokenId, val.key, Buffer.from(val.value))316          .encodeABI();317318        await helper.eth.sendEVM(319          donor,320          evmContract.options.address,321          encodedCall,322          '0',323        );324      }325    },326  );327328  const ethBulkFee = await calculateFeeNftMintWithProperties(329    helper,330    privateKey,331    {Substrate: donor.address},332    ethSigner,333    proxyContract.options.address,334    async (collection) => {335      const evmContract = await helper.ethNativeContract.collection(336        helper.ethAddress.fromCollectionId(collection.collectionId),337        'nft',338      );339340      const subTokenId = await evmContract.methods.nextTokenId().call();341342      let encodedCall = evmContract.methods343        .mint(receiverEthAddress)344        .encodeABI();345346      await helper.eth.sendEVM(347        donor,348        evmContract.options.address,349        encodedCall,350        '0',351      );352353      encodedCall = await evmContract.methods354        .setProperties(355          subTokenId,356          PROPERTIES.slice(0, setup.propertiesNumber),357        )358        .encodeABI();359360      await helper.eth.sendEVM(361        donor,362        evmContract.options.address,363        encodedCall,364        '0',365      );366    },367  );368369  const proxyContractFee = await calculateFeeNftMintWithProperties(370    helper,371    privateKey,372    {Ethereum: ethSigner},373    ethSigner,374    proxyContract.options.address,375    async (collection) => {376      await proxyContract.methods377        .mintToSubstrateWithProperty(378          helper.ethAddress.fromCollectionId(collection.collectionId),379          susbstrateReceiver.addressRaw,380          PROPERTIES.slice(0, setup.propertiesNumber),381        )382        .send({from: ethSigner});383    },384  );385386  const proxyContractBulkFee = await calculateFeeNftMintWithProperties(387    helper,388    privateKey,389    {Ethereum: ethSigner},390    ethSigner,391    proxyContract.options.address,392    async (collection) => {393      await proxyContract.methods394        .mintToSubstrateBulkProperty(395          helper.ethAddress.fromCollectionId(collection.collectionId),396          susbstrateReceiver.addressRaw,397          PROPERTIES.slice(0, setup.propertiesNumber),398        )399        .send({from: ethSigner, gas: 25_000_000});400    },401  );402403  return {404    propertiesNumber: setup.propertiesNumber,405    substrateFee: convertToTokens(substrateFee, nominal),406    ethFee: convertToTokens(ethFee, nominal),407    ethBulkFee: convertToTokens(ethBulkFee, nominal),408    evmProxyContractFee: convertToTokens(proxyContractFee, nominal),409    evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),410  };411}412413async function calculateFeeNftMintWithProperties(414  helper: EthUniqueHelper,415  privateKey: (seed: string) => Promise<IKeyringPair>,416  payer: ICrossAccountId,417  ethSigner: string,418  proxyContractAddress: string,419  calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,420): Promise<bigint> {421  const collection = await createCollectionForBenchmarks(422    helper,423    privateKey,424    ethSigner,425    proxyContractAddress,426    PERMISSIONS,427  );428  return helper.arrange.calculcateFee(payer, async () => {429    await calculatedCall(collection);430  });431}432function convertToTokens(value: bigint, nominal: bigint): number {433  return Number((value * 1000n) / nominal) / 1000;434}