1import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';2import {readFile} from 'fs/promises';3import {4 ICrossAccountId,5} from '../../util/playgrounds/types';6import {IKeyringPair} from '@polkadot/types/types';7import {UniqueNFTCollection} from '../../util/playgrounds/unique';8import {Contract} from 'web3-eth-contract';9import {createObjectCsvWriter} from 'csv-writer';10import {CONTRACT_IMPORT, convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES} from './common';11121314interface IBenchmarkResultForProp {15 propertiesNumber: number;16 substrateFee: number;17 ethFee: number;18 ethBulkFee: number;19 ethMintCrossFee: number;20 evmProxyContractFee: number;21 evmProxyContractBulkFee: number;22}2324const main = async () => {25 const benchmarks = [26 'substrateFee',27 'ethFee',28 'ethBulkFee',29 'ethMintCrossFee',30 'evmProxyContractFee',31 'evmProxyContractBulkFee',32 ];33 const headers = [34 'propertiesNumber',35 ...benchmarks,36 ];373839 const csvWriter = createObjectCsvWriter({40 path: 'properties.csv',41 header: headers,42 });4344 await usingEthPlaygrounds(async (helper, privateKey) => {45 const CONTRACT_SOURCE = (46 await readFile(`${__dirname}/proxyContract.sol`)47 ).toString();4849 const donor = await privateKey('//Alice'); 50 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);5152 const contract = await helper.ethContract.deployByCode(53 ethSigner,54 'ProxyMint',55 CONTRACT_SOURCE,56 CONTRACT_IMPORT,57 );5859 const fees = await benchMintFee(helper, privateKey, contract);60 console.log('Minting without properties');61 console.table(fees);6263 const result: IBenchmarkResultForProp[] = [];64 const csvResult: IBenchmarkResultForProp[] = [];6566 for (let i = 1; i <= 20; i++) {67 const benchResult = await benchMintWithProperties(helper, privateKey, contract, {68 propertiesNumber: i,69 }) as any;7071 csvResult.push(benchResult);7273 const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));74 for(const key of benchmarks) {75 const keyPercent = Math.round((benchResult[key] / minFee) * 100);76 benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;77 }7879 result.push(benchResult);80 }8182 await csvWriter.writeRecords(csvResult);8384 console.log('Minting with properties');85 console.table(result, headers);86 });87};8889main()90 .then(() => process.exit(0))91 .catch((e) => {92 console.log(e);93 process.exit(1);94 });95969798async function benchMintFee(99 helper: EthUniqueHelper,100 privateKey: (seed: string) => Promise<IKeyringPair>,101 proxyContract: Contract,102): Promise<{103 substrateFee: number;104 ethFee: number;105 evmProxyContractFee: number;106}> {107 const donor = await privateKey('//Alice');108 const substrateReceiver = await privateKey('//Bob');109 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);110111 const nominal = helper.balance.getOneTokenNominal();112113 await helper.eth.transferBalanceFromSubstrate(114 donor,115 proxyContract.options.address,116 100n,117 );118119 const collection = (await createCollectionForBenchmarks(120 'nft',121 helper,122 privateKey,123 ethSigner,124 proxyContract.options.address,125 PERMISSIONS,126 )) as UniqueNFTCollection;127128 const substrateFee = await helper.arrange.calculcateFee(129 {Substrate: donor.address},130 () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),131 );132133 const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);134 const collectionContract = await helper.ethNativeContract.collection(135 collectionEthAddress,136 'nft',137 );138139 const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);140141 const encodedCall = collectionContract.methods142 .mint(receiverEthAddress)143 .encodeABI();144145 const ethFee = await helper.arrange.calculcateFee(146 {Substrate: donor.address},147 async () => {148 await helper.eth.sendEVM(149 donor,150 collectionContract.options.address,151 encodedCall,152 '0',153 );154 },155 );156157 const evmProxyContractFee = await helper.arrange.calculcateFee(158 {Ethereum: ethSigner},159 async () => {160 await proxyContract.methods161 .mintToSubstrate(162 helper.ethAddress.fromCollectionId(collection.collectionId),163 substrateReceiver.addressRaw,164 )165 .send({from: ethSigner});166 },167 );168169 return {170 substrateFee: convertToTokens(substrateFee, nominal),171 ethFee: convertToTokens(ethFee, nominal),172 evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),173 };174}175176async function benchMintWithProperties(177 helper: EthUniqueHelper,178 privateKey: (seed: string) => Promise<IKeyringPair>,179 proxyContract: Contract,180 setup: { propertiesNumber: number },181): Promise<IBenchmarkResultForProp> {182 const donor = await privateKey('//Alice'); 183 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);184185 const susbstrateReceiver = await privateKey('//Bob');186 const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);187188 const nominal = helper.balance.getOneTokenNominal();189190 const substrateFee = await calculateFeeNftMintWithProperties(191 helper,192 privateKey,193 {Substrate: donor.address},194 ethSigner,195 proxyContract.options.address,196 async (collection) => {197 await collection.mintToken(198 donor,199 {Substrate: susbstrateReceiver.address},200 PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {201 return {key: p.key, value: Buffer.from(p.value).toString()};202 }),203 );204 },205 );206207 const ethFee = await calculateFeeNftMintWithProperties(208 helper,209 privateKey,210 {Substrate: donor.address},211 ethSigner,212 proxyContract.options.address,213 async (collection) => {214 const evmContract = await helper.ethNativeContract.collection(215 helper.ethAddress.fromCollectionId(collection.collectionId),216 'nft',217 undefined,218 true,219 );220221 const subTokenId = await evmContract.methods.nextTokenId().call();222223 let encodedCall = evmContract.methods224 .mint(receiverEthAddress)225 .encodeABI();226227 await helper.eth.sendEVM(228 donor,229 evmContract.options.address,230 encodedCall,231 '0',232 );233234 for (const val of PROPERTIES.slice(0, setup.propertiesNumber)) {235 encodedCall = await evmContract.methods236 .setProperty(subTokenId, val.key, Buffer.from(val.value))237 .encodeABI();238239 await helper.eth.sendEVM(240 donor,241 evmContract.options.address,242 encodedCall,243 '0',244 );245 }246 },247 );248249 const ethBulkFee = await calculateFeeNftMintWithProperties(250 helper,251 privateKey,252 {Substrate: donor.address},253 ethSigner,254 proxyContract.options.address,255 async (collection) => {256 const evmContract = await helper.ethNativeContract.collection(257 helper.ethAddress.fromCollectionId(collection.collectionId),258 'nft',259 );260261 const subTokenId = await evmContract.methods.nextTokenId().call();262263 let encodedCall = evmContract.methods264 .mint(receiverEthAddress)265 .encodeABI();266267 await helper.eth.sendEVM(268 donor,269 evmContract.options.address,270 encodedCall,271 '0',272 );273274 encodedCall = await evmContract.methods275 .setProperties(276 subTokenId,277 PROPERTIES.slice(0, setup.propertiesNumber),278 )279 .encodeABI();280281 await helper.eth.sendEVM(282 donor,283 evmContract.options.address,284 encodedCall,285 '0',286 );287 },288 );289290 const ethMintCrossFee = await calculateFeeNftMintWithProperties(291 helper,292 privateKey,293 {Ethereum: ethSigner},294 ethSigner,295 proxyContract.options.address,296 async (collection) => {297 const evmContract = await helper.ethNativeContract.collection(298 helper.ethAddress.fromCollectionId(collection.collectionId),299 'nft',300 );301302 await evmContract.methods.mintCross(303 helper.ethCrossAccount.fromAddress(receiverEthAddress),304 PROPERTIES.slice(0, setup.propertiesNumber),305 )306 .send({from: ethSigner});307 },308 );309310 const proxyContractFee = await calculateFeeNftMintWithProperties(311 helper,312 privateKey,313 {Ethereum: ethSigner},314 ethSigner,315 proxyContract.options.address,316 async (collection) => {317 await proxyContract.methods318 .mintToSubstrateWithProperty(319 helper.ethAddress.fromCollectionId(collection.collectionId),320 susbstrateReceiver.addressRaw,321 PROPERTIES.slice(0, setup.propertiesNumber),322 )323 .send({from: ethSigner});324 },325 );326327 const proxyContractBulkFee = await calculateFeeNftMintWithProperties(328 helper,329 privateKey,330 {Ethereum: ethSigner},331 ethSigner,332 proxyContract.options.address,333 async (collection) => {334 await proxyContract.methods335 .mintToSubstrateBulkProperty(336 helper.ethAddress.fromCollectionId(collection.collectionId),337 susbstrateReceiver.addressRaw,338 PROPERTIES.slice(0, setup.propertiesNumber),339 )340 .send({from: ethSigner, gas: 25_000_000});341 },342 );343344 return {345 propertiesNumber: setup.propertiesNumber,346 substrateFee: convertToTokens(substrateFee, nominal),347 ethFee: convertToTokens(ethFee, nominal),348 ethBulkFee: convertToTokens(ethBulkFee, nominal),349 ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal),350 evmProxyContractFee: convertToTokens(proxyContractFee, nominal),351 evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),352 };353}354355async function calculateFeeNftMintWithProperties(356 helper: EthUniqueHelper,357 privateKey: (seed: string) => Promise<IKeyringPair>,358 payer: ICrossAccountId,359 ethSigner: string,360 proxyContractAddress: string,361 calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,362): Promise<bigint> {363 const collection = (await createCollectionForBenchmarks(364 'nft',365 helper,366 privateKey,367 ethSigner,368 proxyContractAddress,369 PERMISSIONS,370 )) as UniqueNFTCollection;371 return helper.arrange.calculcateFee(payer, async () => {372 await calculatedCall(collection);373 });374}375376377