difftreelog
refactor changed arch of bechmarks
in: master
added export to `csv` ops fees
6 files changed
tests/.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
tests/src/benchmarks/mintFee/benchmark.tsdiffbeforeafterboth1import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';2import {readFile} from 'fs/promises';3import {CollectionLimitField, ContractImports, TokenPermissionField} from '../../eth/util/playgrounds/types';4import {5 ICrossAccountId,6 ITokenPropertyPermission,7 TCollectionMode,8} from '../../util/playgrounds/types';9import {IKeyringPair} from '@polkadot/types/types';10import {UniqueFTCollection, UniqueNFTCollection, UniqueRFTCollection} from '../../util/playgrounds/unique';11import {Contract} from 'web3-eth-contract';12import {createObjectCsvWriter} from 'csv-writer';13import {FunctionFeeVM, IFunctionFee} from './types';1415const CONTRACT_IMPORT: ContractImports[] = [16 {17 fsPath: `${__dirname}/../../eth/api/CollectionHelpers.sol`,18 solPath: 'eth/api/CollectionHelpers.sol',19 },20 {21 fsPath: `${__dirname}/../../eth/api/ContractHelpers.sol`,22 solPath: 'eth/api/ContractHelpers.sol',23 },24 {25 fsPath: `${__dirname}/../../eth/api/UniqueRefungibleToken.sol`,26 solPath: 'eth/api/UniqueRefungibleToken.sol',27 },28 {29 fsPath: `${__dirname}/../../eth/api/UniqueRefungible.sol`,30 solPath: 'eth/api/UniqueRefungible.sol',31 },32 {33 fsPath: `${__dirname}/../../eth/api/UniqueNFT.sol`,34 solPath: 'eth/api/UniqueNFT.sol',35 },36];3738const PROPERTIES = Array(40)39 .fill(0)40 .map((_, i) => {41 return {42 key: `key_${i}`,43 value: Uint8Array.from(Buffer.from(`value_${i}`)),44 };45 });4647const SUBS_PROPERTIES = Array(40)48 .fill(0)49 .map((_, i) => {50 return {51 key: `key_${i}`,52 value: `value_${i}`,53 };54 });5556const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => {57 return {58 key: p.key,59 permission: {60 tokenOwner: true,61 collectionAdmin: true,62 mutable: true,63 },64 };65});6667interface IBenchmarkResultForProp {68 propertiesNumber: number;69 substrateFee: number;70 ethFee: number;71 ethBulkFee: number;72 ethMintCrossFee: number;73 evmProxyContractFee: number;74 evmProxyContractBulkFee: number;75}7677const main = async () => {78 const benchmarks = [79 'substrateFee',80 'ethFee',81 'ethBulkFee',82 'ethMintCrossFee',83 'evmProxyContractFee',84 'evmProxyContractBulkFee',85 ];86 const headers = [87 'propertiesNumber',88 ...benchmarks,89 ];909192 const csvWriter = createObjectCsvWriter({93 path: 'properties.csv',94 header: headers,95 });9697 await usingEthPlaygrounds(async (helper, privateKey) => {98 const NOMINAL = helper.balance.getOneTokenNominal();99 const CONTRACT_SOURCE = (100 await readFile(`${__dirname}/proxyContract.sol`)101 ).toString();102103 console.log('\n ERC20 ops fees');104 console.table(FunctionFeeVM.fromModel(await erc20CalculateFeeGas(helper, privateKey)));105106 console.log('\n ERC721 ops fees');107 const erc721 = await erc721CalculateFeeGas(helper, privateKey);108 console.table(FunctionFeeVM.fromModel(erc721));109110 const donor = await privateKey('//Alice'); // Seed from account with balance on this network111 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);112113 const contract = await helper.ethContract.deployByCode(114 ethSigner,115 'ProxyMint',116 CONTRACT_SOURCE,117 CONTRACT_IMPORT,118 );119120 const fees = await benchMintFee(helper, privateKey, contract);121 console.log('Minting without properties');122 console.table(fees);123124 const result: IBenchmarkResultForProp[] = [];125 const csvResult: IBenchmarkResultForProp[] = [];126127 for (let i = 1; i <= 20; i++) {128 const benchResult = await benchMintWithProperties(helper, privateKey, contract, {129 propertiesNumber: i,130 }) as any;131132 csvResult.push(benchResult);133134 const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));135 for(const key of benchmarks) {136 const keyPercent = Math.round((benchResult[key] / minFee) * 100);137 benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;138 }139140 result.push(benchResult);141 }142143 await csvWriter.writeRecords(csvResult);144145 console.log('Minting with properties');146 console.table(result, headers);147 });148};149150main()151 .then(() => process.exit(0))152 .catch((e) => {153 console.log(e);154 process.exit(1);155 });156157async function createCollectionForBenchmarks(158 mode : TCollectionMode,159 helper: EthUniqueHelper,160 privateKey: (seed: string) => Promise<IKeyringPair>,161 ethSigner: string,162 proxyContract: string | null,163 permissions: ITokenPropertyPermission[],164) {165 const donor = await privateKey('//Alice');166167 const collection = await helper[mode].mintCollection(donor, {168 name: 'test mintToSubstrate',169 description: 'EVMHelpers',170 tokenPrefix: mode,171 ...(mode != 'ft' ? {172 tokenPropertyPermissions: [173 {174 key: 'url',175 permission: {176 tokenOwner: true,177 collectionAdmin: true,178 mutable: true,179 },180 },181 {182 key: 'URI',183 permission: {184 tokenOwner: true,185 collectionAdmin: true,186 mutable: true,187 },188 },189 ],190 } : {}),191 limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0},192 permissions: {mintMode: true},193 });194195 await collection.addToAllowList(donor, {196 Ethereum: helper.address.substrateToEth(donor.address),197 });198 await collection.addToAllowList(donor, {Substrate: donor.address});199 await collection.addAdmin(donor, {Ethereum: ethSigner});200 await collection.addAdmin(donor, {201 Ethereum: helper.address.substrateToEth(donor.address),202 });203204 if (proxyContract) {205 await collection.addToAllowList(donor, {Ethereum: proxyContract});206 await collection.addAdmin(donor, {Ethereum: proxyContract});207 }208 if (collection instanceof UniqueNFTCollection || collection instanceof UniqueRFTCollection)209 await collection.setTokenPropertyPermissions(donor, permissions);210211 return collection;212}213214async function benchMintFee(215 helper: EthUniqueHelper,216 privateKey: (seed: string) => Promise<IKeyringPair>,217 proxyContract: Contract,218): Promise<{219 substrateFee: number;220 ethFee: number;221 evmProxyContractFee: number;222}> {223 const donor = await privateKey('//Alice');224 const substrateReceiver = await privateKey('//Bob');225 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);226227 const nominal = helper.balance.getOneTokenNominal();228229 await helper.eth.transferBalanceFromSubstrate(230 donor,231 proxyContract.options.address,232 100n,233 );234235 const collection = (await createCollectionForBenchmarks(236 'nft',237 helper,238 privateKey,239 ethSigner,240 proxyContract.options.address,241 PERMISSIONS,242 )) as UniqueNFTCollection;243244 const substrateFee = await helper.arrange.calculcateFee(245 {Substrate: donor.address},246 () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),247 );248249 const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);250 const collectionContract = await helper.ethNativeContract.collection(251 collectionEthAddress,252 'nft',253 );254255 const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);256257 const encodedCall = collectionContract.methods258 .mint(receiverEthAddress)259 .encodeABI();260261 const ethFee = await helper.arrange.calculcateFee(262 {Substrate: donor.address},263 async () => {264 await helper.eth.sendEVM(265 donor,266 collectionContract.options.address,267 encodedCall,268 '0',269 );270 },271 );272273 const evmProxyContractFee = await helper.arrange.calculcateFee(274 {Ethereum: ethSigner},275 async () => {276 await proxyContract.methods277 .mintToSubstrate(278 helper.ethAddress.fromCollectionId(collection.collectionId),279 substrateReceiver.addressRaw,280 )281 .send({from: ethSigner});282 },283 );284285 return {286 substrateFee: convertToTokens(substrateFee, nominal),287 ethFee: convertToTokens(ethFee, nominal),288 evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),289 };290}291292async function benchMintWithProperties(293 helper: EthUniqueHelper,294 privateKey: (seed: string) => Promise<IKeyringPair>,295 proxyContract: Contract,296 setup: { propertiesNumber: number },297): Promise<IBenchmarkResultForProp> {298 const donor = await privateKey('//Alice'); // Seed from account with balance on this network299 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);300301 const susbstrateReceiver = await privateKey('//Bob');302 const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);303304 const nominal = helper.balance.getOneTokenNominal();305306 const substrateFee = await calculateFeeNftMintWithProperties(307 helper,308 privateKey,309 {Substrate: donor.address},310 ethSigner,311 proxyContract.options.address,312 async (collection) => {313 await collection.mintToken(314 donor,315 {Substrate: susbstrateReceiver.address},316 PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {317 return {key: p.key, value: Buffer.from(p.value).toString()};318 }),319 );320 },321 );322323 const ethFee = await calculateFeeNftMintWithProperties(324 helper,325 privateKey,326 {Substrate: donor.address},327 ethSigner,328 proxyContract.options.address,329 async (collection) => {330 const evmContract = await helper.ethNativeContract.collection(331 helper.ethAddress.fromCollectionId(collection.collectionId),332 'nft',333 undefined,334 true,335 );336337 const subTokenId = await evmContract.methods.nextTokenId().call();338339 let encodedCall = evmContract.methods340 .mint(receiverEthAddress)341 .encodeABI();342343 await helper.eth.sendEVM(344 donor,345 evmContract.options.address,346 encodedCall,347 '0',348 );349350 for (const val of PROPERTIES.slice(0, setup.propertiesNumber)) {351 encodedCall = await evmContract.methods352 .setProperty(subTokenId, val.key, Buffer.from(val.value))353 .encodeABI();354355 await helper.eth.sendEVM(356 donor,357 evmContract.options.address,358 encodedCall,359 '0',360 );361 }362 },363 );364365 const ethBulkFee = await calculateFeeNftMintWithProperties(366 helper,367 privateKey,368 {Substrate: donor.address},369 ethSigner,370 proxyContract.options.address,371 async (collection) => {372 const evmContract = await helper.ethNativeContract.collection(373 helper.ethAddress.fromCollectionId(collection.collectionId),374 'nft',375 );376377 const subTokenId = await evmContract.methods.nextTokenId().call();378379 let encodedCall = evmContract.methods380 .mint(receiverEthAddress)381 .encodeABI();382383 await helper.eth.sendEVM(384 donor,385 evmContract.options.address,386 encodedCall,387 '0',388 );389390 encodedCall = await evmContract.methods391 .setProperties(392 subTokenId,393 PROPERTIES.slice(0, setup.propertiesNumber),394 )395 .encodeABI();396397 await helper.eth.sendEVM(398 donor,399 evmContract.options.address,400 encodedCall,401 '0',402 );403 },404 );405406 const ethMintCrossFee = await calculateFeeNftMintWithProperties(407 helper,408 privateKey,409 {Ethereum: ethSigner},410 ethSigner,411 proxyContract.options.address,412 async (collection) => {413 const evmContract = await helper.ethNativeContract.collection(414 helper.ethAddress.fromCollectionId(collection.collectionId),415 'nft',416 );417418 await evmContract.methods.mintCross(419 helper.ethCrossAccount.fromAddress(receiverEthAddress),420 PROPERTIES.slice(0, setup.propertiesNumber),421 )422 .send({from: ethSigner});423 },424 );425426 const proxyContractFee = await calculateFeeNftMintWithProperties(427 helper,428 privateKey,429 {Ethereum: ethSigner},430 ethSigner,431 proxyContract.options.address,432 async (collection) => {433 await proxyContract.methods434 .mintToSubstrateWithProperty(435 helper.ethAddress.fromCollectionId(collection.collectionId),436 susbstrateReceiver.addressRaw,437 PROPERTIES.slice(0, setup.propertiesNumber),438 )439 .send({from: ethSigner});440 },441 );442443 const proxyContractBulkFee = await calculateFeeNftMintWithProperties(444 helper,445 privateKey,446 {Ethereum: ethSigner},447 ethSigner,448 proxyContract.options.address,449 async (collection) => {450 await proxyContract.methods451 .mintToSubstrateBulkProperty(452 helper.ethAddress.fromCollectionId(collection.collectionId),453 susbstrateReceiver.addressRaw,454 PROPERTIES.slice(0, setup.propertiesNumber),455 )456 .send({from: ethSigner, gas: 25_000_000});457 },458 );459460 return {461 propertiesNumber: setup.propertiesNumber,462 substrateFee: convertToTokens(substrateFee, nominal),463 ethFee: convertToTokens(ethFee, nominal),464 ethBulkFee: convertToTokens(ethBulkFee, nominal),465 ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal),466 evmProxyContractFee: convertToTokens(proxyContractFee, nominal),467 evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),468 };469}470471async function calculateFeeNftMintWithProperties(472 helper: EthUniqueHelper,473 privateKey: (seed: string) => Promise<IKeyringPair>,474 payer: ICrossAccountId,475 ethSigner: string,476 proxyContractAddress: string,477 calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,478): Promise<bigint> {479 const collection = (await createCollectionForBenchmarks(480 'nft',481 helper,482 privateKey,483 ethSigner,484 proxyContractAddress,485 PERMISSIONS,486 )) as UniqueNFTCollection;487 return helper.arrange.calculcateFee(payer, async () => {488 await calculatedCall(collection);489 });490}491492function convertToTokens(value: bigint, nominal = 1000_000_000_000_000_000n): number {493 return Number((value * 1000n) / nominal) / 1000;494}495496async function erc721CalculateFeeGas(497 helper: EthUniqueHelper,498 privateKey: (seed: string) => Promise<IKeyringPair>,499500): Promise<IFunctionFee> {501 const res: IFunctionFee = {};502 const donor = await privateKey('//Alice');503 const [subReceiver] = await helper.arrange.createAccounts([10n], donor);504 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);505 const ethReceiver = await helper.eth.createAccountWithBalance(donor, 10n);506 const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);507 const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);508 const collection = (await createCollectionForBenchmarks(509 'nft',510 helper,511 privateKey,512 ethSigner,513 null,514 PERMISSIONS,515 )) as UniqueNFTCollection;516517 const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);518 let zeppelelinContract: Contract | null = null;519 const ZEPPELIN_OBJECT = '0x' + (await readFile(`${__dirname}/openZeppelin/ERC721/bin/ZeppelinContract.bin`)).toString();520 const ZEPPELIN_ABI = JSON.parse((await readFile(`${__dirname}/openZeppelin/ERC721/bin/ZeppelinContract.abi`)).toString());521522 const evmContract = await helper.ethNativeContract.collection(523 helper.ethAddress.fromCollectionId(collection.collectionId),524 'nft',525 ethSigner,526 true,527 );528529 res['createCollection'] = await helper.arrange.calculcateFeeGas(530 {Ethereum: ethSigner},531 () => helperContract.methods.createNFTCollection('test','test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}),532 );533534 res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee(535 {Substrate: donor.address},536 () => helper.nft.mintCollection(537 donor,538 {name: 'test', description: 'test', tokenPrefix: 'test'},539 ),540 )));541542 res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas(543 {Ethereum: ethSigner},544 async () => {545 zeppelelinContract = await helper.ethContract.deployByAbi(546 ethSigner,547 ZEPPELIN_ABI,548 ZEPPELIN_OBJECT,549 );550 },551 );552553 res['mint'] =554 await helper.arrange.calculcateFeeGas(555 {Ethereum: ethSigner},556 () => evmContract.methods.mint(ethSigner).send(),557 );558559 res['mint'].zeppelin =560 await helper.arrange.calculcateFeeGas(561 {Ethereum: ethSigner},562 () => zeppelelinContract!.methods.safeMint(ethSigner, 'test').send({from: ethSigner}),563 );564565 res['mintCross'] =566 await helper.arrange.calculcateFeeGas(567 {Ethereum: ethSigner},568 () => evmContract.methods.mintCross(crossSigner, []).send(),569 );570571 res['mint'].substrate = convertToTokens((await helper.arrange.calculcateFee(572 {Substrate: donor.address},573 () => collection.mintToken(574 donor,575 {Substrate: donor.address},576 ),577 )));578579 res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(580 {Substrate: donor.address},581 () => collection.mintMultipleTokens(donor, [{582 owner: {Substrate: donor.address},583 }]),584 )));585586 res['mintWithTokenURI'] =587 await helper.arrange.calculcateFeeGas(588 {Ethereum: ethSigner},589 () => evmContract.methods.mintWithTokenURI(ethSigner, 'Test URI').send(),590 );591592 res['mintWithTokenURI'].substrate = convertToTokens((await helper.arrange.calculcateFee(593 {Substrate: donor.address},594 () => collection.mintToken(595 donor,596 {Ethereum: ethSigner},597 [{key: 'URI', value: 'Test URI'}],598 ),599 )));600601 res['setProperties'] =602 await helper.arrange.calculcateFeeGas(603 {Ethereum: ethSigner},604 () => evmContract.methods.setProperties(1, PROPERTIES.slice(0,1)).send(),605 );606607 res['deleteProperties'] =608 await helper.arrange.calculcateFeeGas(609 {Ethereum: ethSigner},610 () => evmContract.methods.deleteProperties(1, [PROPERTIES[0].key]).send(),611 );612613 res['setProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(614 {Substrate: donor.address},615 () => collection.setTokenProperties(616 donor,617 1,618 SUBS_PROPERTIES.slice(0, 1),619 ),620 )));621622 res['deleteProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(623 {Substrate: donor.address},624 () => collection.deleteTokenProperties(625 donor,626 1,627 SUBS_PROPERTIES.slice(0, 1)628 .map(p => p.key),629 ),630 )));631632 res['transfer'] =633 await helper.arrange.calculcateFeeGas(634 {Ethereum: ethSigner},635 () => evmContract.methods.transfer(ethReceiver, 1).send(),636 );637638 res['safeTransferFrom*'] = {639 zeppelin:640 await helper.arrange.calculcateFeeGas(641 {Ethereum: ethSigner},642 () => zeppelelinContract!.methods.safeTransferFrom(ethSigner, ethReceiver, 0).send({from: ethSigner}),643 ),644 };645646 res['transferCross'] =647 await helper.arrange.calculcateFeeGas(648 {Ethereum: ethReceiver},649 () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),650 );651652 res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(653 {Substrate: donor.address},654 () => collection.transferToken(655 donor,656 3,657 {Substrate: subReceiver.address},658 ),659 )));660 await collection.approveToken(subReceiver, 3, {Substrate: donor.address});661662 res['transferFrom*'] =663 await helper.arrange.calculcateFeeGas(664 {Ethereum: ethSigner},665 () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),666 );667668 res['transferFrom*'].zeppelin =669 await helper.arrange.calculcateFeeGas(670 {Ethereum: ethReceiver},671 () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}),672 );673674675 res['transferFromCross'] =676 await helper.arrange.calculcateFeeGas(677 {Ethereum: ethReceiver},678 () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),679 );680681 res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(682 {Substrate: donor.address},683 () => collection.transferTokenFrom(donor, 3, {Substrate: subReceiver.address}, {Substrate: donor.address}),684 )));685686 res['burn'] =687 await helper.arrange.calculcateFeeGas(688 {Ethereum: ethSigner},689 () => evmContract.methods.burn(1).send(),690 );691692 res['burn'].substrate = convertToTokens((await helper.arrange.calculcateFee(693 {Substrate: donor.address},694 () => collection.burnToken(donor, 3),695 )));696697 res['approve*'] =698 await helper.arrange.calculcateFeeGas(699 {Ethereum: ethSigner},700 () => evmContract.methods.approve(ethReceiver, 2).send(),701 );702703 res['approve*'].zeppelin =704 await helper.arrange.calculcateFeeGas(705 {Ethereum: ethSigner},706 () => zeppelelinContract!.methods.approve(ethReceiver, 0).send({from: ethSigner}),707 );708709 res['approveCross'] =710 await helper.arrange.calculcateFeeGas(711 {Ethereum: ethSigner},712 () => evmContract.methods.approveCross(crossReceiver, 2).send(),713 );714715 res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(716 {Substrate: donor.address},717 () => collection.approveToken(donor, 4, {Substrate: subReceiver.address}),718 )));719720 res['setApprovalForAll*'] =721 await helper.arrange.calculcateFeeGas(722 {Ethereum: ethSigner},723 () => evmContract.methods.setApprovalForAll(ethReceiver, true).send(),724 );725726 res['setApprovalForAll*'].zeppelin =727 await helper.arrange.calculcateFeeGas(728 {Ethereum: ethSigner},729 () => zeppelelinContract!.methods.setApprovalForAll(ethReceiver, true).send({from: ethSigner}),730 );731732 res['setApprovalForAll*'].substrate = convertToTokens((await helper.arrange.calculcateFee(733 {Substrate: donor.address},734 () => helper.nft.setAllowanceForAll(donor, collection.collectionId, {Substrate: subReceiver.address}, true),735 )));736737 res['burnFromCross'] =738 await helper.arrange.calculcateFeeGas(739 {Ethereum: ethReceiver},740 () => evmContract.methods.burnFromCross(crossSigner, 2).send({from:ethReceiver}),741 );742743 res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(744 {Substrate: subReceiver.address},745 () => collection.burnTokenFrom(subReceiver, 4, {Substrate: donor.address}),746 )));747748 res['setTokenPropertyPermissions'] =749 await helper.arrange.calculcateFeeGas(750 {Ethereum: ethSigner},751 () => evmContract.methods.setTokenPropertyPermissions([752 ['url', [753 [TokenPermissionField.Mutable, true],754 [TokenPermissionField.TokenOwner, true],755 [TokenPermissionField.CollectionAdmin, true]],756 ],757 ]).send(),758 );759760 res['setTokenPropertyPermissions'].substrate = convertToTokens((await helper.arrange.calculcateFee(761 {Substrate: donor.address},762 () => collection.setTokenPropertyPermissions(donor, [{key: 'url', permission: {763 tokenOwner: true,764 collectionAdmin: true,765 mutable: true,766 }}]),767 )));768769 res['setCollectionSponsorCross'] =770 await helper.arrange.calculcateFeeGas(771 {Ethereum: ethSigner},772 () => evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),773 );774775 res['confirmCollectionSponsorship'] =776 await helper.arrange.calculcateFeeGas(777 {Ethereum: ethReceiver},778 () => evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),779 );780781 res['removeCollectionSponsor'] =782 await helper.arrange.calculcateFeeGas(783 {Ethereum: ethSigner},784 () => evmContract.methods.removeCollectionSponsor().send(),785 );786787 res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(788 {Substrate: donor.address},789 () => collection.setSponsor(donor, subReceiver.address),790 )));791792 res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(793 {Substrate: subReceiver.address},794 () => collection.confirmSponsorship(subReceiver),795 )));796797 res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(798 {Substrate: donor.address},799 () => collection.removeSponsor(donor),800 )));801802 res['setCollectionProperties'] =803 await helper.arrange.calculcateFeeGas(804 {Ethereum: ethSigner},805 () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),806 );807808 res['deleteCollectionProperties'] =809 await helper.arrange.calculcateFeeGas(810 {Ethereum: ethSigner},811 () => evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),812 );813 res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(814 {Substrate: donor.address},815 () => collection.setProperties(donor, PROPERTIES.slice(0, 1)816 .map(p => { return {key: p.key, value: p.value.toString()}; })),817 )));818819 res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(820 {Substrate: donor.address},821 () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)822 .map(p => p.key)),823 )));824825 res['setCollectionLimit'] =826 await helper.arrange.calculcateFeeGas(827 {Ethereum: ethSigner},828 () => evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),829 );830831 res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(832 {Substrate: donor.address},833 () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),834 )));835836 const {collectionAddress} = await helper.eth.createCollection('nft', ethSigner, 'A', 'B', 'C');837 const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);838839840 res['addCollectionAdminCross'] =841 await helper.arrange.calculcateFeeGas(842 {Ethereum: ethSigner},843 () => collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),844 );845846 res['removeCollectionAdminCross'] =847 await helper.arrange.calculcateFeeGas(848 {Ethereum: ethSigner},849 () => collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),850 );851852 res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(853 {Substrate: donor.address},854 () => collection.addAdmin(donor, {Ethereum: ethReceiver}),855 )));856857 res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(858 {Substrate: donor.address},859 () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),860 )));861862 res['setCollectionNesting'] =863 await helper.arrange.calculcateFeeGas(864 {Ethereum: ethSigner},865 () => evmContract.methods.setCollectionNesting(true).send(),866 );867868 res['setCollectionNesting[]'] =869 await helper.arrange.calculcateFeeGas(870 {Ethereum: ethSigner},871 () => evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),872 );873874 res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(875 {Substrate: donor.address},876 () => collection.disableNesting(donor),877 )));878879 res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(880 {Substrate: donor.address},881 () => collection.setPermissions(882 donor,883 {884 nesting: {885 tokenOwner: true,886 restricted: [collection.collectionId],887 },888 },889 ),890 )));891892 res['setCollectionAccess'] =893 await helper.arrange.calculcateFeeGas(894 {Ethereum: ethSigner},895 () => evmContract.methods.setCollectionAccess(1).send(),896 );897898 res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(899 {Substrate: donor.address},900 () => collection.setPermissions(donor, {access: 'AllowList'}),901 )));902903 res['addToCollectionAllowListCross'] =904 await helper.arrange.calculcateFeeGas(905 {Ethereum: ethSigner},906 () => evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),907 );908909 res['removeFromCollectionAllowListCross'] =910 await helper.arrange.calculcateFeeGas(911 {Ethereum: ethSigner},912 () => evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),913 );914915 res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(916 {Substrate: donor.address},917 () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),918 )));919920 res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(921 {Substrate: donor.address},922 () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),923 )));924925 res['setCollectionMintMode'] =926 await helper.arrange.calculcateFeeGas(927 {Ethereum: ethSigner},928 () => evmContract.methods.setCollectionMintMode(true).send(),929 );930931 res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(932 {Substrate: donor.address},933 () => collection.setPermissions(donor, {mintMode: false}),934 )));935936 res['changeCollectionOwnerCross'] =937 await helper.arrange.calculcateFeeGas(938 {Ethereum: ethSigner},939 () => collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),940 );941942 res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(943 {Substrate: donor.address},944 () => collection.changeOwner(donor, subReceiver.address),945 )));946947 return res;948}949950async function erc20CalculateFeeGas(951 helper: EthUniqueHelper,952 privateKey: (seed: string) => Promise<IKeyringPair>,953954): Promise<IFunctionFee> {955 const res: IFunctionFee = {};956 const donor = await privateKey('//Alice');957 const [subReceiver] = await helper.arrange.createAccounts([10n], donor);958 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);959 const ethReceiver = await helper.eth.createAccountWithBalance(donor, 10n);960 const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);961 const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);962 const collection = (await createCollectionForBenchmarks(963 'ft',964 helper,965 privateKey,966 ethSigner,967 null,968 PERMISSIONS,969 )) as UniqueFTCollection;970971 const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);972 let zeppelelinContract: Contract | null = null;973 const ZEPPELIN_OBJECT = '0x' + (await readFile(`${__dirname}/openZeppelin/ERC20/bin/ZeppelinContract.bin`)).toString();974 const ZEPPELIN_ABI = JSON.parse((await readFile(`${__dirname}/openZeppelin/ERC20/bin/ZeppelinContract.abi`)).toString());975976 const evmContract = await helper.ethNativeContract.collection(977 helper.ethAddress.fromCollectionId(collection.collectionId),978 'ft',979 ethSigner,980 true,981 );982983 res['createCollection'] = await helper.arrange.calculcateFeeGas(984 {Ethereum: ethSigner},985 () => helperContract.methods.createFTCollection('test', 18,'test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}),986 );987988 res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee(989 {Substrate: donor.address},990 () => helper.ft.mintCollection(991 donor,992 {name: 'test', description: 'test', tokenPrefix: 'test'},993 18,994 ),995 )));996997 res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas(998 {Ethereum: ethSigner},999 async () => {1000 zeppelelinContract = await helper.ethContract.deployByAbi(1001 ethSigner,1002 ZEPPELIN_ABI,1003 ZEPPELIN_OBJECT,1004 );1005 },1006 );10071008 res['mint'] =1009 await helper.arrange.calculcateFeeGas(1010 {Ethereum: ethSigner},1011 () => evmContract.methods.mint(ethSigner, 1).send(),1012 );10131014 res['mint'].zeppelin =1015 await helper.arrange.calculcateFeeGas(1016 {Ethereum: ethSigner},1017 () => zeppelelinContract!.methods.mint(ethSigner, 1).send(),1018 );10191020 res['mintCross'] =1021 await helper.arrange.calculcateFeeGas(1022 {Ethereum: ethSigner},1023 () => evmContract.methods.mintCross(crossSigner, 1).send(),1024 );10251026 res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1027 {Substrate: donor.address},1028 () => collection.mint(1029 donor,1030 10n,1031 {Substrate: donor.address},1032 ),1033 )));10341035 res['mintBulk'] =1036 await helper.arrange.calculcateFeeGas(1037 {Ethereum: ethSigner},1038 () => evmContract.methods.mintBulk([{to: ethSigner, amount: 1}]).send(),1039 );10401041 res['mintBulk'].substrate = convertToTokens((await helper.arrange.calculcateFee(1042 {Substrate: donor.address},1043 () => helper.executeExtrinsic(donor, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, {1044 Fungible: new Map([1045 [JSON.stringify({Ethereum: ethSigner}), 1],10461047 ]),1048 }], true),1049 )));10501051 res['transfer*'] =1052 await helper.arrange.calculcateFeeGas(1053 {Ethereum: ethSigner},1054 () => evmContract.methods.transfer(ethReceiver, 1).send(),1055 );10561057 res['transfer*'].zeppelin =1058 await helper.arrange.calculcateFeeGas(1059 {Ethereum: ethSigner},1060 () => zeppelelinContract!.methods.transfer(ethReceiver, 1).send(),1061 );10621063 res['transferCross'] =1064 await helper.arrange.calculcateFeeGas(1065 {Ethereum: ethReceiver},1066 () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),1067 );10681069 res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1070 {Substrate: donor.address},1071 () => collection.transfer(1072 donor,1073 {Substrate: subReceiver.address},1074 1n,1075 ),1076 )));1077 await collection.approveTokens(subReceiver, {Substrate: donor.address}, 1n);10781079 res['transferFrom*'] =1080 await helper.arrange.calculcateFeeGas(1081 {Ethereum: ethSigner},1082 () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),1083 );10841085 res['transferFrom*'].zeppelin =1086 await helper.arrange.calculcateFeeGas(1087 {Ethereum: ethReceiver},1088 () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}),1089 );10901091 res['transferFromCross'] =1092 await helper.arrange.calculcateFeeGas(1093 {Ethereum: ethReceiver},1094 () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),1095 );10961097 res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1098 {Substrate: donor.address},1099 () => collection.transferFrom(donor, {Substrate: subReceiver.address}, {Substrate: donor.address}, 1n),1100 )));110111021103 res['burnTokens'] = {fee: 0n, gas: 0n};1104 res['burnTokens'].substrate = convertToTokens((await helper.arrange.calculcateFee(1105 {Substrate: donor.address},1106 () => collection.burnTokens(donor, 1n),1107 )));110811091110 res['approve*'] =1111 await helper.arrange.calculcateFeeGas(1112 {Ethereum: ethSigner},1113 () => evmContract.methods.approve(ethReceiver, 2).send(),1114 );11151116 res['approve*'].zeppelin =1117 await helper.arrange.calculcateFeeGas(1118 {Ethereum: ethSigner},1119 () => zeppelelinContract!.methods.approve(ethReceiver, 0).send(),1120 );11211122 res['approveCross'] =1123 await helper.arrange.calculcateFeeGas(1124 {Ethereum: ethSigner},1125 () => evmContract.methods.approveCross(crossReceiver, 2).send(),1126 );11271128 res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1129 {Substrate: donor.address},1130 () => collection.approveTokens(donor, {Substrate: subReceiver.address}, 1n),1131 )));11321133 res['burnFromCross'] =1134 await helper.arrange.calculcateFeeGas(1135 {Ethereum: ethReceiver},1136 () => evmContract.methods.burnFromCross(crossSigner, 1).send({from:ethReceiver}),1137 );11381139 res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1140 {Substrate: subReceiver.address},1141 () => collection.burnTokensFrom(subReceiver, {Substrate: donor.address}, 1n),1142 )));11431144 res['setCollectionSponsorCross'] =1145 await helper.arrange.calculcateFeeGas(1146 {Ethereum: ethSigner},1147 () => evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),1148 );11491150 res['confirmCollectionSponsorship'] =1151 await helper.arrange.calculcateFeeGas(1152 {Ethereum: ethReceiver},1153 () => evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),1154 );11551156 res['removeCollectionSponsor'] =1157 await helper.arrange.calculcateFeeGas(1158 {Ethereum: ethSigner},1159 () => evmContract.methods.removeCollectionSponsor().send(),1160 );11611162 res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1163 {Substrate: donor.address},1164 () => collection.setSponsor(donor, subReceiver.address),1165 )));11661167 res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(1168 {Substrate: subReceiver.address},1169 () => collection.confirmSponsorship(subReceiver),1170 )));11711172 res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(1173 {Substrate: donor.address},1174 () => collection.removeSponsor(donor),1175 )));11761177 res['setCollectionProperties'] =1178 await helper.arrange.calculcateFeeGas(1179 {Ethereum: ethSigner},1180 () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),1181 );11821183 res['deleteCollectionProperties'] =1184 await helper.arrange.calculcateFeeGas(1185 {Ethereum: ethSigner},1186 () => evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),1187 );1188 res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(1189 {Substrate: donor.address},1190 () => collection.setProperties(donor, PROPERTIES.slice(0, 1)1191 .map(p => { return {key: p.key, value: p.value.toString()}; })),1192 )));11931194 res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(1195 {Substrate: donor.address},1196 () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)1197 .map(p => p.key)),1198 )));11991200 res['setCollectionLimit'] =1201 await helper.arrange.calculcateFeeGas(1202 {Ethereum: ethSigner},1203 () => evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),1204 );12051206 res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(1207 {Substrate: donor.address},1208 () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),1209 )));12101211 const {collectionAddress} = await helper.eth.createCollection('nft', ethSigner, 'A', 'B', 'C');1212 const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);121312141215 res['addCollectionAdminCross'] =1216 await helper.arrange.calculcateFeeGas(1217 {Ethereum: ethSigner},1218 () => collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),1219 );12201221 res['removeCollectionAdminCross'] =1222 await helper.arrange.calculcateFeeGas(1223 {Ethereum: ethSigner},1224 () => collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),1225 );12261227 res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1228 {Substrate: donor.address},1229 () => collection.addAdmin(donor, {Ethereum: ethReceiver}),1230 )));12311232 res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1233 {Substrate: donor.address},1234 () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),1235 )));12361237 res['setCollectionNesting'] =1238 await helper.arrange.calculcateFeeGas(1239 {Ethereum: ethSigner},1240 () => evmContract.methods.setCollectionNesting(true).send(),1241 );12421243 res['setCollectionNesting[]'] =1244 await helper.arrange.calculcateFeeGas(1245 {Ethereum: ethSigner},1246 () => evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),1247 );12481249 res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(1250 {Substrate: donor.address},1251 () => collection.disableNesting(donor),1252 )));12531254 res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(1255 {Substrate: donor.address},1256 () => collection.setPermissions(1257 donor,1258 {1259 nesting: {1260 tokenOwner: true,1261 restricted: [collection.collectionId],1262 },1263 },1264 ),1265 )));12661267 res['setCollectionAccess'] =1268 await helper.arrange.calculcateFeeGas(1269 {Ethereum: ethSigner},1270 () => evmContract.methods.setCollectionAccess(1).send(),1271 );12721273 res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(1274 {Substrate: donor.address},1275 () => collection.setPermissions(donor, {access: 'AllowList'}),1276 )));12771278 res['addToCollectionAllowListCross'] =1279 await helper.arrange.calculcateFeeGas(1280 {Ethereum: ethSigner},1281 () => evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),1282 );12831284 res['removeFromCollectionAllowListCross'] =1285 await helper.arrange.calculcateFeeGas(1286 {Ethereum: ethSigner},1287 () => evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),1288 );12891290 res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1291 {Substrate: donor.address},1292 () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),1293 )));12941295 res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1296 {Substrate: donor.address},1297 () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),1298 )));12991300 res['setCollectionMintMode'] =1301 await helper.arrange.calculcateFeeGas(1302 {Ethereum: ethSigner},1303 () => evmContract.methods.setCollectionMintMode(true).send(),1304 );13051306 res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(1307 {Substrate: donor.address},1308 () => collection.setPermissions(donor, {mintMode: false}),1309 )));13101311 res['changeCollectionOwnerCross'] =1312 await helper.arrange.calculcateFeeGas(1313 {Ethereum: ethSigner},1314 () => collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),1315 );13161317 res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(1318 {Substrate: donor.address},1319 () => collection.changeOwner(donor, subReceiver.address),1320 )));13211322 return res;1323}tests/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
tests/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);
+ });
+}
+
+
+
tests/src/benchmarks/mintFee/opsFee.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/benchmarks/mintFee/opsFee.ts
@@ -0,0 +1,881 @@
+import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';
+import {readFile} from 'fs/promises';
+import {CollectionLimitField, TokenPermissionField} from '../../eth/util/playgrounds/types';
+import {IKeyringPair} from '@polkadot/types/types';
+import {UniqueFTCollection, UniqueNFTCollection} from '../../util/playgrounds/unique';
+import {Contract} from 'web3-eth-contract';
+import {createObjectCsvWriter} from 'csv-writer';
+import {FunctionFeeVM, IFunctionFee} from './types';
+import {convertToTokens, createCollectionForBenchmarks, PERMISSIONS, PROPERTIES, SUBS_PROPERTIES} from './common';
+
+
+
+const main = async () => {
+
+ const headers = [
+ 'function',
+ 'ethFee',
+ 'ethGas',
+ 'substrate',
+ 'zeppelinFee',
+ 'zeppelinGas',
+ ];
+
+ const csvWriter20 = createObjectCsvWriter({
+ path: 'erc20.csv',
+ header: headers,
+ });
+
+ const csvWriter721 = createObjectCsvWriter({
+ path: 'erc721.csv',
+ header: headers,
+ });
+
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ console.log('\n ERC20 ops fees');
+ const erc20 = FunctionFeeVM.fromModel(await erc20CalculateFeeGas(helper, privateKey));
+ console.table(erc20);
+ await csvWriter20.writeRecords(FunctionFeeVM.toCsv(erc20));
+
+ console.log('\n ERC721 ops fees');
+ const erc721 = FunctionFeeVM.fromModel(await erc721CalculateFeeGas(helper, privateKey));
+ console.table(erc721);
+
+ await csvWriter721.writeRecords(FunctionFeeVM.toCsv(erc721));
+ });
+};
+
+main()
+ .then(() => process.exit(0))
+ .catch((e) => {
+ console.log(e);
+ process.exit(1);
+ });
+
+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
tests/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 = {};