From f6a7434765df6ed2fd999bb82190bee199c55dc1 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Mon, 1 Jan 2024 10:20:43 +0100 Subject: [PATCH] TokenPaymaster "gas-calc" test (#300) * Initial commit for TokenPaymaster "gas-calc" test * Deploy all TokenPaymaster contracts with Create2Factory for "gas-calc" * Make TestPaymaster use postOp * Storage-Optimization for TokenPaymaster - change gas and timestamps to 48 bits - change blanaces and multipliers to 128 bit - saves 10kgas (and since 10-op batch uses the same TokenPaymaster, the * add initial balance to paymaster --- contracts/test/TestPaymasterAcceptAll.sol | 11 +- gascalc/4-token-paymaster.gas.ts | 132 ++++++++++++++++++++++ reports/gas-checker.txt | 24 ++-- 3 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 gascalc/4-token-paymaster.gas.ts diff --git a/contracts/test/TestPaymasterAcceptAll.sol b/contracts/test/TestPaymasterAcceptAll.sol index 7db7dc2c..f97ccd4a 100644 --- a/contracts/test/TestPaymasterAcceptAll.sol +++ b/contracts/test/TestPaymasterAcceptAll.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.12; import "../core/BasePaymaster.sol"; +/* solhint-disable no-empty-blocks */ /** * test paymaster, that pays for everything, without any check. @@ -21,6 +22,14 @@ contract TestPaymasterAcceptAll is BasePaymaster { internal virtual override view returns (bytes memory context, uint256 validationData) { (userOp, userOpHash, maxCost); - return ("", 0); + // return a context, as it is used for EntryPoint gas checking. + return ("1", 0); + } + + function _postOp( + PostOpMode mode, + bytes calldata context, + uint256 actualGasCost + ) internal override { } } diff --git a/gascalc/4-token-paymaster.gas.ts b/gascalc/4-token-paymaster.gas.ts new file mode 100644 index 00000000..1d2dfd96 --- /dev/null +++ b/gascalc/4-token-paymaster.gas.ts @@ -0,0 +1,132 @@ +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('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: 0, + nativeOracle: nativeAssetOracleAddress, + nativeOracleReverse: false, + priceUpdateThreshold: 200_000, // +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/reports/gas-checker.txt b/reports/gas-checker.txt index 25d1dcea..3fac0df2 100644 --- a/reports/gas-checker.txt +++ b/reports/gas-checker.txt @@ -12,28 +12,36 @@ ║ │ │ │ (delta for │ (compared to ║ ║ │ │ │ one UserOp) │ account.exec()) ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple │ 1 │ 81930 │ │ ║ +║ simple │ 1 │ 81918 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ ║ simple - diff from previous │ 2 │ │ 44187 │ 15173 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple │ 10 │ 479742 │ │ ║ +║ simple │ 10 │ 479730 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ ║ simple - diff from previous │ 11 │ │ 44247 │ 15233 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster │ 1 │ 88187 │ │ ║ +║ simple paymaster │ 1 │ 89813 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster with diff │ 2 │ │ 43114 │ 14100 ║ +║ simple paymaster with diff │ 2 │ │ 44796 │ 15782 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster │ 10 │ 476706 │ │ ║ +║ simple paymaster │ 10 │ 493254 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster with diff │ 11 │ │ 43173 │ 14159 ║ +║ simple paymaster with diff │ 11 │ │ 44820 │ 15806 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ big tx 5k │ 1 │ 182987 │ │ ║ +║ big tx 5k │ 1 │ 182975 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ ║ big tx - diff from previous │ 2 │ │ 144698 │ 19438 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ big tx 5k │ 10 │ 1485386 │ │ ║ +║ big tx 5k │ 10 │ 1485374 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ ║ big tx - diff from previous │ 11 │ │ 144759 │ 19499 ║ +╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ +║ token paymaster │ 1 │ 148244 │ │ ║ +╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ +║ token paymaster with diff │ 2 │ │ 72920 │ 43906 ║ +╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ +║ token paymaster │ 10 │ 804795 │ │ ║ +╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ +║ token paymaster with diff │ 11 │ │ 73015 │ 44001 ║ ╚════════════════════════════════╧═══════╧═══════════════╧════════════════╧═════════════════════╝