From 033e568bdb1faa07a2d92ce71ec912c1ae45c197 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Tue, 1 Oct 2024 01:20:26 +0200 Subject: [PATCH 01/16] AA-398: (WIP) Standardize 'preVerificationGas' verification --- packages/bundler/src/MethodHandlerERC4337.ts | 7 +- packages/bundler/src/modules/initServer.ts | 8 +- packages/bundler/src/runBundler.ts | 12 ++- packages/bundler/src/runner/runop.ts | 5 +- packages/bundler/test/BundlerManager.test.ts | 10 +- packages/bundler/test/BundlerServer.test.ts | 7 +- .../bundler/test/DebugMethodHandler.test.ts | 8 +- .../bundler/test/UserOpMethodHandler.test.ts | 11 ++- packages/bundler/test/ValidateManager.test.ts | 12 ++- packages/sdk/src/BaseAccountAPI.ts | 14 +-- .../sdk/src/PreVerificationGasCalculator.ts | 91 +++++++++++++++++++ packages/sdk/src/calcPreVerificationGas.ts | 79 ---------------- packages/sdk/src/index.ts | 2 +- .../sdk/test/4-calcPreVerificationGas.test.ts | 35 ------- .../src/ValidationManager.ts | 15 +-- packages/validation-manager/src/index.ts | 4 +- 16 files changed, 157 insertions(+), 163 deletions(-) create mode 100644 packages/sdk/src/PreVerificationGasCalculator.ts delete mode 100644 packages/sdk/src/calcPreVerificationGas.ts delete mode 100644 packages/sdk/test/4-calcPreVerificationGas.test.ts diff --git a/packages/bundler/src/MethodHandlerERC4337.ts b/packages/bundler/src/MethodHandlerERC4337.ts index 9ec56677..dbe2e642 100644 --- a/packages/bundler/src/MethodHandlerERC4337.ts +++ b/packages/bundler/src/MethodHandlerERC4337.ts @@ -19,7 +19,7 @@ import { } from '@account-abstraction/utils' import { ExecutionManager } from './modules/ExecutionManager' import { StateOverride, UserOperationByHashResponse, UserOperationReceipt } from './RpcTypes' -import { calcPreVerificationGas } from '@account-abstraction/sdk' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' import { EventFragment } from '@ethersproject/abi' export const HEX_REGEX = /^0x[a-fA-F\d]*$/i @@ -58,7 +58,8 @@ export class MethodHandlerERC4337 { readonly provider: JsonRpcProvider, readonly signer: Signer, readonly config: BundlerConfig, - readonly entryPoint: IEntryPoint + readonly entryPoint: IEntryPoint, + readonly preVerificationGasCalculator: PreVerificationGasCalculator ) { } @@ -152,7 +153,7 @@ export class MethodHandlerERC4337 { throw new RpcError(message, ValidationErrors.UserOperationReverted) }) - const preVerificationGas = calcPreVerificationGas(userOp) + const preVerificationGas = this.preVerificationGasCalculator.estimatePreVerificationGas(userOp) const verificationGasLimit = BigNumber.from(preOpGas).toNumber() return { preVerificationGas, diff --git a/packages/bundler/src/modules/initServer.ts b/packages/bundler/src/modules/initServer.ts index 590aba6a..b839198b 100644 --- a/packages/bundler/src/modules/initServer.ts +++ b/packages/bundler/src/modules/initServer.ts @@ -20,6 +20,7 @@ import { BundleManagerRIP7560 } from './BundleManagerRIP7560' import { IBundleManager } from './IBundleManager' import { DepositManager } from './DepositManager' import { IRip7560StakeManager__factory } from '@account-abstraction/utils/dist/src/types' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' /** * initialize server modules. @@ -27,15 +28,16 @@ import { IRip7560StakeManager__factory } from '@account-abstraction/utils/dist/s * @param config * @param signer */ -export function initServer (config: BundlerConfig, signer: Signer): [ExecutionManager, EventsManager, ReputationManager, MempoolManager] { +export function initServer (config: BundlerConfig, signer: Signer): [ExecutionManager, EventsManager, ReputationManager, MempoolManager, PreVerificationGasCalculator] { const entryPoint = IEntryPoint__factory.connect(config.entryPoint, signer) const reputationManager = new ReputationManager(getNetworkProvider(config.network), BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) const mempoolManager = new MempoolManager(reputationManager) const eventsManager = new EventsManager(entryPoint, mempoolManager, reputationManager) + const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) let validationManager: IValidationManager let bundleManager: IBundleManager if (!config.rip7560) { - validationManager = new ValidationManager(entryPoint, config.unsafe) + validationManager = new ValidationManager(entryPoint, config.unsafe, preVerificationGasCalculator) bundleManager = new BundleManager(entryPoint, entryPoint.provider as JsonRpcProvider, signer, eventsManager, mempoolManager, validationManager, reputationManager, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, config.conditionalRpc) } else { @@ -52,5 +54,5 @@ export function initServer (config: BundlerConfig, signer: Signer): [ExecutionMa if (config.rip7560 && config.rip7560Mode === 'PUSH') { executionManager.setAutoBundler(config.autoBundleInterval, config.autoBundleMempoolSize) } - return [executionManager, eventsManager, reputationManager, mempoolManager] + return [executionManager, eventsManager, reputationManager, mempoolManager, preVerificationGasCalculator] } diff --git a/packages/bundler/src/runBundler.ts b/packages/bundler/src/runBundler.ts index 0e0c918b..1a223add 100644 --- a/packages/bundler/src/runBundler.ts +++ b/packages/bundler/src/runBundler.ts @@ -23,6 +23,7 @@ import { MethodHandlerRIP7560 } from './MethodHandlerRIP7560' import { JsonRpcProvider } from '@ethersproject/providers' import { deployNonceManager } from '@account-abstraction/utils/dist/src/RIP7712NonceManagerUtils' import { deployStakeManager } from '@account-abstraction/utils/dist/src/deployStakeManager' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' // this is done so that console.log outputs BigNumber as hex string instead of unreadable object export const inspectCustomSymbol = Symbol.for('nodejs.util.inspect.custom') @@ -159,14 +160,21 @@ export async function runBundler (argv: string[], overrideExit = true): Promise< execManagerConfig.autoBundleInterval = 0 } - const [execManager, eventsManager, reputationManager, mempoolManager] = initServer(execManagerConfig, wallet) + const [ + execManager, + eventsManager, + reputationManager, + mempoolManager, + preVerificationGasCalculator + ] = initServer(execManagerConfig, wallet) const methodHandler = new MethodHandlerERC4337( execManager, provider, wallet, config, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - entryPoint! + entryPoint!, + preVerificationGasCalculator ) const methodHandlerRip7560 = new MethodHandlerRIP7560( execManager, diff --git a/packages/bundler/src/runner/runop.ts b/packages/bundler/src/runner/runop.ts index 505ca4d4..8418e4c4 100644 --- a/packages/bundler/src/runner/runop.ts +++ b/packages/bundler/src/runner/runop.ts @@ -63,10 +63,7 @@ class Runner { entryPointAddress: this.entryPointAddress, factoryAddress: accountDeployer, owner: this.accountOwner, - index: this.index, - overheads: { - // perUserOp: 100000 - } + index: this.index }) return this } diff --git a/packages/bundler/test/BundlerManager.test.ts b/packages/bundler/test/BundlerManager.test.ts index f6336f31..713b23c8 100644 --- a/packages/bundler/test/BundlerManager.test.ts +++ b/packages/bundler/test/BundlerManager.test.ts @@ -22,6 +22,7 @@ import { ExecutionManager } from '../src/modules/ExecutionManager' import { EventsManager } from '../src/modules/EventsManager' import { createSigner } from './testUtils' import { DepositManager } from '../src/modules/DepositManager' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' describe('#BundlerManager', () => { let bm: BundleManager @@ -59,7 +60,8 @@ describe('#BundlerManager', () => { const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) const mempoolMgr = new MempoolManager(repMgr) - const validMgr = new ValidationManager(entryPoint, config.unsafe) + const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const validMgr = new ValidationManager(entryPoint, config.unsafe, preVerificationGasCalculator) const evMgr = new EventsManager(entryPoint, mempoolMgr, repMgr) bm = new BundleManager(entryPoint, entryPoint.provider as JsonRpcProvider, entryPoint.signer, evMgr, mempoolMgr, validMgr, repMgr, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, config.conditionalRpc) }) @@ -112,7 +114,8 @@ describe('#BundlerManager', () => { } const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) const mempoolMgr = new MempoolManager(repMgr) - const validMgr = new ValidationManager(_entryPoint, config.unsafe) + const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const validMgr = new ValidationManager(_entryPoint, config.unsafe, preVerificationGasCalculator) const evMgr = new EventsManager(_entryPoint, mempoolMgr, repMgr) bundleMgr = new BundleManager(_entryPoint, _entryPoint.provider as JsonRpcProvider, _entryPoint.signer, evMgr, mempoolMgr, validMgr, repMgr, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, false) const depositManager = new DepositManager(entryPoint, mempoolMgr, bundleMgr) @@ -124,7 +127,8 @@ describe('#BundlerManager', () => { provider, bundlerSigner, config, - _entryPoint + _entryPoint, + preVerificationGasCalculator ) }) diff --git a/packages/bundler/test/BundlerServer.test.ts b/packages/bundler/test/BundlerServer.test.ts index b7207625..ecaf82d8 100644 --- a/packages/bundler/test/BundlerServer.test.ts +++ b/packages/bundler/test/BundlerServer.test.ts @@ -22,6 +22,7 @@ import { ExecutionManager } from '../src/modules/ExecutionManager' import { MethodHandlerERC4337 } from '../src/MethodHandlerERC4337' import { BundlerConfig } from '../src/BundlerConfig' import { DepositManager } from '../src/modules/DepositManager' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' describe('BundleServer', function () { let entryPoint: IEntryPoint @@ -55,7 +56,8 @@ describe('BundleServer', function () { const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) const mempoolMgr = new MempoolManager(repMgr) - const validMgr = new ValidationManager(entryPoint, config.unsafe) + const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const validMgr = new ValidationManager(entryPoint, config.unsafe, preVerificationGasCalculator) const evMgr = new EventsManager(entryPoint, mempoolMgr, repMgr) const bundleMgr = new BundleManager(entryPoint, entryPoint.provider as JsonRpcProvider, entryPoint.signer, evMgr, mempoolMgr, validMgr, repMgr, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, false) const depositManager = new DepositManager(entryPoint, mempoolMgr, bundleMgr) @@ -65,7 +67,8 @@ describe('BundleServer', function () { provider, signer, config, - entryPoint + entryPoint, + preVerificationGasCalculator ) const None: any = {} server = new BundlerServer(methodHandler, None, None, None, None, None) diff --git a/packages/bundler/test/DebugMethodHandler.test.ts b/packages/bundler/test/DebugMethodHandler.test.ts index af63e624..077b5f6d 100644 --- a/packages/bundler/test/DebugMethodHandler.test.ts +++ b/packages/bundler/test/DebugMethodHandler.test.ts @@ -1,5 +1,5 @@ import { ethers } from 'hardhat' -import { SimpleAccountAPI } from '@account-abstraction/sdk' +import { PreVerificationGasCalculator, SimpleAccountAPI } from '@account-abstraction/sdk' import { Signer, Wallet } from 'ethers' import { parseEther } from 'ethers/lib/utils' import { expect } from 'chai' @@ -66,7 +66,8 @@ describe('#DebugMethodHandler', () => { const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) const mempoolMgr = new MempoolManager(repMgr) - const validMgr = new ValidationManager(entryPoint, config.unsafe) + const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const validMgr = new ValidationManager(entryPoint, config.unsafe, preVerificationGasCalculator) const eventsManager = new EventsManager(entryPoint, mempoolMgr, repMgr) const bundleMgr = new BundleManager(entryPoint, entryPoint.provider as JsonRpcProvider, entryPoint.signer, eventsManager, mempoolMgr, validMgr, repMgr, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, false) @@ -77,7 +78,8 @@ describe('#DebugMethodHandler', () => { provider, signer, config, - entryPoint + entryPoint, + preVerificationGasCalculator ) debugMethodHandler = new DebugMethodHandler(execManager, eventsManager, repMgr, mempoolMgr) diff --git a/packages/bundler/test/UserOpMethodHandler.test.ts b/packages/bundler/test/UserOpMethodHandler.test.ts index 73bfe19c..fc5ccd7b 100644 --- a/packages/bundler/test/UserOpMethodHandler.test.ts +++ b/packages/bundler/test/UserOpMethodHandler.test.ts @@ -6,7 +6,7 @@ import { BundlerConfig } from '../src/BundlerConfig' import { toHex } from 'hardhat/internal/util/bigint' import { Signer, Wallet } from 'ethers' -import { SimpleAccountAPI } from '@account-abstraction/sdk' +import { PreVerificationGasCalculator, SimpleAccountAPI } from '@account-abstraction/sdk' import { postExecutionDump } from '@account-abstraction/utils/dist/src/postExecCheck' import { SampleRecipient, @@ -84,7 +84,8 @@ describe('UserOpMethodHandler', function () { const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) mempoolMgr = new MempoolManager(repMgr) - const validMgr = new ValidationManager(entryPoint, config.unsafe) + const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const validMgr = new ValidationManager(entryPoint, config.unsafe, preVerificationGasCalculator) const evMgr = new EventsManager(entryPoint, mempoolMgr, repMgr) const bundleMgr = new BundleManager(entryPoint, entryPoint.provider as JsonRpcProvider, entryPoint.signer, evMgr, mempoolMgr, validMgr, repMgr, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, false) const depositManager = new DepositManager(entryPoint, mempoolMgr, bundleMgr) @@ -94,7 +95,8 @@ describe('UserOpMethodHandler', function () { provider, signer, config, - entryPoint + entryPoint, + preVerificationGasCalculator ) }) @@ -272,8 +274,7 @@ describe('UserOpMethodHandler', function () { provider, entryPointAddress: entryPoint.address, accountAddress, - owner: accountSigner, - overheads: { perUserOp: 0 } + owner: accountSigner }) const op = await api.createSignedUserOp({ data: sampleRecipient.interface.encodeFunctionData('something', [helloWorld]), diff --git a/packages/bundler/test/ValidateManager.test.ts b/packages/bundler/test/ValidateManager.test.ts index aca75487..e611a73f 100644 --- a/packages/bundler/test/ValidateManager.test.ts +++ b/packages/bundler/test/ValidateManager.test.ts @@ -15,6 +15,7 @@ import { checkRulesViolations, supportsDebugTraceCall } from '@account-abstraction/validation-manager' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' import { TestCoin, @@ -58,12 +59,16 @@ describe('#ValidationManager', () => { let rulesAccount: TestRulesAccount let storageAccount: TestStorageAccount - async function testUserOp (validateRule: string = '', pmRule?: string, initFunc?: string, factoryAddress = opcodeFactory.address): Promise { + async function testUserOp (validateRule: string = '', pmRule?: string, initFunc?: string, factoryAddress = opcodeFactory.address): Promise { const userOp = await createTestUserOp(validateRule, pmRule, initFunc, factoryAddress) return { userOp, ...await vm.validateUserOp(userOp) } } - async function testExistingUserOp (validateRule: string = '', pmRule = ''): Promise { + async function testExistingUserOp (validateRule: string = '', pmRule = ''): Promise { const userOp = await existingStorageAccountUserOp(validateRule, pmRule) return { userOp, ...await vm.validateUserOp(userOp) } } @@ -148,7 +153,8 @@ describe('#ValidationManager', () => { await entryPoint.depositTo(rulesAccount.address, { value: parseEther('1') }) const unsafe = !await supportsDebugTraceCall(provider, false) - vm = new ValidationManager(entryPoint, unsafe) + const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + vm = new ValidationManager(entryPoint, unsafe, preVerificationGasCalculator) if (!await supportsDebugTraceCall(ethers.provider, false)) { console.log('WARNING: opcode banning tests can only run with geth') diff --git a/packages/sdk/src/BaseAccountAPI.ts b/packages/sdk/src/BaseAccountAPI.ts index d990a72e..7788bade 100644 --- a/packages/sdk/src/BaseAccountAPI.ts +++ b/packages/sdk/src/BaseAccountAPI.ts @@ -5,7 +5,6 @@ import { TransactionDetailsForUserOp } from './TransactionDetailsForUserOp' import { defaultAbiCoder } from 'ethers/lib/utils' import { PaymasterAPI } from './PaymasterAPI' import { encodeUserOp, getUserOpHash, IEntryPoint, IEntryPoint__factory, UserOperation } from '@account-abstraction/utils' -import { calcPreVerificationGas, GasOverheads } from './calcPreVerificationGas' export interface FactoryParams { factory: string @@ -16,7 +15,6 @@ export interface BaseApiParams { provider: Provider entryPointAddress: string accountAddress?: string - overheads?: Partial paymasterAPI?: PaymasterAPI } @@ -45,7 +43,6 @@ export abstract class BaseAccountAPI { private readonly entryPointView: IEntryPoint provider: Provider - overheads?: Partial entryPointAddress: string accountAddress?: string paymasterAPI?: PaymasterAPI @@ -56,7 +53,6 @@ export abstract class BaseAccountAPI { */ protected constructor (params: BaseApiParams) { this.provider = params.provider - this.overheads = params.overheads this.entryPointAddress = params.entryPointAddress this.accountAddress = params.accountAddress this.paymasterAPI = params.paymasterAPI @@ -152,14 +148,6 @@ export abstract class BaseAccountAPI { return 100000 } - /** - * should cover cost of putting calldata on-chain, and some overhead. - * actual overhead depends on the expected bundle size - */ - async getPreVerificationGas (userOp: Partial): Promise { - return calcPreVerificationGas(userOp, this.overheads) - } - /** * ABI-encode a user operation. used for calldata cost estimation */ @@ -278,7 +266,7 @@ export abstract class BaseAccountAPI { } return { ...partialUserOp, - preVerificationGas: await this.getPreVerificationGas(partialUserOp), + preVerificationGas: 0, signature: '' } } diff --git a/packages/sdk/src/PreVerificationGasCalculator.ts b/packages/sdk/src/PreVerificationGasCalculator.ts new file mode 100644 index 00000000..e64bce8f --- /dev/null +++ b/packages/sdk/src/PreVerificationGasCalculator.ts @@ -0,0 +1,91 @@ +import { encodeUserOp, packUserOp, UserOperation } from '@account-abstraction/utils' +import { arrayify, hexlify } from 'ethers/lib/utils' +import { BigNumber, BigNumberish } from 'ethers' + +// export const DefaultGasOverheads: GasOverheads = { +// fixed: 21000, +// perUserOp: 18300, +// perUserOpWord: 4, +// zeroByte: 4, +// nonZeroByte: 16, +// bundleSize: 1, +// sigSize: 65 +// } + +export class PreVerificationGasCalculator { + constructor ( + /** + * Gas overhead is added to entire 'handleOp' bundle. + */ + readonly fixedGasOverhead: number, + /** + * Gas overhead per UserOperation is added on top of the above fixed per-bundle. + */ + readonly perUserOpGasOverhead: number, + /** + * Gas overhead per single "word" (32 bytes) of an ABI-encoding of the UserOperation. + */ + readonly perUserOpWordGasOverhead: number, + /** + * The gas cost of a single zero byte an ABI-encoding of the UserOperation. + */ + readonly zeroByteGasCost: number, + /** + * The gas cost of a single zero byte an ABI-encoding of the UserOperation. + */ + readonly nonZeroByteGasCost: number, + /** + * The expected average size of a bundle in current network conditions. + * This value is used to split the bundle gas overhead between all ops. + */ + readonly expectedBundleSize: number, + /** + * The size of the dummy 'signature' parameter to be used during estimation. + */ + readonly estimationSignatureSize: number, + /** + * The size of the dummy 'paymasterData' parameter to be used during estimation. + */ + readonly estimationPaymasterDataSize: number = 0 + ) {} + + validatePreVerificationGas ( + userOp: UserOperation, preVerificationGas: BigNumberish + ): { isPreVerificationGasValid: boolean, minRequiredPreVerificationGas: number } { + return { isPreVerificationGasValid: false, minRequiredPreVerificationGas: 0 } + } + + /** + * Estimate the 'preVerificationGas' necessary for the given UserOperation. + * Value of the 'preVerificationGas' is the cost overhead that cannot be calculated precisely or accessed on-chain. + * It depends on blockchain parameters that are defined by the protocol for all transactions. + * @param userOp - the UserOperation object that may be missing the 'signature' and 'paymasterData' fields. + */ + estimatePreVerificationGas ( + userOp: Partial + ): number { + const filledUserOp = this._fillUserOpWithDummyData(userOp) + const packedUserOp = arrayify(encodeUserOp(packUserOp(filledUserOp), false)) + const userOpWordsLength = (packedUserOp.length + 31) / 32 + const callDataCost = packedUserOp + .map( + x => x === 0 ? this.zeroByteGasCost : this.nonZeroByteGasCost) + .reduce( + (sum, x) => sum + x + ) + const userOpDataWordsOverhead = userOpWordsLength * this.perUserOpWordGasOverhead + + const userOpSpecificOverhead = callDataCost + userOpDataWordsOverhead + this.perUserOpGasOverhead + const userOpShareOfBundleCost = this.fixedGasOverhead / this.expectedBundleSize + + return Math.round(userOpSpecificOverhead + userOpShareOfBundleCost) + } + + _fillUserOpWithDummyData (userOp: Partial): UserOperation { + const filledUserOp: UserOperation = Object.assign({}, userOp) as UserOperation + filledUserOp.preVerificationGas = 21000 // dummy value + filledUserOp.signature = hexlify(Buffer.alloc(this.estimationSignatureSize, 0xff)) + filledUserOp.paymasterData = hexlify(Buffer.alloc(this.estimationPaymasterDataSize, 0xff)) + return filledUserOp + } +} diff --git a/packages/sdk/src/calcPreVerificationGas.ts b/packages/sdk/src/calcPreVerificationGas.ts deleted file mode 100644 index 98c90c3c..00000000 --- a/packages/sdk/src/calcPreVerificationGas.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { encodeUserOp, packUserOp, UserOperation } from '@account-abstraction/utils' -import { arrayify, hexlify } from 'ethers/lib/utils' - -export interface GasOverheads { - /** - * fixed overhead for entire handleOp bundle. - */ - fixed: number - - /** - * per userOp overhead, added on top of the above fixed per-bundle. - */ - perUserOp: number - - /** - * overhead for userOp word (32 bytes) block - */ - perUserOpWord: number - - // perCallDataWord: number - - /** - * zero byte cost, for calldata gas cost calculations - */ - zeroByte: number - - /** - * non-zero byte cost, for calldata gas cost calculations - */ - nonZeroByte: number - - /** - * expected bundle size, to split per-bundle overhead between all ops. - */ - bundleSize: number - - /** - * expected length of the userOp signature. - */ - sigSize: number -} - -export const DefaultGasOverheads: GasOverheads = { - fixed: 21000, - perUserOp: 18300, - perUserOpWord: 4, - zeroByte: 4, - nonZeroByte: 16, - bundleSize: 1, - sigSize: 65 -} - -/** - * calculate the preVerificationGas of the given UserOperation - * preVerificationGas (by definition) is the cost overhead that can't be calculated on-chain. - * it is based on parameters that are defined by the Ethereum protocol for external transactions. - * @param userOp filled userOp to calculate. The only possible missing fields can be the signature and preVerificationGas itself - * @param overheads gas overheads to use, to override the default values - */ -export function calcPreVerificationGas (userOp: Partial, overheads?: Partial): number { - const ov = { ...DefaultGasOverheads, ...(overheads ?? {}) } - const p: UserOperation = { - // dummy values, in case the UserOp is incomplete. - preVerificationGas: 21000, // dummy value, just for calldata cost - signature: hexlify(Buffer.alloc(ov.sigSize, 1)), // dummy signature - ...userOp - } as any - - const packed = arrayify(encodeUserOp(packUserOp(p), false)) - const lengthInWord = (packed.length + 31) / 32 - const callDataCost = packed.map(x => x === 0 ? ov.zeroByte : ov.nonZeroByte).reduce((sum, x) => sum + x) - const ret = Math.round( - callDataCost + - ov.fixed / ov.bundleSize + - ov.perUserOp + - ov.perUserOpWord * lengthInWord - ) - return ret -} diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index f86de66d..b8e1ae1e 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -6,4 +6,4 @@ export { ERC4337EthersSigner } from './ERC4337EthersSigner' export { ERC4337EthersProvider } from './ERC4337EthersProvider' export { ClientConfig } from './ClientConfig' export { HttpRpcClient } from './HttpRpcClient' -export * from './calcPreVerificationGas' +export { PreVerificationGasCalculator } from './PreVerificationGasCalculator' diff --git a/packages/sdk/test/4-calcPreVerificationGas.test.ts b/packages/sdk/test/4-calcPreVerificationGas.test.ts deleted file mode 100644 index 759bd41e..00000000 --- a/packages/sdk/test/4-calcPreVerificationGas.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { expect } from 'chai' -import { hexlify } from 'ethers/lib/utils' -import { calcPreVerificationGas } from '../src/calcPreVerificationGas' - -describe('#calcPreVerificationGas', () => { - const userOp = { - sender: '0x'.padEnd(42, '1'), - nonce: 0, - initCode: '0x3333', - callData: '0x4444', - callGasLimit: 5, - verificationGasLimit: 6, - maxFeePerGas: 8, - maxPriorityFeePerGas: 9, - paymasterAndData: '0xaaaaaa' - } - - it('returns a gas value proportional to sigSize', async () => { - const pvg1 = calcPreVerificationGas(userOp, { sigSize: 0 }) - const pvg2 = calcPreVerificationGas(userOp, { sigSize: 65 }) - - expect(pvg2).to.be.greaterThan(pvg1) - }) - - it('returns a gas value that ignores sigSize if userOp already signed', async () => { - const userOpWithSig = { - ...userOp, - signature: hexlify(Buffer.alloc(65, 1)) - } - - const pvg1 = calcPreVerificationGas(userOpWithSig, { sigSize: 0 }) - const pvg2 = calcPreVerificationGas(userOpWithSig) - expect(pvg2).to.equal(pvg1) - }) -}) diff --git a/packages/validation-manager/src/ValidationManager.ts b/packages/validation-manager/src/ValidationManager.ts index 723030c5..defd8095 100644 --- a/packages/validation-manager/src/ValidationManager.ts +++ b/packages/validation-manager/src/ValidationManager.ts @@ -3,7 +3,7 @@ import { BigNumber } from 'ethers' import { JsonRpcProvider } from '@ethersproject/providers' import Debug from 'debug' -import { calcPreVerificationGas } from '@account-abstraction/sdk' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' import { AddressZero, @@ -47,7 +47,8 @@ const entryPointSimulations = IEntryPointSimulations__factory.createInterface() export class ValidationManager implements IValidationManager { constructor ( readonly entryPoint: IEntryPoint, - readonly unsafe: boolean + readonly unsafe: boolean, + readonly preVerificationGasCalculator: PreVerificationGasCalculator ) { } @@ -292,10 +293,12 @@ export class ValidationManager implements IValidationManager { requireAddressAndFields(userOp, 'paymaster', ['paymasterPostOpGasLimit', 'paymasterVerificationGasLimit'], ['paymasterData']) requireAddressAndFields(userOp, 'factory', ['factoryData']) - if ((userOp as UserOperation).preVerificationGas != null) { - const calcPreVerificationGas1 = calcPreVerificationGas(userOp) - requireCond(BigNumber.from((userOp as UserOperation).preVerificationGas).gte(calcPreVerificationGas1), - `preVerificationGas too low: expected at least ${calcPreVerificationGas1}`, + const preVerificationGas = (userOp as UserOperation).preVerificationGas + if (preVerificationGas != null) { + const { isPreVerificationGasValid, minRequiredPreVerificationGas } = + this.preVerificationGasCalculator.validatePreVerificationGas(userOp as UserOperation, preVerificationGas) + requireCond(isPreVerificationGasValid, + `preVerificationGas too low: expected at least ${minRequiredPreVerificationGas}`, ValidationErrors.InvalidFields) } } diff --git a/packages/validation-manager/src/index.ts b/packages/validation-manager/src/index.ts index 8a8f018a..db703e96 100644 --- a/packages/validation-manager/src/index.ts +++ b/packages/validation-manager/src/index.ts @@ -6,6 +6,7 @@ import { bundlerCollectorTracer } from './BundlerCollectorTracer' import { debug_traceCall, eth_traceRip7560Validation } from './GethTracer' import { ValidateUserOpResult } from './IValidationManager' import { ValidationManager } from './ValidationManager' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' export * from './ValidationManager' export * from './ValidationManagerRIP7560' @@ -68,7 +69,8 @@ export async function checkRulesViolations ( const entryPoint = IEntryPoint__factory.connect(entryPointAddress, provider) const validationManager = new ValidationManager( entryPoint, - false + false, + Object.assign({}) as PreVerificationGasCalculator ) return await validationManager.validateUserOp(userOperation) } From 32b21c2f578eadcf0050e65cae480eb03fec6f5e Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Tue, 1 Oct 2024 13:19:13 +0200 Subject: [PATCH 02/16] Enable configuring PVG calculation overrides --- packages/bundler/src/BundlerConfig.ts | 25 +++- packages/bundler/src/modules/initServer.ts | 5 +- packages/bundler/test/BundlerManager.test.ts | 8 +- packages/bundler/test/BundlerServer.test.ts | 5 +- .../bundler/test/DebugMethodHandler.test.ts | 5 +- .../bundler/test/UserOpMethodHandler.test.ts | 5 +- packages/bundler/test/ValidateManager.test.ts | 24 ++-- .../sdk/src/PreVerificationGasCalculator.ts | 129 +++++++++++------- packages/sdk/src/index.ts | 2 +- .../src/ValidationManager.ts | 5 +- 10 files changed, 132 insertions(+), 81 deletions(-) diff --git a/packages/bundler/src/BundlerConfig.ts b/packages/bundler/src/BundlerConfig.ts index 131632d4..fb93be5c 100644 --- a/packages/bundler/src/BundlerConfig.ts +++ b/packages/bundler/src/BundlerConfig.ts @@ -4,7 +4,9 @@ import ow from 'ow' // RIP-7560 EntyPoint address const MIN_UNSTAKE_DELAY = 86400 const MIN_STAKE_VALUE = 1e18.toString() + export interface BundlerConfig { + chainId: number beneficiary: string entryPoint: string gasFactor: string @@ -27,10 +29,21 @@ export interface BundlerConfig { rip7560: boolean rip7560Mode: string gethDevMode: boolean + + // Config overrides for PreVerificationGas calculation + fixedGasOverhead?: number + perUserOpGasOverhead?: number + perUserOpWordGasOverhead?: number + zeroByteGasCost?: number + nonZeroByteGasCost?: number + expectedBundleSize?: number + estimationSignatureSize?: number + estimationPaymasterDataSize?: number } // TODO: implement merging config (args -> config.js -> default) and runtime shape validation export const BundlerConfigShape = { + chainId: ow.number, beneficiary: ow.string, entryPoint: ow.string, gasFactor: ow.string, @@ -52,7 +65,17 @@ export const BundlerConfigShape = { autoBundleMempoolSize: ow.number, rip7560: ow.boolean, rip7560Mode: ow.string.oneOf(['PULL', 'PUSH']), - gethDevMode: ow.boolean + gethDevMode: ow.boolean, + + // Config overrides for PreVerificationGas calculation + fixedGasOverhead: ow.optional.number, + perUserOpGasOverhead: ow.optional.number, + perUserOpWordGasOverhead: ow.optional.number, + zeroByteGasCost: ow.optional.number, + nonZeroByteGasCost: ow.optional.number, + expectedBundleSize: ow.optional.number, + estimationSignatureSize: ow.optional.number, + estimationPaymasterDataSize: ow.optional.number } // TODO: consider if we want any default fields at all diff --git a/packages/bundler/src/modules/initServer.ts b/packages/bundler/src/modules/initServer.ts index b839198b..d51fec68 100644 --- a/packages/bundler/src/modules/initServer.ts +++ b/packages/bundler/src/modules/initServer.ts @@ -20,7 +20,7 @@ import { BundleManagerRIP7560 } from './BundleManagerRIP7560' import { IBundleManager } from './IBundleManager' import { DepositManager } from './DepositManager' import { IRip7560StakeManager__factory } from '@account-abstraction/utils/dist/src/types' -import { PreVerificationGasCalculator } from '@account-abstraction/sdk' +import { PreVerificationGasCalculator, ChainConfigs } from '@account-abstraction/sdk' /** * initialize server modules. @@ -33,7 +33,8 @@ export function initServer (config: BundlerConfig, signer: Signer): [ExecutionMa const reputationManager = new ReputationManager(getNetworkProvider(config.network), BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) const mempoolManager = new MempoolManager(reputationManager) const eventsManager = new EventsManager(entryPoint, mempoolManager, reputationManager) - const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const mergedPvgcConfig = Object.assign({}, ChainConfigs[config.chainId] ?? {}, config) + const preVerificationGasCalculator = new PreVerificationGasCalculator(mergedPvgcConfig) let validationManager: IValidationManager let bundleManager: IBundleManager if (!config.rip7560) { diff --git a/packages/bundler/test/BundlerManager.test.ts b/packages/bundler/test/BundlerManager.test.ts index 713b23c8..6e64604e 100644 --- a/packages/bundler/test/BundlerManager.test.ts +++ b/packages/bundler/test/BundlerManager.test.ts @@ -11,6 +11,7 @@ import { UserOperation, deployEntryPoint, IEntryPoint, DeterministicDeployer } from '@account-abstraction/utils' +import { PreVerificationGasCalculator, MainnetConfig } from '@account-abstraction/sdk' import { ValidationManager, supportsDebugTraceCall } from '@account-abstraction/validation-manager' import { MempoolManager } from '../src/modules/MempoolManager' @@ -22,7 +23,6 @@ import { ExecutionManager } from '../src/modules/ExecutionManager' import { EventsManager } from '../src/modules/EventsManager' import { createSigner } from './testUtils' import { DepositManager } from '../src/modules/DepositManager' -import { PreVerificationGasCalculator } from '@account-abstraction/sdk' describe('#BundlerManager', () => { let bm: BundleManager @@ -37,6 +37,7 @@ describe('#BundlerManager', () => { DeterministicDeployer.init(provider) const config: BundlerConfig = { + chainId: 1337, beneficiary: await signer.getAddress(), entryPoint: entryPoint.address, gasFactor: '0.2', @@ -60,7 +61,7 @@ describe('#BundlerManager', () => { const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) const mempoolMgr = new MempoolManager(repMgr) - const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const preVerificationGasCalculator = new PreVerificationGasCalculator(MainnetConfig) const validMgr = new ValidationManager(entryPoint, config.unsafe, preVerificationGasCalculator) const evMgr = new EventsManager(entryPoint, mempoolMgr, repMgr) bm = new BundleManager(entryPoint, entryPoint.provider as JsonRpcProvider, entryPoint.signer, evMgr, mempoolMgr, validMgr, repMgr, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, config.conditionalRpc) @@ -92,6 +93,7 @@ describe('#BundlerManager', () => { const bundlerSigner = await createSigner() const _entryPoint = entryPoint.connect(bundlerSigner) const config: BundlerConfig = { + chainId: 1337, beneficiary: await bundlerSigner.getAddress(), entryPoint: _entryPoint.address, gasFactor: '0.2', @@ -114,7 +116,7 @@ describe('#BundlerManager', () => { } const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) const mempoolMgr = new MempoolManager(repMgr) - const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const preVerificationGasCalculator = new PreVerificationGasCalculator(MainnetConfig) const validMgr = new ValidationManager(_entryPoint, config.unsafe, preVerificationGasCalculator) const evMgr = new EventsManager(_entryPoint, mempoolMgr, repMgr) bundleMgr = new BundleManager(_entryPoint, _entryPoint.provider as JsonRpcProvider, _entryPoint.signer, evMgr, mempoolMgr, validMgr, repMgr, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, false) diff --git a/packages/bundler/test/BundlerServer.test.ts b/packages/bundler/test/BundlerServer.test.ts index ecaf82d8..c8cdd4ba 100644 --- a/packages/bundler/test/BundlerServer.test.ts +++ b/packages/bundler/test/BundlerServer.test.ts @@ -11,6 +11,7 @@ import { deployEntryPoint } from '@account-abstraction/utils' import { supportsDebugTraceCall, ValidationManager } from '@account-abstraction/validation-manager' +import { PreVerificationGasCalculator, MainnetConfig } from '@account-abstraction/sdk' import { BundlerServer } from '../src/BundlerServer' import { createSigner } from './testUtils' @@ -22,7 +23,6 @@ import { ExecutionManager } from '../src/modules/ExecutionManager' import { MethodHandlerERC4337 } from '../src/MethodHandlerERC4337' import { BundlerConfig } from '../src/BundlerConfig' import { DepositManager } from '../src/modules/DepositManager' -import { PreVerificationGasCalculator } from '@account-abstraction/sdk' describe('BundleServer', function () { let entryPoint: IEntryPoint @@ -33,6 +33,7 @@ describe('BundleServer', function () { entryPoint = await deployEntryPoint(provider) const config: BundlerConfig = { + chainId: 1337, beneficiary: await signer.getAddress(), entryPoint: entryPoint.address, gasFactor: '0.2', @@ -56,7 +57,7 @@ describe('BundleServer', function () { const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) const mempoolMgr = new MempoolManager(repMgr) - const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const preVerificationGasCalculator = new PreVerificationGasCalculator(MainnetConfig) const validMgr = new ValidationManager(entryPoint, config.unsafe, preVerificationGasCalculator) const evMgr = new EventsManager(entryPoint, mempoolMgr, repMgr) const bundleMgr = new BundleManager(entryPoint, entryPoint.provider as JsonRpcProvider, entryPoint.signer, evMgr, mempoolMgr, validMgr, repMgr, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, false) diff --git a/packages/bundler/test/DebugMethodHandler.test.ts b/packages/bundler/test/DebugMethodHandler.test.ts index 077b5f6d..3626e315 100644 --- a/packages/bundler/test/DebugMethodHandler.test.ts +++ b/packages/bundler/test/DebugMethodHandler.test.ts @@ -1,5 +1,5 @@ import { ethers } from 'hardhat' -import { PreVerificationGasCalculator, SimpleAccountAPI } from '@account-abstraction/sdk' +import { MainnetConfig, PreVerificationGasCalculator, SimpleAccountAPI } from '@account-abstraction/sdk' import { Signer, Wallet } from 'ethers' import { parseEther } from 'ethers/lib/utils' import { expect } from 'chai' @@ -43,6 +43,7 @@ describe('#DebugMethodHandler', () => { DeterministicDeployer.init(provider) const config: BundlerConfig = { + chainId: 1337, beneficiary: await signer.getAddress(), entryPoint: entryPoint.address, gasFactor: '0.2', @@ -66,7 +67,7 @@ describe('#DebugMethodHandler', () => { const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) const mempoolMgr = new MempoolManager(repMgr) - const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const preVerificationGasCalculator = new PreVerificationGasCalculator(MainnetConfig) const validMgr = new ValidationManager(entryPoint, config.unsafe, preVerificationGasCalculator) const eventsManager = new EventsManager(entryPoint, mempoolMgr, repMgr) const bundleMgr = new BundleManager(entryPoint, entryPoint.provider as JsonRpcProvider, entryPoint.signer, eventsManager, mempoolMgr, validMgr, repMgr, diff --git a/packages/bundler/test/UserOpMethodHandler.test.ts b/packages/bundler/test/UserOpMethodHandler.test.ts index fc5ccd7b..a5c233c6 100644 --- a/packages/bundler/test/UserOpMethodHandler.test.ts +++ b/packages/bundler/test/UserOpMethodHandler.test.ts @@ -6,7 +6,7 @@ import { BundlerConfig } from '../src/BundlerConfig' import { toHex } from 'hardhat/internal/util/bigint' import { Signer, Wallet } from 'ethers' -import { PreVerificationGasCalculator, SimpleAccountAPI } from '@account-abstraction/sdk' +import { MainnetConfig, PreVerificationGasCalculator, SimpleAccountAPI } from '@account-abstraction/sdk' import { postExecutionDump } from '@account-abstraction/utils/dist/src/postExecCheck' import { SampleRecipient, @@ -61,6 +61,7 @@ describe('UserOpMethodHandler', function () { sampleRecipient = await sampleRecipientFactory.deploy() const config: BundlerConfig = { + chainId: 1337, beneficiary: await signer.getAddress(), entryPoint: entryPoint.address, gasFactor: '0.2', @@ -84,7 +85,7 @@ describe('UserOpMethodHandler', function () { const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay) mempoolMgr = new MempoolManager(repMgr) - const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const preVerificationGasCalculator = new PreVerificationGasCalculator(MainnetConfig) const validMgr = new ValidationManager(entryPoint, config.unsafe, preVerificationGasCalculator) const evMgr = new EventsManager(entryPoint, mempoolMgr, repMgr) const bundleMgr = new BundleManager(entryPoint, entryPoint.provider as JsonRpcProvider, entryPoint.signer, evMgr, mempoolMgr, validMgr, repMgr, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, false) diff --git a/packages/bundler/test/ValidateManager.test.ts b/packages/bundler/test/ValidateManager.test.ts index e611a73f..05997442 100644 --- a/packages/bundler/test/ValidateManager.test.ts +++ b/packages/bundler/test/ValidateManager.test.ts @@ -15,7 +15,7 @@ import { checkRulesViolations, supportsDebugTraceCall } from '@account-abstraction/validation-manager' -import { PreVerificationGasCalculator } from '@account-abstraction/sdk' +import { PreVerificationGasCalculator, MainnetConfig } from '@account-abstraction/sdk' import { TestCoin, @@ -77,11 +77,11 @@ describe('#ValidationManager', () => { const pmd = pmRule === '' ? {} : { - paymaster: paymaster.address, - paymasterVerificationGasLimit: 1e5, - paymasterPostOpGasLimit: 1e5, - paymasterData: Buffer.from(pmRule) - } + paymaster: paymaster.address, + paymasterVerificationGasLimit: 1e5, + paymasterPostOpGasLimit: 1e5, + paymasterData: Buffer.from(pmRule) + } const signature = hexlify(Buffer.from(validateRule)) return { ...cEmptyUserOp, @@ -103,11 +103,11 @@ describe('#ValidationManager', () => { const pmInfo = pmRule == null ? {} : { - paymaster: paymaster.address, - paymasterVerificationGasLimit: 1e6, - paymasterPostOpGasLimit: 1e6, - paymasterData: Buffer.from(pmRule) - } + paymaster: paymaster.address, + paymasterVerificationGasLimit: 1e6, + paymasterPostOpGasLimit: 1e6, + paymasterData: Buffer.from(pmRule) + } const signature = hexlify(Buffer.from(validateRule)) const callinitCodeForAddr = await provider.call({ to: factoryAddress, @@ -153,7 +153,7 @@ describe('#ValidationManager', () => { await entryPoint.depositTo(rulesAccount.address, { value: parseEther('1') }) const unsafe = !await supportsDebugTraceCall(provider, false) - const preVerificationGasCalculator = new PreVerificationGasCalculator(0, 0, 0, 0, 0, 0, 0, 0) + const preVerificationGasCalculator = new PreVerificationGasCalculator(MainnetConfig) vm = new ValidationManager(entryPoint, unsafe, preVerificationGasCalculator) if (!await supportsDebugTraceCall(ethers.provider, false)) { diff --git a/packages/sdk/src/PreVerificationGasCalculator.ts b/packages/sdk/src/PreVerificationGasCalculator.ts index e64bce8f..0946e6f6 100644 --- a/packages/sdk/src/PreVerificationGasCalculator.ts +++ b/packages/sdk/src/PreVerificationGasCalculator.ts @@ -1,62 +1,81 @@ import { encodeUserOp, packUserOp, UserOperation } from '@account-abstraction/utils' import { arrayify, hexlify } from 'ethers/lib/utils' -import { BigNumber, BigNumberish } from 'ethers' -// export const DefaultGasOverheads: GasOverheads = { -// fixed: 21000, -// perUserOp: 18300, -// perUserOpWord: 4, -// zeroByte: 4, -// nonZeroByte: 16, -// bundleSize: 1, -// sigSize: 65 -// } +export interface PreVerificationGasCalculatorConfig { + /** + * Gas overhead is added to entire 'handleOp' bundle. + */ + readonly fixedGasOverhead: number + /** + * Gas overhead per UserOperation is added on top of the above fixed per-bundle. + */ + readonly perUserOpGasOverhead: number + /** + * Gas overhead per single "word" (32 bytes) of an ABI-encoding of the UserOperation. + */ + readonly perUserOpWordGasOverhead: number + /** + * The gas cost of a single zero byte an ABI-encoding of the UserOperation. + */ + readonly zeroByteGasCost: number + /** + * The gas cost of a single zero byte an ABI-encoding of the UserOperation. + */ + readonly nonZeroByteGasCost: number + /** + * The expected average size of a bundle in current network conditions. + * This value is used to split the bundle gas overhead between all ops. + */ + readonly expectedBundleSize: number + /** + * The size of the dummy 'signature' parameter to be used during estimation. + */ + readonly estimationSignatureSize: number + /** + * The size of the dummy 'paymasterData' parameter to be used during estimation. + */ + readonly estimationPaymasterDataSize: number +} + +export const MainnetConfig: PreVerificationGasCalculatorConfig = { + fixedGasOverhead: 21000, + perUserOpGasOverhead: 18300, + perUserOpWordGasOverhead: 4, + zeroByteGasCost: 4, + nonZeroByteGasCost: 16, + expectedBundleSize: 1, + estimationSignatureSize: 65, + estimationPaymasterDataSize: 0 +} + +export const ChainConfigs: { [key: number]: PreVerificationGasCalculatorConfig } = { + 1: MainnetConfig, + 1337: MainnetConfig +} export class PreVerificationGasCalculator { constructor ( - /** - * Gas overhead is added to entire 'handleOp' bundle. - */ - readonly fixedGasOverhead: number, - /** - * Gas overhead per UserOperation is added on top of the above fixed per-bundle. - */ - readonly perUserOpGasOverhead: number, - /** - * Gas overhead per single "word" (32 bytes) of an ABI-encoding of the UserOperation. - */ - readonly perUserOpWordGasOverhead: number, - /** - * The gas cost of a single zero byte an ABI-encoding of the UserOperation. - */ - readonly zeroByteGasCost: number, - /** - * The gas cost of a single zero byte an ABI-encoding of the UserOperation. - */ - readonly nonZeroByteGasCost: number, - /** - * The expected average size of a bundle in current network conditions. - * This value is used to split the bundle gas overhead between all ops. - */ - readonly expectedBundleSize: number, - /** - * The size of the dummy 'signature' parameter to be used during estimation. - */ - readonly estimationSignatureSize: number, - /** - * The size of the dummy 'paymasterData' parameter to be used during estimation. - */ - readonly estimationPaymasterDataSize: number = 0 + readonly config: PreVerificationGasCalculatorConfig ) {} + /** + * When accepting a UserOperation from a user to a mempool bundler validates the amount of 'preVerificationGas'. + * If the proposed value is lower that the one expected by the bundler the UserOperation may not be profitable. + * Notice that in order to participate in a P2P UserOperations mempool all bundlers must use the same configuration. + * @param userOp - the complete and signed UserOperation received from the user. + */ validatePreVerificationGas ( - userOp: UserOperation, preVerificationGas: BigNumberish + userOp: UserOperation ): { isPreVerificationGasValid: boolean, minRequiredPreVerificationGas: number } { - return { isPreVerificationGasValid: false, minRequiredPreVerificationGas: 0 } + const minRequiredPreVerificationGas = this._calculate(userOp) + return { + minRequiredPreVerificationGas, + isPreVerificationGasValid: minRequiredPreVerificationGas <= userOp.preVerificationGas + } } /** - * Estimate the 'preVerificationGas' necessary for the given UserOperation. + * While filling the partial UserOperation bundler estimate the 'preVerificationGas' necessary for it to be accepted. * Value of the 'preVerificationGas' is the cost overhead that cannot be calculated precisely or accessed on-chain. * It depends on blockchain parameters that are defined by the protocol for all transactions. * @param userOp - the UserOperation object that may be missing the 'signature' and 'paymasterData' fields. @@ -65,18 +84,22 @@ export class PreVerificationGasCalculator { userOp: Partial ): number { const filledUserOp = this._fillUserOpWithDummyData(userOp) - const packedUserOp = arrayify(encodeUserOp(packUserOp(filledUserOp), false)) + return this._calculate(filledUserOp) + } + + _calculate (userOp: UserOperation): number { + const packedUserOp = arrayify(encodeUserOp(packUserOp(userOp), false)) const userOpWordsLength = (packedUserOp.length + 31) / 32 const callDataCost = packedUserOp .map( - x => x === 0 ? this.zeroByteGasCost : this.nonZeroByteGasCost) + x => x === 0 ? this.config.zeroByteGasCost : this.config.nonZeroByteGasCost) .reduce( (sum, x) => sum + x ) - const userOpDataWordsOverhead = userOpWordsLength * this.perUserOpWordGasOverhead + const userOpDataWordsOverhead = userOpWordsLength * this.config.perUserOpWordGasOverhead - const userOpSpecificOverhead = callDataCost + userOpDataWordsOverhead + this.perUserOpGasOverhead - const userOpShareOfBundleCost = this.fixedGasOverhead / this.expectedBundleSize + const userOpSpecificOverhead = callDataCost + userOpDataWordsOverhead + this.config.perUserOpGasOverhead + const userOpShareOfBundleCost = this.config.fixedGasOverhead / this.config.expectedBundleSize return Math.round(userOpSpecificOverhead + userOpShareOfBundleCost) } @@ -84,8 +107,8 @@ export class PreVerificationGasCalculator { _fillUserOpWithDummyData (userOp: Partial): UserOperation { const filledUserOp: UserOperation = Object.assign({}, userOp) as UserOperation filledUserOp.preVerificationGas = 21000 // dummy value - filledUserOp.signature = hexlify(Buffer.alloc(this.estimationSignatureSize, 0xff)) - filledUserOp.paymasterData = hexlify(Buffer.alloc(this.estimationPaymasterDataSize, 0xff)) + filledUserOp.signature = hexlify(Buffer.alloc(this.config.estimationSignatureSize, 0xff)) + filledUserOp.paymasterData = hexlify(Buffer.alloc(this.config.estimationPaymasterDataSize, 0xff)) return filledUserOp } } diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index b8e1ae1e..c0906edb 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -6,4 +6,4 @@ export { ERC4337EthersSigner } from './ERC4337EthersSigner' export { ERC4337EthersProvider } from './ERC4337EthersProvider' export { ClientConfig } from './ClientConfig' export { HttpRpcClient } from './HttpRpcClient' -export { PreVerificationGasCalculator } from './PreVerificationGasCalculator' +export { PreVerificationGasCalculator, ChainConfigs, MainnetConfig } from './PreVerificationGasCalculator' diff --git a/packages/validation-manager/src/ValidationManager.ts b/packages/validation-manager/src/ValidationManager.ts index defd8095..bd531b9c 100644 --- a/packages/validation-manager/src/ValidationManager.ts +++ b/packages/validation-manager/src/ValidationManager.ts @@ -293,10 +293,9 @@ export class ValidationManager implements IValidationManager { requireAddressAndFields(userOp, 'paymaster', ['paymasterPostOpGasLimit', 'paymasterVerificationGasLimit'], ['paymasterData']) requireAddressAndFields(userOp, 'factory', ['factoryData']) - const preVerificationGas = (userOp as UserOperation).preVerificationGas - if (preVerificationGas != null) { + if ((userOp as UserOperation).preVerificationGas != null) { const { isPreVerificationGasValid, minRequiredPreVerificationGas } = - this.preVerificationGasCalculator.validatePreVerificationGas(userOp as UserOperation, preVerificationGas) + this.preVerificationGasCalculator.validatePreVerificationGas(userOp as UserOperation) requireCond(isPreVerificationGasValid, `preVerificationGas too low: expected at least ${minRequiredPreVerificationGas}`, ValidationErrors.InvalidFields) From d62820f61fd6badf263061a9a43cebab686c90d5 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Wed, 2 Oct 2024 16:49:32 +0200 Subject: [PATCH 03/16] Add 'parseInt' --- packages/bundler/localconfig/bundler.config.json | 5 +++-- packages/bundler/localconfig/bundler.rip7560.config.json | 4 ++-- packages/sdk/src/PreVerificationGasCalculator.ts | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/bundler/localconfig/bundler.config.json b/packages/bundler/localconfig/bundler.config.json index f798cd6f..e59bd70d 100644 --- a/packages/bundler/localconfig/bundler.config.json +++ b/packages/bundler/localconfig/bundler.config.json @@ -1,4 +1,5 @@ { + "chainId": 1337, "gasFactor": "1", "port": "3000", "privateApiPort": "3001", @@ -8,8 +9,8 @@ "minBalance": "1", "mnemonic": "./localconfig/mnemonic.txt", "maxBundleGas": 5e6, - "minStake": "1" , - "minUnstakeDelay": 0 , + "minStake": "1", + "minUnstakeDelay": 0, "autoBundleInterval": 3, "autoBundleMempoolSize": 10, "rip7560": false, diff --git a/packages/bundler/localconfig/bundler.rip7560.config.json b/packages/bundler/localconfig/bundler.rip7560.config.json index 8db4570e..fc347601 100644 --- a/packages/bundler/localconfig/bundler.rip7560.config.json +++ b/packages/bundler/localconfig/bundler.rip7560.config.json @@ -8,8 +8,8 @@ "minBalance": "1", "mnemonic": "./localconfig/mnemonic.txt", "maxBundleGas": 30e6, - "minStake": "1" , - "minUnstakeDelay": 0 , + "minStake": "1", + "minUnstakeDelay": 0, "autoBundleInterval": 3, "autoBundleMempoolSize": 10, "rip7560": true, diff --git a/packages/sdk/src/PreVerificationGasCalculator.ts b/packages/sdk/src/PreVerificationGasCalculator.ts index 0946e6f6..4b9fb166 100644 --- a/packages/sdk/src/PreVerificationGasCalculator.ts +++ b/packages/sdk/src/PreVerificationGasCalculator.ts @@ -70,7 +70,7 @@ export class PreVerificationGasCalculator { const minRequiredPreVerificationGas = this._calculate(userOp) return { minRequiredPreVerificationGas, - isPreVerificationGasValid: minRequiredPreVerificationGas <= userOp.preVerificationGas + isPreVerificationGasValid: minRequiredPreVerificationGas <= parseInt(userOp.preVerificationGas.toString()) } } From 6e02112c7eaf022973da9fdc7aec8e24a9b4bb3d Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Wed, 2 Oct 2024 18:51:31 +0200 Subject: [PATCH 04/16] Fix lint --- packages/sdk/src/PreVerificationGasCalculator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/src/PreVerificationGasCalculator.ts b/packages/sdk/src/PreVerificationGasCalculator.ts index 4b9fb166..7bf70bfd 100644 --- a/packages/sdk/src/PreVerificationGasCalculator.ts +++ b/packages/sdk/src/PreVerificationGasCalculator.ts @@ -70,7 +70,7 @@ export class PreVerificationGasCalculator { const minRequiredPreVerificationGas = this._calculate(userOp) return { minRequiredPreVerificationGas, - isPreVerificationGasValid: minRequiredPreVerificationGas <= parseInt(userOp.preVerificationGas.toString()) + isPreVerificationGasValid: minRequiredPreVerificationGas <= parseInt((userOp.preVerificationGas as any).toString()) } } From c4ecdf208b7f51bb715ae128e7662d187a9499ea Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Wed, 2 Oct 2024 18:53:04 +0200 Subject: [PATCH 05/16] Fix lint --- packages/utils/src/DeterministicDeployer.ts | 2 +- packages/utils/src/ERC4337Utils.ts | 1 - packages/utils/src/RIP7560Utils.ts | 2 +- packages/utils/src/Utils.ts | 4 ++-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/utils/src/DeterministicDeployer.ts b/packages/utils/src/DeterministicDeployer.ts index ea879acc..527c41b8 100644 --- a/packages/utils/src/DeterministicDeployer.ts +++ b/packages/utils/src/DeterministicDeployer.ts @@ -67,7 +67,7 @@ export class DeterministicDeployer { to: DeterministicDeployer.deploymentSignerAddress, value: neededBalance, gasLimit: DeterministicDeployer.deploymentGasLimit - }).then(t=>t.wait()) + }).then(async t => await t.wait()) } const tx = await this.provider.send('eth_sendRawTransaction', [DeterministicDeployer.deploymentTransaction]) await this.provider.waitForTransaction(tx) diff --git a/packages/utils/src/ERC4337Utils.ts b/packages/utils/src/ERC4337Utils.ts index b4149e47..13f2815d 100644 --- a/packages/utils/src/ERC4337Utils.ts +++ b/packages/utils/src/ERC4337Utils.ts @@ -30,7 +30,6 @@ export type NotPromise = { [P in keyof T]: Exclude> } - // todo: remove this wrapper method? export function packAccountGasLimits (validationGasLimit: BigNumberish, callGasLimit: BigNumberish): string { return packUint(validationGasLimit, callGasLimit) diff --git a/packages/utils/src/RIP7560Utils.ts b/packages/utils/src/RIP7560Utils.ts index 4f2201a3..60b391bf 100644 --- a/packages/utils/src/RIP7560Utils.ts +++ b/packages/utils/src/RIP7560Utils.ts @@ -15,7 +15,7 @@ export function getRIP7560TransactionHash (op: OperationRIP7560, forSignature = return keccak256(rlpEncoded) } -function nonZeroAddr(addr?: string): Buffer { +function nonZeroAddr (addr?: string): Buffer { if (addr == null || addr === AddressZero) { return Buffer.from([]) } diff --git a/packages/utils/src/Utils.ts b/packages/utils/src/Utils.ts index b3e3544a..b5cd13d5 100644 --- a/packages/utils/src/Utils.ts +++ b/packages/utils/src/Utils.ts @@ -1,7 +1,7 @@ // misc utilities for the various modules. import { BytesLike, ContractFactory, BigNumber, ethers } from 'ethers' -import { defaultAbiCoder, hexlify, hexZeroPad, Result } from 'ethers/lib/utils' +import { hexlify, hexZeroPad, Result } from 'ethers/lib/utils' import { Provider, JsonRpcProvider } from '@ethersproject/providers' import { BigNumberish } from 'ethers/lib/ethers' @@ -226,7 +226,7 @@ export function getPackedNonce (userOp: OperationBase): BigNumber { // Either not RIP-7560 operation or not using RIP-7712 nonce return BigNumber.from(userOp.nonce) } - const packed = ethers.utils.solidityPack(["uint192", "uint64"], [nonceKey, userOp.nonce]) + const packed = ethers.utils.solidityPack(['uint192', 'uint64'], [nonceKey, userOp.nonce]) const bigNumberNonce = BigNumber.from(packed) return bigNumberNonce } From 60c8c5e9b577e7cf7db4d5932f95091fe9fe55ac Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Wed, 2 Oct 2024 20:32:27 +0200 Subject: [PATCH 06/16] Fix lint --- packages/bundler/src/runBundler.ts | 1 - packages/bundler/test/ValidateManager.test.ts | 20 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/bundler/src/runBundler.ts b/packages/bundler/src/runBundler.ts index 1a223add..d12494f2 100644 --- a/packages/bundler/src/runBundler.ts +++ b/packages/bundler/src/runBundler.ts @@ -23,7 +23,6 @@ import { MethodHandlerRIP7560 } from './MethodHandlerRIP7560' import { JsonRpcProvider } from '@ethersproject/providers' import { deployNonceManager } from '@account-abstraction/utils/dist/src/RIP7712NonceManagerUtils' import { deployStakeManager } from '@account-abstraction/utils/dist/src/deployStakeManager' -import { PreVerificationGasCalculator } from '@account-abstraction/sdk' // this is done so that console.log outputs BigNumber as hex string instead of unreadable object export const inspectCustomSymbol = Symbol.for('nodejs.util.inspect.custom') diff --git a/packages/bundler/test/ValidateManager.test.ts b/packages/bundler/test/ValidateManager.test.ts index 05997442..129869bf 100644 --- a/packages/bundler/test/ValidateManager.test.ts +++ b/packages/bundler/test/ValidateManager.test.ts @@ -77,11 +77,11 @@ describe('#ValidationManager', () => { const pmd = pmRule === '' ? {} : { - paymaster: paymaster.address, - paymasterVerificationGasLimit: 1e5, - paymasterPostOpGasLimit: 1e5, - paymasterData: Buffer.from(pmRule) - } + paymaster: paymaster.address, + paymasterVerificationGasLimit: 1e5, + paymasterPostOpGasLimit: 1e5, + paymasterData: Buffer.from(pmRule) + } const signature = hexlify(Buffer.from(validateRule)) return { ...cEmptyUserOp, @@ -103,11 +103,11 @@ describe('#ValidationManager', () => { const pmInfo = pmRule == null ? {} : { - paymaster: paymaster.address, - paymasterVerificationGasLimit: 1e6, - paymasterPostOpGasLimit: 1e6, - paymasterData: Buffer.from(pmRule) - } + paymaster: paymaster.address, + paymasterVerificationGasLimit: 1e6, + paymasterPostOpGasLimit: 1e6, + paymasterData: Buffer.from(pmRule) + } const signature = hexlify(Buffer.from(validateRule)) const callinitCodeForAddr = await provider.call({ to: factoryAddress, From 976552dda47a74f5c81dfa042655093cd91edded Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Wed, 2 Oct 2024 20:46:07 +0200 Subject: [PATCH 07/16] Fix tests --- packages/sdk/src/BaseAccountAPI.ts | 2 +- packages/validation-manager/src/ValidationManager.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/sdk/src/BaseAccountAPI.ts b/packages/sdk/src/BaseAccountAPI.ts index 7788bade..de5e2c10 100644 --- a/packages/sdk/src/BaseAccountAPI.ts +++ b/packages/sdk/src/BaseAccountAPI.ts @@ -266,7 +266,7 @@ export abstract class BaseAccountAPI { } return { ...partialUserOp, - preVerificationGas: 0, + preVerificationGas: 60000, signature: '' } } diff --git a/packages/validation-manager/src/ValidationManager.ts b/packages/validation-manager/src/ValidationManager.ts index bd531b9c..a628512b 100644 --- a/packages/validation-manager/src/ValidationManager.ts +++ b/packages/validation-manager/src/ValidationManager.ts @@ -293,11 +293,12 @@ export class ValidationManager implements IValidationManager { requireAddressAndFields(userOp, 'paymaster', ['paymasterPostOpGasLimit', 'paymasterVerificationGasLimit'], ['paymasterData']) requireAddressAndFields(userOp, 'factory', ['factoryData']) - if ((userOp as UserOperation).preVerificationGas != null) { + const preVerificationGas = (userOp as UserOperation).preVerificationGas + if (preVerificationGas != null) { const { isPreVerificationGasValid, minRequiredPreVerificationGas } = this.preVerificationGasCalculator.validatePreVerificationGas(userOp as UserOperation) requireCond(isPreVerificationGasValid, - `preVerificationGas too low: expected at least ${minRequiredPreVerificationGas}`, + `preVerificationGas too low: expected at least ${minRequiredPreVerificationGas}, provided only ${preVerificationGas}`, ValidationErrors.InvalidFields) } } From a7d1b7a655dc3d50b919eb7c71a360765fa9cae6 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Wed, 2 Oct 2024 20:53:40 +0200 Subject: [PATCH 08/16] Fix lint and tests once more --- packages/bundler/test/UserOpMethodHandler.test.ts | 1 + packages/validation-manager/src/ValidationManager.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bundler/test/UserOpMethodHandler.test.ts b/packages/bundler/test/UserOpMethodHandler.test.ts index a5c233c6..7c15814d 100644 --- a/packages/bundler/test/UserOpMethodHandler.test.ts +++ b/packages/bundler/test/UserOpMethodHandler.test.ts @@ -281,6 +281,7 @@ describe('UserOpMethodHandler', function () { data: sampleRecipient.interface.encodeFunctionData('something', [helloWorld]), target: sampleRecipient.address }) + op.preVerificationGas = 1000 try { await methodHandler.sendUserOperation(await resolveHexlify(op), entryPoint.address) throw new Error('expected to revert') diff --git a/packages/validation-manager/src/ValidationManager.ts b/packages/validation-manager/src/ValidationManager.ts index a628512b..b726fc1e 100644 --- a/packages/validation-manager/src/ValidationManager.ts +++ b/packages/validation-manager/src/ValidationManager.ts @@ -298,7 +298,7 @@ export class ValidationManager implements IValidationManager { const { isPreVerificationGasValid, minRequiredPreVerificationGas } = this.preVerificationGasCalculator.validatePreVerificationGas(userOp as UserOperation) requireCond(isPreVerificationGasValid, - `preVerificationGas too low: expected at least ${minRequiredPreVerificationGas}, provided only ${preVerificationGas}`, + `preVerificationGas too low: expected at least ${minRequiredPreVerificationGas}, provided only ${(preVerificationGas as string).toString()}`, ValidationErrors.InvalidFields) } } From 7e72b068f24d430e6f241bd0a99664238d6290b8 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Tue, 15 Oct 2024 11:56:10 +0200 Subject: [PATCH 09/16] Implement 'debug_bundler_setConfiguration' --- packages/bundler/src/BundlerServer.ts | 6 ++++ packages/bundler/src/DebugMethodHandler.ts | 30 +++++++++++++++++-- packages/bundler/src/MethodHandlerERC4337.ts | 2 +- .../bundler/src/modules/ExecutionManager.ts | 17 +++++++++-- .../sdk/src/PreVerificationGasCalculator.ts | 4 ++- packages/sdk/src/index.ts | 4 ++- .../src/IValidationManager.ts | 9 +++++- .../src/ValidationManager.ts | 16 ++++++++-- .../src/ValidationManagerRIP7560.ts | 23 ++++++++++---- 9 files changed, 95 insertions(+), 16 deletions(-) diff --git a/packages/bundler/src/BundlerServer.ts b/packages/bundler/src/BundlerServer.ts index d98d36f2..87aece58 100644 --- a/packages/bundler/src/BundlerServer.ts +++ b/packages/bundler/src/BundlerServer.ts @@ -330,6 +330,12 @@ export class BundlerServer { case 'debug_bundler_getStakeStatus': result = await this.debugHandler.getStakeStatus(params[0], params[1]) break + case 'debug_bundler_setConfiguration': { + const pvgc = await this.debugHandler._setConfiguration(params[0]) + this.methodHandler.preVerificationGasCalculator = pvgc + } + result = {} + break default: throw new RpcError(`Method ${method} is not supported`, -32601) } diff --git a/packages/bundler/src/DebugMethodHandler.ts b/packages/bundler/src/DebugMethodHandler.ts index 8a0b1abf..72f20fd2 100644 --- a/packages/bundler/src/DebugMethodHandler.ts +++ b/packages/bundler/src/DebugMethodHandler.ts @@ -1,9 +1,28 @@ +import ow from 'ow' + +import { StakeInfo } from '@account-abstraction/utils' + +import { BundlerConfig } from './BundlerConfig' +import { EventsManager } from './modules/EventsManager' import { ExecutionManager } from './modules/ExecutionManager' -import { ReputationDump, ReputationManager } from './modules/ReputationManager' import { MempoolManager } from './modules/MempoolManager' +import { ReputationDump, ReputationManager } from './modules/ReputationManager' import { SendBundleReturn } from './modules/BundleManager' -import { EventsManager } from './modules/EventsManager' -import { StakeInfo } from '@account-abstraction/utils' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' + +/** + * Only parameters in this object can be provided by a 'debug_bundler_setConfiguration' API. + */ +export const BundlerConfigShape = { + fixedGasOverhead: ow.optional.number, + perUserOpGasOverhead: ow.optional.number, + perUserOpWordGasOverhead: ow.optional.number, + zeroByteGasCost: ow.optional.number, + nonZeroByteGasCost: ow.optional.number, + expectedBundleSize: ow.optional.number, + estimationSignatureSize: ow.optional.number, + estimationPaymasterDataSize: ow.optional.number +} export class DebugMethodHandler { constructor ( @@ -76,4 +95,9 @@ export class DebugMethodHandler { }> { return await this.repManager.getStakeStatus(address, entryPoint) } + + async _setConfiguration (config: Partial): Promise { + ow.object.exactShape(BundlerConfigShape) + return await this.execManager._setConfiguration(config) + } } diff --git a/packages/bundler/src/MethodHandlerERC4337.ts b/packages/bundler/src/MethodHandlerERC4337.ts index dbe2e642..7cd79893 100644 --- a/packages/bundler/src/MethodHandlerERC4337.ts +++ b/packages/bundler/src/MethodHandlerERC4337.ts @@ -59,7 +59,7 @@ export class MethodHandlerERC4337 { readonly signer: Signer, readonly config: BundlerConfig, readonly entryPoint: IEntryPoint, - readonly preVerificationGasCalculator: PreVerificationGasCalculator + public preVerificationGasCalculator: PreVerificationGasCalculator ) { } diff --git a/packages/bundler/src/modules/ExecutionManager.ts b/packages/bundler/src/modules/ExecutionManager.ts index b0168074..5f28e1a0 100644 --- a/packages/bundler/src/modules/ExecutionManager.ts +++ b/packages/bundler/src/modules/ExecutionManager.ts @@ -9,10 +9,12 @@ import { ReputationManager } from './ReputationManager' import { IBundleManager } from './IBundleManager' import { EmptyValidateUserOpResult, - IValidationManager + IValidationManager, ValidationManager } from '@account-abstraction/validation-manager' import { DepositManager } from './DepositManager' import { BigNumberish, Signer } from 'ethers' +import { BundlerConfig } from '../BundlerConfig' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' const debug = Debug('aa.exec') @@ -30,7 +32,7 @@ export class ExecutionManager { constructor (private readonly reputationManager: ReputationManager, private readonly mempoolManager: MempoolManager, private readonly bundleManager: IBundleManager, - private readonly validationManager: IValidationManager, + private validationManager: IValidationManager, private readonly depositManager: DepositManager, private readonly signer: Signer, private readonly rip7560: boolean, @@ -143,4 +145,15 @@ export class ExecutionManager { ): Promise<[OperationBase[], StorageMap]> { return await this.bundleManager.createBundle(minBaseFee, maxBundleGas, maxBundleSize) } + + async _setConfiguration (configOverrides: Partial): Promise { + const { configuration, entryPoint, unsafe } = this.validationManager._getDebugConfiguration() + const pvgc = new PreVerificationGasCalculator(Object.assign({}, configuration, configOverrides)) + this.validationManager = new ValidationManager( + entryPoint, + unsafe, + pvgc + ) + return pvgc + } } diff --git a/packages/sdk/src/PreVerificationGasCalculator.ts b/packages/sdk/src/PreVerificationGasCalculator.ts index 7bf70bfd..63e4c3f5 100644 --- a/packages/sdk/src/PreVerificationGasCalculator.ts +++ b/packages/sdk/src/PreVerificationGasCalculator.ts @@ -101,7 +101,9 @@ export class PreVerificationGasCalculator { const userOpSpecificOverhead = callDataCost + userOpDataWordsOverhead + this.config.perUserOpGasOverhead const userOpShareOfBundleCost = this.config.fixedGasOverhead / this.config.expectedBundleSize - return Math.round(userOpSpecificOverhead + userOpShareOfBundleCost) + const preVerificationGas = Math.round(userOpSpecificOverhead + userOpShareOfBundleCost) + console.log(`calculate preVerificationGas: ${preVerificationGas} callDataCost: ${callDataCost}`) + return preVerificationGas } _fillUserOpWithDummyData (userOp: Partial): UserOperation { diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index c0906edb..8a7572bf 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -6,4 +6,6 @@ export { ERC4337EthersSigner } from './ERC4337EthersSigner' export { ERC4337EthersProvider } from './ERC4337EthersProvider' export { ClientConfig } from './ClientConfig' export { HttpRpcClient } from './HttpRpcClient' -export { PreVerificationGasCalculator, ChainConfigs, MainnetConfig } from './PreVerificationGasCalculator' +export { + PreVerificationGasCalculator, PreVerificationGasCalculatorConfig, ChainConfigs, MainnetConfig +} from './PreVerificationGasCalculator' diff --git a/packages/validation-manager/src/IValidationManager.ts b/packages/validation-manager/src/IValidationManager.ts index fae75d72..0f537609 100644 --- a/packages/validation-manager/src/IValidationManager.ts +++ b/packages/validation-manager/src/IValidationManager.ts @@ -1,6 +1,8 @@ -import { OperationBase, ReferencedCodeHashes, StakeInfo, StorageMap } from '@account-abstraction/utils' import { BigNumber, BigNumberish } from 'ethers' +import { IEntryPoint, OperationBase, ReferencedCodeHashes, StakeInfo, StorageMap } from '@account-abstraction/utils' +import { PreVerificationGasCalculatorConfig } from '@account-abstraction/sdk' + /** * result from successful validation */ @@ -52,4 +54,9 @@ export interface IValidationManager { getOperationHash: (userOp: OperationBase) => Promise + _getDebugConfiguration: () => { + configuration: PreVerificationGasCalculatorConfig + entryPoint: IEntryPoint + unsafe: boolean + } } diff --git a/packages/validation-manager/src/ValidationManager.ts b/packages/validation-manager/src/ValidationManager.ts index b726fc1e..a4bcc142 100644 --- a/packages/validation-manager/src/ValidationManager.ts +++ b/packages/validation-manager/src/ValidationManager.ts @@ -3,7 +3,7 @@ import { BigNumber } from 'ethers' import { JsonRpcProvider } from '@ethersproject/providers' import Debug from 'debug' -import { PreVerificationGasCalculator } from '@account-abstraction/sdk' +import { PreVerificationGasCalculator, PreVerificationGasCalculatorConfig } from '@account-abstraction/sdk' import { AddressZero, @@ -52,6 +52,18 @@ export class ValidationManager implements IValidationManager { ) { } + _getDebugConfiguration (): { + configuration: PreVerificationGasCalculatorConfig + entryPoint: IEntryPoint + unsafe: boolean + } { + return { + configuration: this.preVerificationGasCalculator.config, + entryPoint: this.entryPoint, + unsafe: this.unsafe + } + } + parseValidationResult (userOp: UserOperation, res: ValidationResultStructOutput): ValidationResult { const mergedValidation = mergeValidationDataValues(res.returnInfo.accountValidationData, res.returnInfo.paymasterValidationData) @@ -298,7 +310,7 @@ export class ValidationManager implements IValidationManager { const { isPreVerificationGasValid, minRequiredPreVerificationGas } = this.preVerificationGasCalculator.validatePreVerificationGas(userOp as UserOperation) requireCond(isPreVerificationGasValid, - `preVerificationGas too low: expected at least ${minRequiredPreVerificationGas}, provided only ${(preVerificationGas as string).toString()}`, + `preVerificationGas too low: expected at least ${minRequiredPreVerificationGas}, provided only ${parseInt(preVerificationGas as string)}`, ValidationErrors.InvalidFields) } } diff --git a/packages/validation-manager/src/ValidationManagerRIP7560.ts b/packages/validation-manager/src/ValidationManagerRIP7560.ts index 21c05395..5121555a 100644 --- a/packages/validation-manager/src/ValidationManagerRIP7560.ts +++ b/packages/validation-manager/src/ValidationManagerRIP7560.ts @@ -1,18 +1,19 @@ import { JsonRpcProvider } from '@ethersproject/providers' +import debug from 'debug' +import { isAddress } from 'ethers/lib/utils' import { OperationBase, OperationRIP7560, ReferencedCodeHashes, - getRIP7560TransactionHash, StakeInfo + getRIP7560TransactionHash, StakeInfo, IEntryPoint } from '@account-abstraction/utils' +import { IRip7560StakeManager } from '@account-abstraction/utils/dist/src/types' +import { PreVerificationGasCalculatorConfig } from '@account-abstraction/sdk' import { IValidationManager, ValidateUserOpResult, ValidationResult } from './IValidationManager' import { eth_traceRip7560Validation } from './GethTracer' import { tracerResultParser } from './TracerResultParser' -import debug from 'debug' -import { isAddress } from 'ethers/lib/utils' -import { IRip7560StakeManager } from '@account-abstraction/utils/dist/src/types' export const AA_ENTRY_POINT = '0x0000000000000000000000000000000000007560' export const AA_STAKE_MANAGER = '0x570Aa568b6cf62ff08c6C3a3b3DB1a0438E871Fb' @@ -25,11 +26,23 @@ export class ValidationManagerRIP7560 implements IValidationManager { ) { } + _getDebugConfiguration (): { + configuration: PreVerificationGasCalculatorConfig + entryPoint: IEntryPoint + unsafe: boolean + } { + throw new Error('Method not implemented.') + } + validateInputParameters (_operation: OperationBase, _entryPointInput?: string): void { // TODO } - async _getStakesInfo (operation: OperationBase): Promise<{ senderInfo: StakeInfo, paymasterInfo?: StakeInfo, factoryInfo?: StakeInfo }> { + async _getStakesInfo (operation: OperationBase): Promise<{ + senderInfo: StakeInfo + paymasterInfo?: StakeInfo + factoryInfo?: StakeInfo + }> { const addresses = [operation.sender] let paymasterInfo, factoryInfo if (operation.paymaster != null && isAddress(operation.paymaster)) { From 67349f84290e7c78dbca5618da48abb3042d3170 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Tue, 15 Oct 2024 14:49:39 +0200 Subject: [PATCH 10/16] Do not override the supplied fields with dummy data --- packages/sdk/src/PreVerificationGasCalculator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/sdk/src/PreVerificationGasCalculator.ts b/packages/sdk/src/PreVerificationGasCalculator.ts index 63e4c3f5..94a0fed0 100644 --- a/packages/sdk/src/PreVerificationGasCalculator.ts +++ b/packages/sdk/src/PreVerificationGasCalculator.ts @@ -108,9 +108,9 @@ export class PreVerificationGasCalculator { _fillUserOpWithDummyData (userOp: Partial): UserOperation { const filledUserOp: UserOperation = Object.assign({}, userOp) as UserOperation - filledUserOp.preVerificationGas = 21000 // dummy value - filledUserOp.signature = hexlify(Buffer.alloc(this.config.estimationSignatureSize, 0xff)) - filledUserOp.paymasterData = hexlify(Buffer.alloc(this.config.estimationPaymasterDataSize, 0xff)) + filledUserOp.preVerificationGas = filledUserOp.preVerificationGas ?? 21000 + filledUserOp.signature = filledUserOp.signature ?? hexlify(Buffer.alloc(this.config.estimationSignatureSize, 0xff)) + filledUserOp.paymasterData = filledUserOp.paymasterData ?? hexlify(Buffer.alloc(this.config.estimationPaymasterDataSize, 0xff)) return filledUserOp } } From 4b4f1e9da08dc671ef7f20fd1b420afe3875cd5f Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Tue, 15 Oct 2024 15:25:13 +0200 Subject: [PATCH 11/16] Separate 'fixedGasOverhead' and 'transactionGasStipend' chain config params --- packages/bundler/src/BundlerServer.ts | 3 ++- packages/bundler/src/MethodHandlerERC4337.ts | 6 ++++-- packages/sdk/src/PreVerificationGasCalculator.ts | 9 +++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/bundler/src/BundlerServer.ts b/packages/bundler/src/BundlerServer.ts index 87aece58..d9ca8aed 100644 --- a/packages/bundler/src/BundlerServer.ts +++ b/packages/bundler/src/BundlerServer.ts @@ -198,7 +198,8 @@ export class BundlerServer { } = reqItem debug('>>', { jsonrpc, id, method, params }) try { - const result = deepHexlify(await this.handleMethod(method, params)) + const handleResult = await this.handleMethod(method, params) + const result = deepHexlify(handleResult) debug('sent', method, '-', result) debug('<<', { jsonrpc, id, result }) return { diff --git a/packages/bundler/src/MethodHandlerERC4337.ts b/packages/bundler/src/MethodHandlerERC4337.ts index 7cd79893..6fc28e85 100644 --- a/packages/bundler/src/MethodHandlerERC4337.ts +++ b/packages/bundler/src/MethodHandlerERC4337.ts @@ -19,7 +19,7 @@ import { } from '@account-abstraction/utils' import { ExecutionManager } from './modules/ExecutionManager' import { StateOverride, UserOperationByHashResponse, UserOperationReceipt } from './RpcTypes' -import { PreVerificationGasCalculator } from '@account-abstraction/sdk' +import { MainnetConfig, PreVerificationGasCalculator } from '@account-abstraction/sdk' import { EventFragment } from '@ethersproject/abi' export const HEX_REGEX = /^0x[a-fA-F\d]*$/i @@ -144,7 +144,7 @@ export class MethodHandlerERC4337 { } = returnInfo // todo: use simulateHandleOp for this too... - const callGasLimit = await this.provider.estimateGas({ + let callGasLimit = await this.provider.estimateGas({ from: this.entryPoint.address, to: userOp.sender, data: userOp.callData @@ -152,6 +152,8 @@ export class MethodHandlerERC4337 { const message = err.message.match(/reason="(.*?)"/)?.at(1) ?? 'execution reverted' throw new RpcError(message, ValidationErrors.UserOperationReverted) }) + // Results from 'estimateGas' assume making a standalone transaction and paying 21'000 gas extra for it + callGasLimit -= MainnetConfig.transactionGasStipend const preVerificationGas = this.preVerificationGasCalculator.estimatePreVerificationGas(userOp) const verificationGasLimit = BigNumber.from(preOpGas).toNumber() diff --git a/packages/sdk/src/PreVerificationGasCalculator.ts b/packages/sdk/src/PreVerificationGasCalculator.ts index 94a0fed0..d24f8c44 100644 --- a/packages/sdk/src/PreVerificationGasCalculator.ts +++ b/packages/sdk/src/PreVerificationGasCalculator.ts @@ -2,6 +2,10 @@ import { encodeUserOp, packUserOp, UserOperation } from '@account-abstraction/ut import { arrayify, hexlify } from 'ethers/lib/utils' export interface PreVerificationGasCalculatorConfig { + /** + * Cost of sending a basic transaction on the current chain. + */ + readonly transactionGasStipend: number /** * Gas overhead is added to entire 'handleOp' bundle. */ @@ -38,8 +42,9 @@ export interface PreVerificationGasCalculatorConfig { } export const MainnetConfig: PreVerificationGasCalculatorConfig = { - fixedGasOverhead: 21000, - perUserOpGasOverhead: 18300, + transactionGasStipend: 21000, + fixedGasOverhead: 38000, + perUserOpGasOverhead: 11000, perUserOpWordGasOverhead: 4, zeroByteGasCost: 4, nonZeroByteGasCost: 16, From 057a32ab309a5169aac75ee04e6ba6cea99dcb00 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Tue, 15 Oct 2024 15:26:16 +0200 Subject: [PATCH 12/16] Inline variable --- packages/sdk/src/PreVerificationGasCalculator.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/sdk/src/PreVerificationGasCalculator.ts b/packages/sdk/src/PreVerificationGasCalculator.ts index d24f8c44..9a6d87c9 100644 --- a/packages/sdk/src/PreVerificationGasCalculator.ts +++ b/packages/sdk/src/PreVerificationGasCalculator.ts @@ -106,9 +106,7 @@ export class PreVerificationGasCalculator { const userOpSpecificOverhead = callDataCost + userOpDataWordsOverhead + this.config.perUserOpGasOverhead const userOpShareOfBundleCost = this.config.fixedGasOverhead / this.config.expectedBundleSize - const preVerificationGas = Math.round(userOpSpecificOverhead + userOpShareOfBundleCost) - console.log(`calculate preVerificationGas: ${preVerificationGas} callDataCost: ${callDataCost}`) - return preVerificationGas + return Math.round(userOpSpecificOverhead + userOpShareOfBundleCost) } _fillUserOpWithDummyData (userOp: Partial): UserOperation { From b2f1d078eb917867e1f658cb390529864a0c3f84 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Tue, 15 Oct 2024 16:09:04 +0200 Subject: [PATCH 13/16] Remove deprecated 'GetUserOpHashes' and 'BundlerHelper' solidity code --- packages/bundler/contracts/BundlerHelper.sol | 20 ----------- packages/bundler/src/MethodHandlerERC4337.ts | 34 ++++++++++++------- packages/bundler/src/modules/BundleManager.ts | 10 ++---- 3 files changed, 24 insertions(+), 40 deletions(-) delete mode 100644 packages/bundler/contracts/BundlerHelper.sol diff --git a/packages/bundler/contracts/BundlerHelper.sol b/packages/bundler/contracts/BundlerHelper.sol deleted file mode 100644 index 13b281ab..00000000 --- a/packages/bundler/contracts/BundlerHelper.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.15; - -import "@account-abstraction/contracts/interfaces/IEntryPoint.sol"; - -contract GetUserOpHashes { - error UserOpHashesResult(bytes32[] userOpHashes); - - constructor(IEntryPoint entryPoint, PackedUserOperation[] memory userOps) { - revert UserOpHashesResult( - getUserOpHashes(entryPoint, userOps)); - } - - function getUserOpHashes(IEntryPoint entryPoint, PackedUserOperation[] memory userOps) public view returns (bytes32[] memory ret) { - ret = new bytes32[](userOps.length); - for (uint i = 0; i < userOps.length; i++) { - ret[i] = entryPoint.getUserOpHash(userOps[i]); - } - } -} diff --git a/packages/bundler/src/MethodHandlerERC4337.ts b/packages/bundler/src/MethodHandlerERC4337.ts index 6fc28e85..b1af4518 100644 --- a/packages/bundler/src/MethodHandlerERC4337.ts +++ b/packages/bundler/src/MethodHandlerERC4337.ts @@ -1,26 +1,34 @@ +import debug from 'debug' import { BigNumber, BigNumberish, Signer } from 'ethers' import { JsonRpcProvider, Log } from '@ethersproject/providers' +import { EventFragment } from '@ethersproject/abi' + +import { MainnetConfig, PreVerificationGasCalculator } from '@account-abstraction/sdk' -import { BundlerConfig } from './BundlerConfig' import { + AddressZero, + IEntryPoint, + PackedUserOperation, RpcError, + UserOperation, + UserOperationEventEvent, ValidationErrors, - requireAddressAndFields, - packUserOp, - PackedUserOperation, - unpackUserOp, - simulationRpcParams, - decodeSimulateHandleOpResult, - AddressZero, decodeRevertReason, + decodeSimulateHandleOpResult, + deepHexlify, + erc4337RuntimeVersion, mergeValidationDataValues, - UserOperationEventEvent, IEntryPoint, requireCond, deepHexlify, tostr, erc4337RuntimeVersion - , UserOperation + packUserOp, + requireAddressAndFields, + requireCond, + simulationRpcParams, + tostr, + unpackUserOp } from '@account-abstraction/utils' +import { BundlerConfig } from './BundlerConfig' + import { ExecutionManager } from './modules/ExecutionManager' import { StateOverride, UserOperationByHashResponse, UserOperationReceipt } from './RpcTypes' -import { MainnetConfig, PreVerificationGasCalculator } from '@account-abstraction/sdk' -import { EventFragment } from '@ethersproject/abi' export const HEX_REGEX = /^0x[a-fA-F\d]*$/i @@ -169,7 +177,7 @@ export class MethodHandlerERC4337 { async sendUserOperation (userOp: UserOperation, entryPointInput: string): Promise { await this._validateParameters(userOp, entryPointInput) - console.log(`UserOperation: Sender=${userOp.sender} Nonce=${tostr(userOp.nonce)} EntryPoint=${entryPointInput} Paymaster=${userOp.paymaster ?? ''}`) + debug(`UserOperation: Sender=${userOp.sender} Nonce=${tostr(userOp.nonce)} EntryPoint=${entryPointInput} Paymaster=${userOp.paymaster ?? ''}`) await this.execManager.sendUserOperation(userOp, entryPointInput, false) return await this.entryPoint.getUserOpHash(packUserOp(userOp)) } diff --git a/packages/bundler/src/modules/BundleManager.ts b/packages/bundler/src/modules/BundleManager.ts index 3b3dc191..219de843 100644 --- a/packages/bundler/src/modules/BundleManager.ts +++ b/packages/bundler/src/modules/BundleManager.ts @@ -21,11 +21,10 @@ import { ValidationErrors, mergeStorageMap, packUserOp, - runContractScript + getUserOpHash } from '@account-abstraction/utils' import { EventsManager } from './EventsManager' -import { GetUserOpHashes__factory } from '../types' import { IBundleManager } from './IBundleManager' import { MempoolEntry } from './MempoolEntry' import { MempoolManager } from './MempoolManager' @@ -373,11 +372,8 @@ export class BundleManager implements IBundleManager { // helper function to get hashes of all UserOps async getUserOpHashes (userOps: UserOperation[]): Promise { - const { userOpHashes } = await runContractScript(this.entryPoint.provider, - new GetUserOpHashes__factory(), - [this.entryPoint.address, userOps.map(packUserOp)]) - - return userOpHashes + const network = await this.entryPoint.provider.getNetwork() + return userOps.map(it => getUserOpHash(it, this.entryPoint.address, network.chainId)) } async getPaymasterBalance (paymaster: string): Promise { From c3637d91ea0a8ce28086d146496a1aab80baf002 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Tue, 15 Oct 2024 21:02:28 +0200 Subject: [PATCH 14/16] Fix tests --- packages/bundler/test/BundlerServer.test.ts | 2 +- packages/bundler/test/UserOpMethodHandler.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bundler/test/BundlerServer.test.ts b/packages/bundler/test/BundlerServer.test.ts index c8cdd4ba..5eda1c51 100644 --- a/packages/bundler/test/BundlerServer.test.ts +++ b/packages/bundler/test/BundlerServer.test.ts @@ -101,7 +101,7 @@ describe('BundleServer', function () { callData: '0x', callGasLimit: 1e6, verificationGasLimit: 1e6, - preVerificationGas: 50000, + preVerificationGas: 60000, maxFeePerGas: 1e6, maxPriorityFeePerGas: 1e6, signature: '0x' diff --git a/packages/bundler/test/UserOpMethodHandler.test.ts b/packages/bundler/test/UserOpMethodHandler.test.ts index 7c15814d..f8b396cf 100644 --- a/packages/bundler/test/UserOpMethodHandler.test.ts +++ b/packages/bundler/test/UserOpMethodHandler.test.ts @@ -140,7 +140,7 @@ describe('UserOpMethodHandler', function () { // execution should be quite low. // (NOTE: actual execution should revert: it only succeeds because the wallet is NOT deployed yet, // and estimation doesn't perform full deploy-validate-execute cycle) - expect(ret.callGasLimit).to.be.closeTo(25000, 10000) + expect(ret.callGasLimit).to.be.closeTo(1000, 50) }) it('estimateUserOperationGas should estimate using state overrides', async function () { From adf7d7c36844040ca3fd67af3ff4ca1d187cd2f0 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Tue, 15 Oct 2024 21:27:26 +0200 Subject: [PATCH 15/16] Fix config --- packages/bundler/localconfig/bundler.rip7560.config.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/bundler/localconfig/bundler.rip7560.config.json b/packages/bundler/localconfig/bundler.rip7560.config.json index fc347601..8bfc823b 100644 --- a/packages/bundler/localconfig/bundler.rip7560.config.json +++ b/packages/bundler/localconfig/bundler.rip7560.config.json @@ -1,4 +1,5 @@ { + "chainId": 1337, "gasFactor": "1", "port": "3000", "privateApiPort": "3001", From 51b02ef8b54a3d81a375e57c8869cf2e462a0a00 Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Sun, 20 Oct 2024 13:05:39 +0200 Subject: [PATCH 16/16] Move 'DebugBundlerConfigShape' to appropriate location --- packages/bundler/src/BundlerConfig.ts | 14 ++++++++++++++ packages/bundler/src/DebugMethodHandler.ts | 20 +++----------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/bundler/src/BundlerConfig.ts b/packages/bundler/src/BundlerConfig.ts index fb93be5c..3938286c 100644 --- a/packages/bundler/src/BundlerConfig.ts +++ b/packages/bundler/src/BundlerConfig.ts @@ -78,6 +78,20 @@ export const BundlerConfigShape = { estimationPaymasterDataSize: ow.optional.number } +/** + * Only parameters in this object can be provided by a 'debug_bundler_setConfiguration' API. + */ +export const DebugBundlerConfigShape = { + fixedGasOverhead: ow.optional.number, + perUserOpGasOverhead: ow.optional.number, + perUserOpWordGasOverhead: ow.optional.number, + zeroByteGasCost: ow.optional.number, + nonZeroByteGasCost: ow.optional.number, + expectedBundleSize: ow.optional.number, + estimationSignatureSize: ow.optional.number, + estimationPaymasterDataSize: ow.optional.number +} + // TODO: consider if we want any default fields at all // TODO: implement merging config (args -> config.js -> default) and runtime shape validation export const bundlerConfigDefault: Partial = { diff --git a/packages/bundler/src/DebugMethodHandler.ts b/packages/bundler/src/DebugMethodHandler.ts index 72f20fd2..605fc0d6 100644 --- a/packages/bundler/src/DebugMethodHandler.ts +++ b/packages/bundler/src/DebugMethodHandler.ts @@ -1,28 +1,14 @@ import ow from 'ow' import { StakeInfo } from '@account-abstraction/utils' +import { PreVerificationGasCalculator } from '@account-abstraction/sdk' -import { BundlerConfig } from './BundlerConfig' +import { BundlerConfig, DebugBundlerConfigShape } from './BundlerConfig' import { EventsManager } from './modules/EventsManager' import { ExecutionManager } from './modules/ExecutionManager' import { MempoolManager } from './modules/MempoolManager' import { ReputationDump, ReputationManager } from './modules/ReputationManager' import { SendBundleReturn } from './modules/BundleManager' -import { PreVerificationGasCalculator } from '@account-abstraction/sdk' - -/** - * Only parameters in this object can be provided by a 'debug_bundler_setConfiguration' API. - */ -export const BundlerConfigShape = { - fixedGasOverhead: ow.optional.number, - perUserOpGasOverhead: ow.optional.number, - perUserOpWordGasOverhead: ow.optional.number, - zeroByteGasCost: ow.optional.number, - nonZeroByteGasCost: ow.optional.number, - expectedBundleSize: ow.optional.number, - estimationSignatureSize: ow.optional.number, - estimationPaymasterDataSize: ow.optional.number -} export class DebugMethodHandler { constructor ( @@ -97,7 +83,7 @@ export class DebugMethodHandler { } async _setConfiguration (config: Partial): Promise { - ow.object.exactShape(BundlerConfigShape) + ow.object.exactShape(DebugBundlerConfigShape) return await this.execManager._setConfiguration(config) } }