1import {EthUniqueHelper, SponsoringMode, usingEthPlaygrounds} from '../../eth/util';2import {readFile} from 'fs/promises';3import {CollectionLimitField, ContractImports, TokenPermissionField} from '../../eth/util/playgrounds/types';4import {5 ICrossAccountId,6 ITokenPropertyPermission,7} from '../../util/playgrounds/types';8import {IKeyringPair} from '@polkadot/types/types';9import {UniqueNFTCollection} from '../../util/playgrounds/unique';10import {Contract} from 'web3-eth-contract';11import {createObjectCsvWriter} from 'csv-writer';1213const CONTRACT_IMPORT: ContractImports[] = [14 {15 fsPath: `${__dirname}/../../eth/api/CollectionHelpers.sol`,16 solPath: 'eth/api/CollectionHelpers.sol',17 },18 {19 fsPath: `${__dirname}/../../eth/api/ContractHelpers.sol`,20 solPath: 'eth/api/ContractHelpers.sol',21 },22 {23 fsPath: `${__dirname}/../../eth/api/UniqueRefungibleToken.sol`,24 solPath: 'eth/api/UniqueRefungibleToken.sol',25 },26 {27 fsPath: `${__dirname}/../../eth/api/UniqueRefungible.sol`,28 solPath: 'eth/api/UniqueRefungible.sol',29 },30 {31 fsPath: `${__dirname}/../../eth/api/UniqueNFT.sol`,32 solPath: 'eth/api/UniqueNFT.sol',33 },34];3536const PROPERTIES = Array(40)37 .fill(0)38 .map((_, i) => {39 return {40 key: `key_${i}`,41 value: Uint8Array.from(Buffer.from(`value_${i}`)),42 };43 });4445const SUBS_PROPERTIES = Array(40)46 .fill(0)47 .map((_, i) => {48 return {49 key: `key_${i}`,50 value: `value_${i}`,51 };52 });5354const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => {55 return {56 key: p.key,57 permission: {58 tokenOwner: true,59 collectionAdmin: true,60 mutable: true,61 },62 };63});6465interface IBenchmarkResultForProp {66 propertiesNumber: number;67 substrateFee: number;68 ethFee: number;69 ethBulkFee: number;70 ethMintCrossFee: number;71 evmProxyContractFee: number;72 evmProxyContractBulkFee: number;73}74interface IBenchmarkCollection {75 createFee: number,76 mintFee: number,77 transferFee: number,78}79const main = async () => {80 const benchmarks = [81 'substrateFee',82 'ethFee',83 'ethBulkFee',84 'ethMintCrossFee',85 'evmProxyContractFee',86 'evmProxyContractBulkFee',87 ];88 const headers = [89 'propertiesNumber',90 ...benchmarks,91 ];929394 const csvWriter = createObjectCsvWriter({95 path: 'properties.csv',96 header: headers,97 });9899 await usingEthPlaygrounds(async (helper, privateKey) => {100 const NOMINAL = helper.balance.getOneTokenNominal();101 const CONTRACT_SOURCE = (102 await readFile(`${__dirname}/proxyContract.sol`)103 ).toString();104 const ZEPPELIN_OBJECT = '0x' + (await readFile(`${__dirname}/openZeppelin/bin/ZeppelinContract.bin`)).toString();105 const ZEPPELIN_ABI = JSON.parse((await readFile(`${__dirname}/openZeppelin/bin/ZeppelinContract.abi`)).toString());106107 console.log('\n ERC721 ops fees');108 console.table(await ERC721CalculateFeeGas(helper, privateKey), ['fee', 'gas', 'substrate']);109110 const donor = await privateKey('//Alice'); 111 const [substrateReceiver] = await helper.arrange.createAccounts([10n], donor);112113 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);114 const ethReceiver = await helper.eth.createAccountWithBalance(donor, 5n);115116 let susbtrateCollection: UniqueNFTCollection | null;117 const createCollectionSubstrateFee = convertToTokens(118 await helper.arrange.calculcateFee({Substrate: donor.address}, async () => {119 susbtrateCollection = await helper.nft.mintCollection(donor, {tokenPropertyPermissions: [120 {121 key: 'url',122 permission: {123 tokenOwner: true,124 collectionAdmin: true,125 mutable: true,126 },127 },128 ]});129 }),130 NOMINAL,131 );132133 const susbstrateMintFee = convertToTokens(134 await helper.arrange.calculcateFee(135 {Substrate: donor.address},136 () => susbtrateCollection!.mintToken(donor, {Substrate: substrateReceiver.address}, [{key: 'url', value: 'test'}]),137 ),138 NOMINAL,139 );140 const susbstrateTransferFee = convertToTokens(141 await helper.arrange.calculcateFee(142 {Substrate: substrateReceiver.address},143 () => susbtrateCollection!.transferToken(substrateReceiver, 1, {Substrate: donor.address}),144 ),145 NOMINAL,146 );147 const substrateFee: IBenchmarkCollection = {148 createFee: createCollectionSubstrateFee,149 mintFee: susbstrateMintFee,150 transferFee: susbstrateTransferFee,151 };152153 const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);154155 let ethContract: Contract | null = null;156157 const createCollectionEthFee = convertToTokens(158 await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => {159 const result = await helperContract.methods.createNFTCollection('a', 'b', 'c').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())});160 ethContract = await helper.ethNativeContract.collection(helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId), 'nft', ethSigner);161 }),162 NOMINAL,163 );164165 await ethContract!.methods.setTokenPropertyPermissions([166 ['url', [167 [TokenPermissionField.Mutable, true],168 [TokenPermissionField.TokenOwner, true],169 [TokenPermissionField.CollectionAdmin, true]],170 ],171 ]).send({from: ethSigner});172173 const ethMintFee = convertToTokens(await helper.arrange.calculcateFee(174 {Ethereum: ethSigner},175 () => ethContract!.methods.mintCross(176 helper.ethCrossAccount.fromAddress(ethReceiver),177 [{key: 'url', value: Buffer.from('test')}],178 )179 .send({from: ethSigner}),180 ), NOMINAL);181182 const ethTransferFee = convertToTokens(await helper.arrange.calculcateFee(183 {Ethereum: ethReceiver},184 () => ethContract!.methods.transferFrom(ethReceiver, ethSigner, 1)185 .send({from: ethReceiver}),186 ), NOMINAL);187188 const ethFee: IBenchmarkCollection = {189 createFee: createCollectionEthFee,190 mintFee: ethMintFee,191 transferFee: ethTransferFee,192 };193194 let zeppelelinContract: Contract | null = null;195196 const zeppelinDeployFee = convertToTokens(197 await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => {198 zeppelelinContract = await helper.ethContract.deployByAbi(199 ethSigner,200 ZEPPELIN_ABI,201 ZEPPELIN_OBJECT,202 );203 }),204 NOMINAL,205 );206207 const zeppelinMintFee = convertToTokens(await helper.arrange.calculcateFee(208 {Ethereum: ethSigner},209 () => zeppelelinContract!.methods.safeMint(ethReceiver, 'test').send({from: ethSigner}),210 ), NOMINAL);211212213 const zeppelinTransferFee = convertToTokens(await helper.arrange.calculcateFee(214 {Ethereum: ethReceiver},215 () => zeppelelinContract!.methods.safeTransferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}),216 ), NOMINAL);217218 const zeppelinFee: IBenchmarkCollection = {219 createFee: zeppelinDeployFee,220 mintFee: zeppelinMintFee,221 transferFee: zeppelinTransferFee,222 };223224 console.log('collection ops fees');225 const results = {substrate: substrateFee, eth: ethFee, zeppelin: zeppelinFee};226 console.table(results);227228 const contract = await helper.ethContract.deployByCode(229 ethSigner,230 'ProxyMint',231 CONTRACT_SOURCE,232 CONTRACT_IMPORT,233 );234235 const fees = await benchMintFee(helper, privateKey, contract);236 console.log('Minting without properties');237 console.table(fees);238239 const result: IBenchmarkResultForProp[] = [];240 const csvResult: IBenchmarkResultForProp[] = [];241242 for (let i = 1; i <= 20; i++) {243 const benchResult = await benchMintWithProperties(helper, privateKey, contract, {244 propertiesNumber: i,245 }) as any;246247 csvResult.push(benchResult);248249 const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));250 for(const key of benchmarks) {251 const keyPercent = Math.round((benchResult[key] / minFee) * 100);252 benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;253 }254255 result.push(benchResult);256 }257258 await csvWriter.writeRecords(csvResult);259260 console.log('Minting with properties');261 console.table(result, headers);262 });263};264265main()266 .then(() => process.exit(0))267 .catch((e) => {268 console.log(e);269 process.exit(1);270 });271272async function createCollectionForBenchmarks(273 helper: EthUniqueHelper,274 privateKey: (seed: string) => Promise<IKeyringPair>,275 ethSigner: string,276 proxyContract: string | null,277 permissions: ITokenPropertyPermission[],278) {279 const donor = await privateKey('//Alice');280281 const collection = await helper.nft.mintCollection(donor, {282 name: 'test mintToSubstrate',283 description: 'EVMHelpers',284 tokenPrefix: 'ap',285 tokenPropertyPermissions: [286 {287 key: 'url',288 permission: {289 tokenOwner: true,290 collectionAdmin: true,291 mutable: true,292 },293 },294 {295 key: 'URI',296 permission: {297 tokenOwner: true,298 collectionAdmin: true,299 mutable: true,300 },301 },302 ],303 limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0},304 permissions: {mintMode: true},305 });306307 await collection.addToAllowList(donor, {308 Ethereum: helper.address.substrateToEth(donor.address),309 });310 await collection.addToAllowList(donor, {Substrate: donor.address});311 await collection.addAdmin(donor, {Ethereum: ethSigner});312 await collection.addAdmin(donor, {313 Ethereum: helper.address.substrateToEth(donor.address),314 });315316 if (proxyContract) {317 await collection.addToAllowList(donor, {Ethereum: proxyContract});318 await collection.addAdmin(donor, {Ethereum: proxyContract});319 }320321 await collection.setTokenPropertyPermissions(donor, permissions);322323 return collection;324}325326async function benchMintFee(327 helper: EthUniqueHelper,328 privateKey: (seed: string) => Promise<IKeyringPair>,329 proxyContract: Contract,330): Promise<{331 substrateFee: number;332 ethFee: number;333 evmProxyContractFee: number;334}> {335 const donor = await privateKey('//Alice');336 const substrateReceiver = await privateKey('//Bob');337 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);338339 const nominal = helper.balance.getOneTokenNominal();340341 await helper.eth.transferBalanceFromSubstrate(342 donor,343 proxyContract.options.address,344 100n,345 );346347 const collection = await createCollectionForBenchmarks(348 helper,349 privateKey,350 ethSigner,351 proxyContract.options.address,352 PERMISSIONS,353 );354355 const substrateFee = await helper.arrange.calculcateFee(356 {Substrate: donor.address},357 () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),358 );359360 const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);361 const collectionContract = await helper.ethNativeContract.collection(362 collectionEthAddress,363 'nft',364 );365366 const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);367368 const encodedCall = collectionContract.methods369 .mint(receiverEthAddress)370 .encodeABI();371372 const ethFee = await helper.arrange.calculcateFee(373 {Substrate: donor.address},374 async () => {375 await helper.eth.sendEVM(376 donor,377 collectionContract.options.address,378 encodedCall,379 '0',380 );381 },382 );383384 const evmProxyContractFee = await helper.arrange.calculcateFee(385 {Ethereum: ethSigner},386 async () => {387 await proxyContract.methods388 .mintToSubstrate(389 helper.ethAddress.fromCollectionId(collection.collectionId),390 substrateReceiver.addressRaw,391 )392 .send({from: ethSigner});393 },394 );395396 return {397 substrateFee: convertToTokens(substrateFee, nominal),398 ethFee: convertToTokens(ethFee, nominal),399 evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),400 };401}402403async function benchMintWithProperties(404 helper: EthUniqueHelper,405 privateKey: (seed: string) => Promise<IKeyringPair>,406 proxyContract: Contract,407 setup: { propertiesNumber: number },408): Promise<IBenchmarkResultForProp> {409 const donor = await privateKey('//Alice'); 410 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);411412 const susbstrateReceiver = await privateKey('//Bob');413 const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);414415 const nominal = helper.balance.getOneTokenNominal();416417 const substrateFee = await calculateFeeNftMintWithProperties(418 helper,419 privateKey,420 {Substrate: donor.address},421 ethSigner,422 proxyContract.options.address,423 async (collection) => {424 await collection.mintToken(425 donor,426 {Substrate: susbstrateReceiver.address},427 PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {428 return {key: p.key, value: Buffer.from(p.value).toString()};429 }),430 );431 },432 );433434 const ethFee = await calculateFeeNftMintWithProperties(435 helper,436 privateKey,437 {Substrate: donor.address},438 ethSigner,439 proxyContract.options.address,440 async (collection) => {441 const evmContract = await helper.ethNativeContract.collection(442 helper.ethAddress.fromCollectionId(collection.collectionId),443 'nft',444 undefined,445 true,446 );447448 const subTokenId = await evmContract.methods.nextTokenId().call();449450 let encodedCall = evmContract.methods451 .mint(receiverEthAddress)452 .encodeABI();453454 await helper.eth.sendEVM(455 donor,456 evmContract.options.address,457 encodedCall,458 '0',459 );460461 for (const val of PROPERTIES.slice(0, setup.propertiesNumber)) {462 encodedCall = await evmContract.methods463 .setProperty(subTokenId, val.key, Buffer.from(val.value))464 .encodeABI();465466 await helper.eth.sendEVM(467 donor,468 evmContract.options.address,469 encodedCall,470 '0',471 );472 }473 },474 );475476 const ethBulkFee = await calculateFeeNftMintWithProperties(477 helper,478 privateKey,479 {Substrate: donor.address},480 ethSigner,481 proxyContract.options.address,482 async (collection) => {483 const evmContract = await helper.ethNativeContract.collection(484 helper.ethAddress.fromCollectionId(collection.collectionId),485 'nft',486 );487488 const subTokenId = await evmContract.methods.nextTokenId().call();489490 let encodedCall = evmContract.methods491 .mint(receiverEthAddress)492 .encodeABI();493494 await helper.eth.sendEVM(495 donor,496 evmContract.options.address,497 encodedCall,498 '0',499 );500501 encodedCall = await evmContract.methods502 .setProperties(503 subTokenId,504 PROPERTIES.slice(0, setup.propertiesNumber),505 )506 .encodeABI();507508 await helper.eth.sendEVM(509 donor,510 evmContract.options.address,511 encodedCall,512 '0',513 );514 },515 );516517 const ethMintCrossFee = await calculateFeeNftMintWithProperties(518 helper,519 privateKey,520 {Ethereum: ethSigner},521 ethSigner,522 proxyContract.options.address,523 async (collection) => {524 const evmContract = await helper.ethNativeContract.collection(525 helper.ethAddress.fromCollectionId(collection.collectionId),526 'nft',527 );528529 await evmContract.methods.mintCross(530 helper.ethCrossAccount.fromAddress(receiverEthAddress),531 PROPERTIES.slice(0, setup.propertiesNumber),532 )533 .send({from: ethSigner});534 },535 );536537 const proxyContractFee = await calculateFeeNftMintWithProperties(538 helper,539 privateKey,540 {Ethereum: ethSigner},541 ethSigner,542 proxyContract.options.address,543 async (collection) => {544 await proxyContract.methods545 .mintToSubstrateWithProperty(546 helper.ethAddress.fromCollectionId(collection.collectionId),547 susbstrateReceiver.addressRaw,548 PROPERTIES.slice(0, setup.propertiesNumber),549 )550 .send({from: ethSigner});551 },552 );553554 const proxyContractBulkFee = await calculateFeeNftMintWithProperties(555 helper,556 privateKey,557 {Ethereum: ethSigner},558 ethSigner,559 proxyContract.options.address,560 async (collection) => {561 await proxyContract.methods562 .mintToSubstrateBulkProperty(563 helper.ethAddress.fromCollectionId(collection.collectionId),564 susbstrateReceiver.addressRaw,565 PROPERTIES.slice(0, setup.propertiesNumber),566 )567 .send({from: ethSigner, gas: 25_000_000});568 },569 );570571 return {572 propertiesNumber: setup.propertiesNumber,573 substrateFee: convertToTokens(substrateFee, nominal),574 ethFee: convertToTokens(ethFee, nominal),575 ethBulkFee: convertToTokens(ethBulkFee, nominal),576 ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal),577 evmProxyContractFee: convertToTokens(proxyContractFee, nominal),578 evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),579 };580}581582async function calculateFeeNftMintWithProperties(583 helper: EthUniqueHelper,584 privateKey: (seed: string) => Promise<IKeyringPair>,585 payer: ICrossAccountId,586 ethSigner: string,587 proxyContractAddress: string,588 calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,589): Promise<bigint> {590 const collection = await createCollectionForBenchmarks(591 helper,592 privateKey,593 ethSigner,594 proxyContractAddress,595 PERMISSIONS,596 );597 return helper.arrange.calculcateFee(payer, async () => {598 await calculatedCall(collection);599 });600}601function convertToTokens(value: bigint, nominal = 1000_000_000_000_000_000n): number {602 return Number((value * 1000n) / nominal) / 1000;603}604605interface IFeeGas {606 fee: number | bigint,607 gas: number | bigint,608 substrate?: number,609}610interface IFunctionFee {611 [name: string]: IFeeGas612}613614async function ERC721CalculateFeeGas(615 helper: EthUniqueHelper,616 privateKey: (seed: string) => Promise<IKeyringPair>,617618): Promise<IFunctionFee> {619 const res: IFunctionFee = {};620 const donor = await privateKey('//Alice');621 const [subReceiver] = await helper.arrange.createAccounts([10n], donor);622 const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);623 const ethReceiver = await helper.eth.createAccountWithBalance(donor, 10n);624 const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);625 const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);626 const collection = await createCollectionForBenchmarks(627 helper,628 privateKey,629 ethSigner,630 null,631 PERMISSIONS,632 );633634 const evmContract = await helper.ethNativeContract.collection(635 helper.ethAddress.fromCollectionId(collection.collectionId),636 'nft',637 ethSigner,638 true,639 );640641 res['mint'] =642 await helper.arrange.calculcateFeeGas(643 {Ethereum: ethSigner},644 () => evmContract.methods.mint(ethSigner).send(),645 );646647 res['mintCross'] =648 await helper.arrange.calculcateFeeGas(649 {Ethereum: ethSigner},650 () => evmContract.methods.mintCross(crossSigner, []).send(),651 );652653 res['mint'].substrate = convertToTokens((await helper.arrange.calculcateFee(654 {Substrate: donor.address},655 () => collection.mintToken(656 donor,657 {Substrate: donor.address},658 ),659 )));660661 res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(662 {Substrate: donor.address},663 () => collection.mintMultipleTokens(donor, [{664 owner: {Substrate: donor.address},665 }]),666 )));667668 res['mintWithTokenURI'] =669 await helper.arrange.calculcateFeeGas(670 {Ethereum: ethSigner},671 () => evmContract.methods.mintWithTokenURI(ethSigner, 'Test URI').send(),672 );673674 res['mintWithTokenURI'].substrate = convertToTokens((await helper.arrange.calculcateFee(675 {Substrate: donor.address},676 () => collection.mintToken(677 donor,678 {Ethereum: ethSigner},679 [{key: 'URI', value: 'Test URI'}],680 ),681 )));682683 res['setProperties'] =684 await helper.arrange.calculcateFeeGas(685 {Ethereum: ethSigner},686 () => evmContract.methods.setProperties(1, PROPERTIES.slice(0,1)).send(),687 );688689 res['deleteProperties'] =690 await helper.arrange.calculcateFeeGas(691 {Ethereum: ethSigner},692 () => evmContract.methods.deleteProperties(1, [PROPERTIES[0].key]).send(),693 );694695 res['setProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(696 {Substrate: donor.address},697 () => collection.setTokenProperties(698 donor,699 1,700 SUBS_PROPERTIES.slice(0, 1),701 ),702 )));703704 res['deleteProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(705 {Substrate: donor.address},706 () => collection.deleteTokenProperties(707 donor,708 1,709 SUBS_PROPERTIES.slice(0, 1)710 .map(p => p.key),711 ),712 )));713714 res['transfer'] =715 await helper.arrange.calculcateFeeGas(716 {Ethereum: ethSigner},717 () => evmContract.methods.transfer(ethReceiver, 1).send(),718 );719720 res['transferCross'] =721 await helper.arrange.calculcateFeeGas(722 {Ethereum: ethReceiver},723 () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),724 );725726 res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(727 {Substrate: donor.address},728 () => collection.transferToken(729 donor,730 3,731 {Substrate: subReceiver.address},732 ),733 )));734 await collection.approveToken(subReceiver, 3, {Substrate: donor.address});735736 res['transferFrom'] =737 await helper.arrange.calculcateFeeGas(738 {Ethereum: ethSigner},739 () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),740 );741742 res['transferFromCross'] =743 await helper.arrange.calculcateFeeGas(744 {Ethereum: ethReceiver},745 () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),746 );747748 res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(749 {Substrate: donor.address},750 () => collection.transferTokenFrom(donor, 3, {Substrate: subReceiver.address}, {Substrate: donor.address}),751 )));752753 res['burn'] =754 await helper.arrange.calculcateFeeGas(755 {Ethereum: ethSigner},756 () => evmContract.methods.burn(1).send(),757 );758759 res['burn'].substrate = convertToTokens((await helper.arrange.calculcateFee(760 {Substrate: donor.address},761 () => collection.burnToken(donor, 3),762 )));763764765 res['approveCross'] =766 await helper.arrange.calculcateFeeGas(767 {Ethereum: ethSigner},768 () => evmContract.methods.approveCross(crossReceiver, 2).send(),769 );770771 res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(772 {Substrate: donor.address},773 () => collection.approveToken(donor, 4, {Substrate: subReceiver.address}),774 )));775776 res['burnFromCross'] =777 await helper.arrange.calculcateFeeGas(778 {Ethereum: ethReceiver},779 () => evmContract.methods.burnFromCross(crossSigner, 2).send({from:ethReceiver}),780 );781782 res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(783 {Substrate: subReceiver.address},784 () => collection.burnTokenFrom(subReceiver, 4, {Substrate: donor.address}),785 )));786787 res['setTokenPropertyPermissions'] =788 await helper.arrange.calculcateFeeGas(789 {Ethereum: ethSigner},790 () => evmContract.methods.setTokenPropertyPermissions([791 ['url', [792 [TokenPermissionField.Mutable, true],793 [TokenPermissionField.TokenOwner, true],794 [TokenPermissionField.CollectionAdmin, true]],795 ],796 ]).send(),797 );798799 res['setTokenPropertyPermissions'].substrate = convertToTokens((await helper.arrange.calculcateFee(800 {Substrate: donor.address},801 () => collection.setTokenPropertyPermissions(donor, [{key: 'url', permission: {802 tokenOwner: true,803 collectionAdmin: true,804 mutable: true,805 }}]),806 )));807808 res['setCollectionSponsorCross'] =809 await helper.arrange.calculcateFeeGas(810 {Ethereum: ethSigner},811 () => evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),812 );813814 res['confirmCollectionSponsorship'] =815 await helper.arrange.calculcateFeeGas(816 {Ethereum: ethReceiver},817 () => evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),818 );819820 res['removeCollectionSponsor'] =821 await helper.arrange.calculcateFeeGas(822 {Ethereum: ethSigner},823 () => evmContract.methods.removeCollectionSponsor().send(),824 );825826 res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(827 {Substrate: donor.address},828 () => collection.setSponsor(donor, subReceiver.address),829 )));830831 res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(832 {Substrate: subReceiver.address},833 () => collection.confirmSponsorship(subReceiver),834 )));835836 res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(837 {Substrate: donor.address},838 () => collection.removeSponsor(donor),839 )));840841 res['setCollectionProperties'] =842 await helper.arrange.calculcateFeeGas(843 {Ethereum: ethSigner},844 () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),845 );846847 res['deleteCollectionProperties'] =848 await helper.arrange.calculcateFeeGas(849 {Ethereum: ethSigner},850 () => evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),851 );852 res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(853 {Substrate: donor.address},854 () => collection.setProperties(donor, PROPERTIES.slice(0, 1)855 .map(p => { return {key: p.key, value: p.value.toString()}; })),856 )));857858 res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(859 {Substrate: donor.address},860 () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)861 .map(p => p.key)),862 )));863864 res['setCollectionLimit'] =865 await helper.arrange.calculcateFeeGas(866 {Ethereum: ethSigner},867 () => evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),868 );869870 res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(871 {Substrate: donor.address},872 () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),873 )));874875 const {collectionAddress} = await helper.eth.createCollection('nft', ethSigner, 'A', 'B', 'C');876 const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);877878879 res['addCollectionAdminCross'] =880 await helper.arrange.calculcateFeeGas(881 {Ethereum: ethSigner},882 () => collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),883 );884885 res['removeCollectionAdminCross'] =886 await helper.arrange.calculcateFeeGas(887 {Ethereum: ethSigner},888 () => collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),889 );890891 res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(892 {Substrate: donor.address},893 () => collection.addAdmin(donor, {Ethereum: ethReceiver}),894 )));895896 res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(897 {Substrate: donor.address},898 () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),899 )));900901 res['setCollectionNesting'] =902 await helper.arrange.calculcateFeeGas(903 {Ethereum: ethSigner},904 () => evmContract.methods.setCollectionNesting(true).send(),905 );906907 res['setCollectionNesting[]'] =908 await helper.arrange.calculcateFeeGas(909 {Ethereum: ethSigner},910 () => evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),911 );912913 res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(914 {Substrate: donor.address},915 () => collection.disableNesting(donor),916 )));917918 res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(919 {Substrate: donor.address},920 () => collection.setPermissions(921 donor,922 {923 nesting: {924 tokenOwner: true,925 restricted: [collection.collectionId],926 },927 },928 ),929 )));930931 res['setCollectionAccess'] =932 await helper.arrange.calculcateFeeGas(933 {Ethereum: ethSigner},934 () => evmContract.methods.setCollectionAccess(1).send(),935 );936937 res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(938 {Substrate: donor.address},939 () => collection.setPermissions(donor, {access: 'AllowList'}),940 )));941942 res['addToCollectionAllowListCross'] =943 await helper.arrange.calculcateFeeGas(944 {Ethereum: ethSigner},945 () => evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),946 );947948 res['removeFromCollectionAllowListCross'] =949 await helper.arrange.calculcateFeeGas(950 {Ethereum: ethSigner},951 () => evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),952 );953954 res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(955 {Substrate: donor.address},956 () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),957 )));958959 res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(960 {Substrate: donor.address},961 () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),962 )));963964 res['setCollectionMintMode'] =965 await helper.arrange.calculcateFeeGas(966 {Ethereum: ethSigner},967 () => evmContract.methods.setCollectionMintMode(true).send(),968 );969970 res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(971 {Substrate: donor.address},972 () => collection.setPermissions(donor, {mintMode: false}),973 )));974975 res['changeCollectionOwnerCross'] =976 await helper.arrange.calculcateFeeGas(977 {Ethereum: ethSigner},978 () => collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),979 );980981 res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(982 {Substrate: donor.address},983 () => collection.changeOwner(donor, subReceiver.address),984 )));985986 return res;987}