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

difftreelog

source

tests/src/benchmarks/mintFee/benchmark.ts11.9 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      );297298      const subTokenId = await evmContract.methods.nextTokenId().call();299300      let encodedCall = evmContract.methods301        .mint(receiverEthAddress)302        .encodeABI();303304      await helper.eth.sendEVM(305        donor,306        evmContract.options.address,307        encodedCall,308        '0',309      );310311      for (const val of PROPERTIES.slice(0, setup.propertiesNumber)) {312        encodedCall = await evmContract.methods313          .setProperty(subTokenId, val.key, Buffer.from(val.value))314          .encodeABI();315316        await helper.eth.sendEVM(317          donor,318          evmContract.options.address,319          encodedCall,320          '0',321        );322      }323    },324  );325326  const ethBulkFee = await calculateFeeNftMintWithProperties(327    helper,328    privateKey,329    {Substrate: donor.address},330    ethSigner,331    proxyContract.options.address,332    async (collection) => {333      const evmContract = await helper.ethNativeContract.collection(334        helper.ethAddress.fromCollectionId(collection.collectionId),335        'nft',336      );337338      const subTokenId = await evmContract.methods.nextTokenId().call();339340      let encodedCall = evmContract.methods341        .mint(receiverEthAddress)342        .encodeABI();343344      await helper.eth.sendEVM(345        donor,346        evmContract.options.address,347        encodedCall,348        '0',349      );350351      encodedCall = await evmContract.methods352        .setProperties(353          subTokenId,354          PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {355            return {field_0: p.key, field_1: p.value};356          }),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).map((p) => {398            return {field_0: p.key, field_1: p.value};399          }),400        )401        .send({from: ethSigner, gas: 25_000_000});402    },403  );404405  return {406    propertiesNumber: setup.propertiesNumber,407    substrateFee: convertToTokens(substrateFee, nominal),408    ethFee: convertToTokens(ethFee, nominal),409    ethBulkFee: convertToTokens(ethBulkFee, nominal),410    evmProxyContractFee: convertToTokens(proxyContractFee, nominal),411    evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),412  };413}414415async function calculateFeeNftMintWithProperties(416  helper: EthUniqueHelper,417  privateKey: (seed: string) => Promise<IKeyringPair>,418  payer: ICrossAccountId,419  ethSigner: string,420  proxyContractAddress: string,421  calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,422): Promise<bigint> {423  const collection = await createCollectionForBenchmarks(424    helper,425    privateKey,426    ethSigner,427    proxyContractAddress,428    PERMISSIONS,429  );430  return helper.arrange.calculcateFee(payer, async () => {431    await calculatedCall(collection);432  });433}434function convertToTokens(value: bigint, nominal: bigint): number {435  return Number((value * 1000n) / nominal) / 1000;436}