From 95e7af988f22e9c43a62533ee161588e94c63320 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Sat, 6 Apr 2024 21:54:24 +0200 Subject: [PATCH] Write contract names and transaction hashes to a JSON file; skip irrelevant --- gascalc/1-simple-wallet.gas.ts | 9 +- gascalc/4-paymaster-postop.gas.ts | 45 ++++++++++ gascalc/5-token-paymaster.gas.ts | 133 ++++++++++++++++++++++++++++++ gascalc/GasChecker.ts | 45 ++++++++-- 4 files changed, 223 insertions(+), 9 deletions(-) create mode 100644 gascalc/4-paymaster-postop.gas.ts create mode 100644 gascalc/5-token-paymaster.gas.ts diff --git a/gascalc/1-simple-wallet.gas.ts b/gascalc/1-simple-wallet.gas.ts index 0943d1f0d..0d80d9330 100644 --- a/gascalc/1-simple-wallet.gas.ts +++ b/gascalc/1-simple-wallet.gas.ts @@ -1,15 +1,20 @@ -import { GasChecker } from './GasChecker' +import { GasCheckCollector, GasChecker } from './GasChecker' context('simple account', function () { this.timeout(60000) const g = new GasChecker() + before(async function () { + await GasCheckCollector.init() + GasCheckCollector.inst.createJsonResult = true + }) + it('simple 1', async function () { await g.addTestRow({ title: 'simple', count: 1, diffLastGas: false }) await g.addTestRow({ title: 'simple - diff from previous', count: 2, diffLastGas: true }) }) - it('simple 10', async function () { + it.skip('simple 10', async function () { if (g.skipLong()) this.skip() await g.addTestRow({ title: 'simple', count: 10, diffLastGas: false }) await g.addTestRow({ title: 'simple - diff from previous', count: 11, diffLastGas: true }) diff --git a/gascalc/4-paymaster-postop.gas.ts b/gascalc/4-paymaster-postop.gas.ts new file mode 100644 index 000000000..6eb93a683 --- /dev/null +++ b/gascalc/4-paymaster-postop.gas.ts @@ -0,0 +1,45 @@ +import { parseEther } from 'ethers/lib/utils' +import { TestPaymasterWithPostOp__factory } from '../typechain' +import { ethers } from 'hardhat' +import { GasChecker } from './GasChecker' +import { Create2Factory } from '../src/Create2Factory' +import { hexValue } from '@ethersproject/bytes' + +const ethersSigner = ethers.provider.getSigner() + +context.skip('Paymaster with PostOp', function () { + this.timeout(60000) + const g = new GasChecker() + + let paymasterAddress: string + + before(async () => { + const paymasterInit = hexValue(new TestPaymasterWithPostOp__factory(ethersSigner).getDeployTransaction(g.entryPoint().address).data!) + paymasterAddress = await new Create2Factory(ethers.provider, ethersSigner).deploy(paymasterInit, 0) + const paymaster = TestPaymasterWithPostOp__factory.connect(paymasterAddress, ethersSigner) + await paymaster.addStake(1, { value: 1 }) + await g.entryPoint().depositTo(paymaster.address, { value: parseEther('10') }) + }) + + it('paymaster with PostOp', async function () { + await g.addTestRow({ title: 'paymaster+postOp', count: 1, paymaster: paymasterAddress, diffLastGas: false }) + await g.addTestRow({ + title: 'paymaster+postOp with diff', + count: 2, + paymaster: paymasterAddress, + diffLastGas: true + }) + }) + + it('paymaster with postOp 10', async function () { + if (g.skipLong()) this.skip() + + await g.addTestRow({ title: 'paymaster+postOp', count: 10, paymaster: paymasterAddress, diffLastGas: false }) + await g.addTestRow({ + title: 'paymaster+postOp with diff', + count: 11, + paymaster: paymasterAddress, + diffLastGas: true + }) + }) +}) diff --git a/gascalc/5-token-paymaster.gas.ts b/gascalc/5-token-paymaster.gas.ts new file mode 100644 index 000000000..6d44bb3c5 --- /dev/null +++ b/gascalc/5-token-paymaster.gas.ts @@ -0,0 +1,133 @@ +import { parseEther } from 'ethers/lib/utils' +import { + TestERC20__factory, TestOracle2__factory, + TestUniswap__factory, + TestWrappedNativeToken__factory, TokenPaymaster, + TokenPaymaster__factory +} from '../typechain' +import { ethers } from 'hardhat' +import { GasCheckCollector, GasChecker } from './GasChecker' +import { Create2Factory } from '../src/Create2Factory' +import { hexValue } from '@ethersproject/bytes' +import { + OracleHelper as OracleHelperNamespace, + UniswapHelper as UniswapHelperNamespace +} from '../typechain/contracts/samples/TokenPaymaster' +import { BigNumber } from 'ethers' +import { createAccountOwner } from '../test/testutils' +// const ethersSigner = ethers.provider.getSigner() + +context.skip('Token Paymaster', function () { + this.timeout(60000) + const g = new GasChecker() + + let paymasterAddress: string + before(async () => { + await GasCheckCollector.init() + const globalSigner = ethers.provider.getSigner() + const create2Factory = new Create2Factory(ethers.provider, globalSigner) + + const ethersSigner = createAccountOwner() + await globalSigner.sendTransaction({ to: ethersSigner.getAddress(), value: parseEther('10') }) + + const minEntryPointBalance = 1e17.toString() + const initialPriceToken = 100000000 // USD per TOK + const initialPriceEther = 500000000 // USD per ETH + const priceDenominator = BigNumber.from(10).pow(26) + + const tokenInit = await new TestERC20__factory(ethersSigner).getDeployTransaction(6) + const tokenAddress = await create2Factory.deploy(tokenInit, 0) + const token = TestERC20__factory.connect(tokenAddress, ethersSigner) + + const wethInit = await new TestWrappedNativeToken__factory(ethersSigner).getDeployTransaction() + const wethAddress = await create2Factory.deploy(wethInit, 0) + const testUniswapInit = await new TestUniswap__factory(ethersSigner).getDeployTransaction(wethAddress) + const testUniswapAddress = await create2Factory.deploy(testUniswapInit, 0) + + const tokenPaymasterConfig: TokenPaymaster.TokenPaymasterConfigStruct = { + priceMaxAge: 86400, + refundPostopCost: 40000, + minEntryPointBalance, + priceMarkup: priceDenominator.mul(15).div(10) // +50% + } + + const nativeAssetOracleInit = await new TestOracle2__factory(ethersSigner).getDeployTransaction(initialPriceEther, 8) + const nativeAssetOracleAddress = await create2Factory.deploy(nativeAssetOracleInit, 0, 10_000_000) + const tokenOracleInit = await new TestOracle2__factory(ethersSigner).getDeployTransaction(initialPriceToken, 8) + const tokenOracleAddress = await create2Factory.deploy(tokenOracleInit, 0, 10_000_000) + + const oracleHelperConfig: OracleHelperNamespace.OracleHelperConfigStruct = { + cacheTimeToLive: 100000000, + maxOracleRoundAge: 0, + nativeOracle: nativeAssetOracleAddress, + nativeOracleReverse: false, + priceUpdateThreshold: priceDenominator.mul(2).div(10), // +20% + tokenOracle: tokenOracleAddress, + tokenOracleReverse: false, + tokenToNativeOracle: false + } + + const uniswapHelperConfig: UniswapHelperNamespace.UniswapHelperConfigStruct = { + minSwapAmount: 1, + slippage: 5, + uniswapPoolFee: 3 + } + + const owner = await ethersSigner.getAddress() + + const paymasterInit = hexValue(new TokenPaymaster__factory(ethersSigner).getDeployTransaction( + tokenAddress, + g.entryPoint().address, + wethAddress, + testUniswapAddress, + tokenPaymasterConfig, + oracleHelperConfig, + uniswapHelperConfig, + owner + ).data!) + paymasterAddress = await create2Factory.deploy(paymasterInit, 0) + const paymaster = TokenPaymaster__factory.connect(paymasterAddress, ethersSigner) + await paymaster.addStake(1, { value: 1 }) + await g.entryPoint().depositTo(paymaster.address, { value: parseEther('10') }) + await paymaster.updateCachedPrice(true) + await g.createAccounts1(11) + await token.sudoMint(await ethersSigner.getAddress(), parseEther('20')) + await token.transfer(paymaster.address, parseEther('0.1')) + for (const address of g.createdAccounts) { + await token.transfer(address, parseEther('1')) + await token.sudoApprove(address, paymaster.address, ethers.constants.MaxUint256) + } + + console.log('==addresses:', { + ethersSigner: await ethersSigner.getAddress(), + paymasterAddress, + nativeAssetOracleAddress, + tokenOracleAddress, + tokenAddress, + owner, + createdAccounts: g.createdAccounts + }) + }) + + it('token paymaster', async function () { + await g.addTestRow({ title: 'token paymaster', count: 1, paymaster: paymasterAddress, diffLastGas: false }) + await g.addTestRow({ + title: 'token paymaster with diff', + count: 2, + paymaster: paymasterAddress, + diffLastGas: true + }) + }) + + it('token paymaster 10', async function () { + if (g.skipLong()) this.skip() + + await g.addTestRow({ title: 'token paymaster', count: 10, paymaster: paymasterAddress, diffLastGas: false }) + await g.addTestRow({ + title: 'token paymaster with diff', + count: 11, + paymaster: paymasterAddress, + diffLastGas: true + }) + }) +}) diff --git a/gascalc/GasChecker.ts b/gascalc/GasChecker.ts index 4acdb92f8..91b8f26be 100644 --- a/gascalc/GasChecker.ts +++ b/gascalc/GasChecker.ts @@ -129,7 +129,11 @@ export class GasChecker { defaultAbiCoder.encode(['address'], [this.entryPoint().address]) ]), 0, 2885201) console.log('factaddr', factoryAddress) + GasCheckCollector.inst.setContractName(factoryAddress, 'SimpleAccountFactory') const fact = SimpleAccountFactory__factory.connect(factoryAddress, ethersSigner) + + const implAddress = await fact.accountImplementation() + GasCheckCollector.inst.setContractName(implAddress, 'SimpleAccount') // create accounts const creationOps: UserOperation[] = [] for (const n of range(count)) { @@ -157,6 +161,7 @@ export class GasChecker { this.accounts[addr] = this.accountOwner // deploy if not already deployed. await fact.createAccount(this.accountOwner.address, salt) + GasCheckCollector.inst.setContractName(addr, 'ERC1967Proxy') const accountBalance = await GasCheckCollector.inst.entryPoint.balanceOf(addr) if (accountBalance.lte(minDepositOrBalance)) { await GasCheckCollector.inst.entryPoint.depositTo(addr, { value: minDepositOrBalance.mul(5) }) @@ -207,6 +212,7 @@ export class GasChecker { dest = account } else if (dest === 'random') { dest = createAddress() + GasCheckCollector.inst.setContractName(dest, '!EOA!') const destBalance = await getBalance(dest) if (destBalance.eq(0)) { console.log('dest replenish', dest) @@ -265,8 +271,8 @@ export class GasChecker { count: info.count, gasUsed, accountEst, - title: info.title - // receipt: rcpt + title: info.title, + receipt: rcpt } if (info.diffLastGas) { ret1.gasDiff = gasDiff @@ -294,6 +300,13 @@ export class GasCheckCollector { static initPromise?: Promise entryPoint: EntryPoint + createJsonResult: boolean = false + readonly contracts = new Map() + readonly txHashes: string[] = [] + + setContractName (address: string, name: string): void { + this.contracts.set(address.toLowerCase(), name) + } static async init (): Promise { if (this.inst == null) { @@ -307,6 +320,7 @@ export class GasCheckCollector { async _init (entryPointAddressOrTest: string = 'test'): Promise { console.log('signer=', await ethersSigner.getAddress()) DefaultGasTestInfo.beneficiary = createAddress() + this.setContractName(DefaultGasTestInfo.beneficiary, '!EOA! (beneficiary)') const bal = await getBalance(ethersSigner.getAddress()) if (bal.gt(parseEther('100000000'))) { @@ -321,14 +335,16 @@ export class GasCheckCollector { } else { this.entryPoint = EntryPoint__factory.connect(entryPointAddressOrTest, ethersSigner) } + this.setContractName(this.entryPoint.address, 'EntryPoint') const tableHeaders = [ 'handleOps description ', 'count', 'total gasUsed', - 'per UserOp gas\n(delta for\none UserOp)', + // 'per UserOp gas\n(delta for\none UserOp)', // 'account.exec()\nestimateGas', - 'per UserOp overhead\n(compared to\naccount.exec())' + // 'per UserOp overhead\n(compared to\naccount.exec())', + 'transaction hash' ] this.initTable(tableHeaders) @@ -380,7 +396,19 @@ export class GasCheckCollector { const tableOutput = table(this.tabRows, this.tableConfig) write(tableOutput) - process.exit(0) + if (this.createJsonResult) { + this.writeResultInJson() + } + // process.exit(0) + } + + writeResultInJson (): void { + const res = { + contracts: Object.fromEntries(this.contracts.entries()), + transactions: this.txHashes + } + + fs.writeFileSync(`gas-checker-result-${Date.now()}.json`, JSON.stringify(res)) } addRow (res: GasTestResult): void { @@ -391,9 +419,12 @@ export class GasCheckCollector { res.title, res.count, gasUsed, - res.gasDiff ?? '', + // res.gasDiff ?? '', // res.accountEst, - perOp]) + // perOp, + res.receipt?.transactionHash]) + + this.txHashes.push(res.receipt!.transactionHash) } }