difftreelog
feat(bench) added fee benchmarks for erc721 functions
in: master
3 files changed
tests/src/benchmarks/mintFee/benchmark.tsdiffbeforeafterboth--- a/tests/src/benchmarks/mintFee/benchmark.ts
+++ b/tests/src/benchmarks/mintFee/benchmark.ts
@@ -1,6 +1,6 @@
-import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';
+import {EthUniqueHelper, SponsoringMode, usingEthPlaygrounds} from '../../eth/util';
import {readFile} from 'fs/promises';
-import {ContractImports, TokenPermissionField} from '../../eth/util/playgrounds/types';
+import {CollectionLimitField, ContractImports, TokenPermissionField} from '../../eth/util/playgrounds/types';
import {
ICrossAccountId,
ITokenPropertyPermission,
@@ -9,6 +9,7 @@
import {UniqueNFTCollection} from '../../util/playgrounds/unique';
import {Contract} from 'web3-eth-contract';
import {createObjectCsvWriter} from 'csv-writer';
+import {FeeGas} from '../../eth/util/playgrounds/unique.dev';
const CONTRACT_IMPORT: ContractImports[] = [
{
@@ -95,6 +96,9 @@
const ZEPPELIN_OBJECT = '0x' + (await readFile(`${__dirname}/openZeppelin/bin/ZeppelinContract.bin`)).toString();
const ZEPPELIN_ABI = JSON.parse((await readFile(`${__dirname}/openZeppelin/bin/ZeppelinContract.abi`)).toString());
+ console.log('\n ERC721 ops fees');
+ console.table(await ERC721CalculateFeeGas(helper, privateKey), ['fee', 'gas', 'substrate']);
+
const donor = await privateKey('//Alice'); // Seed from account with balance on this network
const [substrateReceiver] = await helper.arrange.createAccounts([10n], donor);
@@ -261,7 +265,7 @@
helper: EthUniqueHelper,
privateKey: (seed: string) => Promise<IKeyringPair>,
ethSigner: string,
- proxyContract: string,
+ proxyContract: string | null,
permissions: ITokenPropertyPermission[],
) {
const donor = await privateKey('//Alice');
@@ -279,6 +283,14 @@
mutable: true,
},
},
+ {
+ key: 'URI',
+ permission: {
+ tokenOwner: true,
+ collectionAdmin: true,
+ mutable: true,
+ },
+ },
],
limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0},
permissions: {mintMode: true},
@@ -292,8 +304,12 @@
await collection.addAdmin(donor, {
Ethereum: helper.address.substrateToEth(donor.address),
});
- await collection.addToAllowList(donor, {Ethereum: proxyContract});
- await collection.addAdmin(donor, {Ethereum: proxyContract});
+
+ if (proxyContract) {
+ await collection.addToAllowList(donor, {Ethereum: proxyContract});
+ await collection.addAdmin(donor, {Ethereum: proxyContract});
+ }
+
await collection.setTokenPropertyPermissions(donor, permissions);
return collection;
@@ -577,3 +593,223 @@
function convertToTokens(value: bigint, nominal: bigint): number {
return Number((value * 1000n) / nominal) / 1000;
}
+
+interface IFeeGas {
+ fee: number | bigint,
+ gas: number | bigint,
+ substrate?: number,
+}
+interface IFunctionFee {
+ [name: string]: IFeeGas
+}
+
+async function ERC721CalculateFeeGas(
+ helper: EthUniqueHelper,
+ privateKey: (seed: string) => Promise<IKeyringPair>,
+
+): Promise<IFunctionFee> {
+ const res: IFunctionFee = {};
+ const donor = await privateKey('//Alice');
+ 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(
+ helper,
+ privateKey,
+ ethSigner,
+ null,
+ PERMISSIONS,
+ );
+
+ const evmContract = await helper.ethNativeContract.collection(
+ helper.ethAddress.fromCollectionId(collection.collectionId),
+ 'nft',
+ ethSigner,
+ true,
+ );
+
+ res['mint'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.mint(ethSigner).send(),
+ );
+
+ res['mintCross'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.mintCross(crossSigner, []).send(),
+ );
+
+ res['mintWithTokenURI'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.mintWithTokenURI(ethSigner, 'Test URI').send(),
+ );
+
+ 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['transfer'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.transfer(ethReceiver, 1).send(),
+ );
+
+ res['transferCross'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethReceiver},
+ () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),
+ );
+
+ res['transferFrom'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),
+ );
+
+ res['transferFromCross'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethReceiver},
+ () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),
+ );
+
+ res['burn'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.burn(1).send(),
+ );
+
+ res['approveCross'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.approveCross(crossReceiver, 2).send(),
+ );
+
+ res['burnFromCross'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethReceiver},
+ () => evmContract.methods.burnFromCross(crossSigner, 2).send({from:ethReceiver}),
+ );
+
+ res['setTokenPropertyPermissions'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.setTokenPropertyPermissions([
+ ['url', [
+ [TokenPermissionField.Mutable, true],
+ [TokenPermissionField.TokenOwner, true],
+ [TokenPermissionField.CollectionAdmin, true]],
+ ],
+ ]).send(),
+ );
+
+ 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['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['setCollectionLimit'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),
+ );
+
+ 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['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['setCollectionAcces'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.setCollectionAccess(SponsoringMode.Allowlisted).send(),
+ );
+
+ 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['setCollectionMintMode'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => evmContract.methods.setCollectionMintMode(true).send(),
+ );
+
+ res['changeCollectionOwnerCross'] =
+ await helper.arrange.calculcateFeeGas(
+ {Ethereum: ethSigner},
+ () => collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),
+ );
+ for (const name in res) {
+ res[name] = {fee: res[name].fee, gas: res[name].gas};
+ }
+ return res;
+}
\ No newline at end of file
tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth16import {evmToAddress} from '@polkadot/util-crypto';16import {evmToAddress} from '@polkadot/util-crypto';17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';181819import {DevUniqueHelper} from '../../../util/playgrounds/unique.dev';19import {ArrangeGroup, DevUniqueHelper} from '../../../util/playgrounds/unique.dev';202021import {ContractImports, CompiledContract, CrossAddress, NormalizedEvent, EthProperty} from './types';21import {ContractImports, CompiledContract, CrossAddress, NormalizedEvent, EthProperty} from './types';2222461 }461 }462}462}463463464export class FeeGas {465 fee: number | bigint = 0n;466467 gas: number | bigint = 0n;468469 public static async build(helper: EthUniqueHelper, fee: bigint): Promise<FeeGas> {470 const instance = new FeeGas();471 instance.fee = instance.convertToTokens(fee);472 instance.gas = await instance.convertToGas(fee, helper);473 return instance;474 }475476 private async convertToGas(fee: bigint, helper: EthUniqueHelper): Promise<bigint> {477 const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());478 return fee / gasPrice;479 }480481 private convertToTokens(value: bigint, nominal = 1_000_000_000_000_000_000n): number {482 return Number((value * 1000n) / nominal) / 1000;483 }484}485486class EthArrangeGroup extends ArrangeGroup {487 helper: EthUniqueHelper;488489 constructor(helper: EthUniqueHelper) {490 super(helper);491 this.helper = helper;492 }493494 async calculcateFeeGas(payer: ICrossAccountId, promise: () => Promise<any>): Promise<FeeGas> {495 const fee = await this.calculcateFee(payer, promise);496 return await FeeGas.build(this.helper, fee);497 }498}464export class EthUniqueHelper extends DevUniqueHelper {499export class EthUniqueHelper extends DevUniqueHelper {465 web3: Web3 | null = null;500 web3: Web3 | null = null;466 web3Provider: WebsocketProvider | null = null;501 web3Provider: WebsocketProvider | null = null;471 ethNativeContract: NativeContractGroup;506 ethNativeContract: NativeContractGroup;472 ethContract: ContractGroup;507 ethContract: ContractGroup;473 ethProperty: EthPropertyGroup;508 ethProperty: EthPropertyGroup;474509 arrange: EthArrangeGroup;475 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {510 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {476 options.helperBase = options.helperBase ?? EthUniqueHelper;511 options.helperBase = options.helperBase ?? EthUniqueHelper;477512482 this.ethNativeContract = new NativeContractGroup(this);517 this.ethNativeContract = new NativeContractGroup(this);483 this.ethContract = new ContractGroup(this);518 this.ethContract = new ContractGroup(this);484 this.ethProperty = new EthPropertyGroup(this);519 this.ethProperty = new EthPropertyGroup(this);520 this.arrange = new EthArrangeGroup(this);521 super.arrange = this.arrange;485 }522 }486523487 getWeb3(): Web3 {524 getWeb3(): Web3 {tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -187,7 +187,7 @@
export class DevKaruraHelper extends DevAcalaHelper {}
-class ArrangeGroup {
+export class ArrangeGroup {
helper: DevUniqueHelper;
scheduledIdSlider = 0;