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 {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES} from '../utils/common';11import {ContractImports} from '../../eth/util/playgrounds/types';1213export const 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];3536interface IBenchmarkResultForProp {37 propertiesNumber: number;38 substrateFee: number;39 ethFee: number;40 ethBulkFee: number;41 ethMintCrossFee: number;42 evmProxyContractFee: number;43 evmProxyContractBulkFee: number;44}4546const main = async () => {47 const benchmarks = [48 'substrateFee',49 'ethFee',50 'ethBulkFee',51 'ethMintCrossFee',52 'evmProxyContractFee',53 'evmProxyContractBulkFee',54 ];55 const headers = [56 'propertiesNumber',57 ...benchmarks,58 ];596061 const csvWriter = createObjectCsvWriter({62 path: 'properties.csv',63 header: headers,64 });6566 await usingEthPlaygrounds(async (helper, privateKey) => {67 const CONTRACT_SOURCE = (68 await readFile(`${__dirname}/proxyContract.sol`)69 ).toString();7071 const donor = await privateKey('//Alice'); 72 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);7374 const contract = await helper.ethContract.deployByCode(75 ethSigner,76 'ProxyMint',77 CONTRACT_SOURCE,78 CONTRACT_IMPORT,79 );8081 const fees = await benchMintFee(helper, privateKey, contract);82 console.log('Minting without properties');83 console.table(fees);8485 const result: IBenchmarkResultForProp[] = [];86 const csvResult: IBenchmarkResultForProp[] = [];8788 for (let i = 1; i <= 20; i++) {89 const benchResult = await benchMintWithProperties(helper, privateKey, contract, {90 propertiesNumber: i,91 }) as any;9293 csvResult.push(benchResult);9495 const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));96 for(const key of benchmarks) {97 const keyPercent = Math.round((benchResult[key] / minFee) * 100);98 benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;99 }100101 result.push(benchResult);102 }103104 await csvWriter.writeRecords(csvResult);105106 console.log('Minting with properties');107 console.table(result, headers);108 });109};110111main()112 .then(() => process.exit(0))113 .catch((e) => {114 console.log(e);115 process.exit(1);116 });117118119120async function benchMintFee(121 helper: EthUniqueHelper,122 privateKey: (seed: string) => Promise<IKeyringPair>,123 proxyContract: Contract,124): Promise<{125 substrateFee: number;126 ethFee: number;127 evmProxyContractFee: number;128}> {129 const donor = await privateKey('//Alice');130 const substrateReceiver = await privateKey('//Bob');131 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);132133 const nominal = helper.balance.getOneTokenNominal();134135 await helper.eth.transferBalanceFromSubstrate(136 donor,137 proxyContract.options.address,138 100n,139 );140141 const collection = (await createCollectionForBenchmarks(142 'nft',143 helper,144 privateKey,145 ethSigner,146 proxyContract.options.address,147 PERMISSIONS,148 )) as UniqueNFTCollection;149150 const substrateFee = await helper.arrange.calculcateFee(151 {Substrate: donor.address},152 () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),153 );154155 const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);156 const collectionContract = await helper.ethNativeContract.collection(157 collectionEthAddress,158 'nft',159 );160161 const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);162163 const encodedCall = collectionContract.methods164 .mint(receiverEthAddress)165 .encodeABI();166167 const ethFee = await helper.arrange.calculcateFee(168 {Substrate: donor.address},169 async () => {170 await helper.eth.sendEVM(171 donor,172 collectionContract.options.address,173 encodedCall,174 '0',175 );176 },177 );178179 const evmProxyContractFee = await helper.arrange.calculcateFee(180 {Ethereum: ethSigner},181 async () => {182 await proxyContract.methods183 .mintToSubstrate(184 helper.ethAddress.fromCollectionId(collection.collectionId),185 substrateReceiver.addressRaw,186 )187 .send({from: ethSigner});188 },189 );190191 return {192 substrateFee: convertToTokens(substrateFee, nominal),193 ethFee: convertToTokens(ethFee, nominal),194 evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),195 };196}197198async function benchMintWithProperties(199 helper: EthUniqueHelper,200 privateKey: (seed: string) => Promise<IKeyringPair>,201 proxyContract: Contract,202 setup: { propertiesNumber: number },203): Promise<IBenchmarkResultForProp> {204 const donor = await privateKey('//Alice'); 205 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);206207 const susbstrateReceiver = await privateKey('//Bob');208 const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);209210 const nominal = helper.balance.getOneTokenNominal();211212 const substrateFee = await calculateFeeNftMintWithProperties(213 helper,214 privateKey,215 {Substrate: donor.address},216 ethSigner,217 proxyContract.options.address,218 async (collection) => {219 await collection.mintToken(220 donor,221 {Substrate: susbstrateReceiver.address},222 PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {223 return {key: p.key, value: Buffer.from(p.value).toString()};224 }),225 );226 },227 );228229 const ethFee = await calculateFeeNftMintWithProperties(230 helper,231 privateKey,232 {Substrate: donor.address},233 ethSigner,234 proxyContract.options.address,235 async (collection) => {236 const evmContract = await helper.ethNativeContract.collection(237 helper.ethAddress.fromCollectionId(collection.collectionId),238 'nft',239 undefined,240 true,241 );242243 const subTokenId = await evmContract.methods.nextTokenId().call();244245 let encodedCall = evmContract.methods246 .mint(receiverEthAddress)247 .encodeABI();248249 await helper.eth.sendEVM(250 donor,251 evmContract.options.address,252 encodedCall,253 '0',254 );255256 for (const val of PROPERTIES.slice(0, setup.propertiesNumber)) {257 encodedCall = await evmContract.methods258 .setProperty(subTokenId, val.key, Buffer.from(val.value))259 .encodeABI();260261 await helper.eth.sendEVM(262 donor,263 evmContract.options.address,264 encodedCall,265 '0',266 );267 }268 },269 );270271 const ethBulkFee = await calculateFeeNftMintWithProperties(272 helper,273 privateKey,274 {Substrate: donor.address},275 ethSigner,276 proxyContract.options.address,277 async (collection) => {278 const evmContract = await helper.ethNativeContract.collection(279 helper.ethAddress.fromCollectionId(collection.collectionId),280 'nft',281 );282283 const subTokenId = await evmContract.methods.nextTokenId().call();284285 let encodedCall = evmContract.methods286 .mint(receiverEthAddress)287 .encodeABI();288289 await helper.eth.sendEVM(290 donor,291 evmContract.options.address,292 encodedCall,293 '0',294 );295296 encodedCall = await evmContract.methods297 .setProperties(298 subTokenId,299 PROPERTIES.slice(0, setup.propertiesNumber),300 )301 .encodeABI();302303 await helper.eth.sendEVM(304 donor,305 evmContract.options.address,306 encodedCall,307 '0',308 );309 },310 );311312 const ethMintCrossFee = await calculateFeeNftMintWithProperties(313 helper,314 privateKey,315 {Ethereum: ethSigner},316 ethSigner,317 proxyContract.options.address,318 async (collection) => {319 const evmContract = await helper.ethNativeContract.collection(320 helper.ethAddress.fromCollectionId(collection.collectionId),321 'nft',322 );323324 await evmContract.methods.mintCross(325 helper.ethCrossAccount.fromAddress(receiverEthAddress),326 PROPERTIES.slice(0, setup.propertiesNumber),327 )328 .send({from: ethSigner});329 },330 );331332 const proxyContractFee = await calculateFeeNftMintWithProperties(333 helper,334 privateKey,335 {Ethereum: ethSigner},336 ethSigner,337 proxyContract.options.address,338 async (collection) => {339 await proxyContract.methods340 .mintToSubstrateWithProperty(341 helper.ethAddress.fromCollectionId(collection.collectionId),342 susbstrateReceiver.addressRaw,343 PROPERTIES.slice(0, setup.propertiesNumber),344 )345 .send({from: ethSigner});346 },347 );348349 const proxyContractBulkFee = await calculateFeeNftMintWithProperties(350 helper,351 privateKey,352 {Ethereum: ethSigner},353 ethSigner,354 proxyContract.options.address,355 async (collection) => {356 await proxyContract.methods357 .mintToSubstrateBulkProperty(358 helper.ethAddress.fromCollectionId(collection.collectionId),359 susbstrateReceiver.addressRaw,360 PROPERTIES.slice(0, setup.propertiesNumber),361 )362 .send({from: ethSigner, gas: 25_000_000});363 },364 );365366 return {367 propertiesNumber: setup.propertiesNumber,368 substrateFee: convertToTokens(substrateFee, nominal),369 ethFee: convertToTokens(ethFee, nominal),370 ethBulkFee: convertToTokens(ethBulkFee, nominal),371 ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal),372 evmProxyContractFee: convertToTokens(proxyContractFee, nominal),373 evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),374 };375}376377async function calculateFeeNftMintWithProperties(378 helper: EthUniqueHelper,379 privateKey: (seed: string) => Promise<IKeyringPair>,380 payer: ICrossAccountId,381 ethSigner: string,382 proxyContractAddress: string,383 calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,384): Promise<bigint> {385 const collection = (await createCollectionForBenchmarks(386 'nft',387 helper,388 privateKey,389 ethSigner,390 proxyContractAddress,391 PERMISSIONS,392 )) as UniqueNFTCollection;393 return helper.arrange.calculcateFee(payer, async () => {394 await calculatedCall(collection);395 });396}397398399