From 646c6869bdc1b2b8e9e339368012d5decbf80cec Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Tue, 21 Jan 2025 01:46:39 +0100 Subject: [PATCH] WIP: Add support for RIP-7560 transaction type --- packages/bundler/src/modules/initServer.ts | 7 +- .../src/AccountAbstractionEntity.ts | 1 + .../validation-manager/src/ERC7562Parser.ts | 233 +++++++++++++++++- packages/validation-manager/src/index.ts | 37 +-- 4 files changed, 240 insertions(+), 38 deletions(-) diff --git a/packages/bundler/src/modules/initServer.ts b/packages/bundler/src/modules/initServer.ts index 2078edba..a40ac3fb 100644 --- a/packages/bundler/src/modules/initServer.ts +++ b/packages/bundler/src/modules/initServer.ts @@ -9,9 +9,11 @@ import { BundlerReputationParams, ReputationManager } from './ReputationManager' import { MempoolManager } from './MempoolManager' import { BundleManager } from './BundleManager' import { + AA_ENTRY_POINT, + AA_STAKE_MANAGER, + IValidationManager, ValidationManager, ValidationManagerRIP7560, - IValidationManager, AA_STAKE_MANAGER } from '@account-abstraction/validation-manager' import { BundlerConfig } from '../BundlerConfig' import { EventsManager } from './EventsManager' @@ -36,15 +38,16 @@ export function initServer (config: BundlerConfig, signer: Signer): [ExecutionMa const eventsManager = new EventsManager(entryPoint, mempoolManager, reputationManager) const mergedPvgcConfig = Object.assign({}, ChainConfigs[config.chainId] ?? {}, config) const preVerificationGasCalculator = new PreVerificationGasCalculator(mergedPvgcConfig) - const erc7562Parser = new ERC7562Parser(entryPoint.address, config.senderCreator, true) let validationManager: IValidationManager let bundleManager: IBundleManager if (!config.rip7560) { + const erc7562Parser = new ERC7562Parser(entryPoint.address, config.senderCreator, true) const tracerProvider = config.tracerRpcUrl == null ? undefined : getNetworkProvider(config.tracerRpcUrl) validationManager = new ValidationManager(entryPoint, config.unsafe, preVerificationGasCalculator, erc7562Parser, tracerProvider) bundleManager = new BundleManager(entryPoint, entryPoint.provider as JsonRpcProvider, signer, eventsManager, mempoolManager, validationManager, reputationManager, config.beneficiary, parseEther(config.minBalance), config.maxBundleGas, config.conditionalRpc) } else { + const erc7562Parser = new ERC7562Parser(AA_ENTRY_POINT, config.senderCreator, true) const stakeManager = IRip7560StakeManager__factory.connect(AA_STAKE_MANAGER, signer) validationManager = new ValidationManagerRIP7560(stakeManager, entryPoint.provider as JsonRpcProvider, erc7562Parser, config.unsafe) bundleManager = new BundleManagerRIP7560(entryPoint.provider as JsonRpcProvider, signer, eventsManager, mempoolManager, validationManager, reputationManager, diff --git a/packages/validation-manager/src/AccountAbstractionEntity.ts b/packages/validation-manager/src/AccountAbstractionEntity.ts index e5fe6280..d50d2b1e 100644 --- a/packages/validation-manager/src/AccountAbstractionEntity.ts +++ b/packages/validation-manager/src/AccountAbstractionEntity.ts @@ -5,5 +5,6 @@ export enum AccountAbstractionEntity { aggregator = 'aggregator', senderCreator = 'SenderCreator', entryPoint = 'EntryPoint', + nativeEntryPoint = 'NativeEntryPoint', none = 'none' } diff --git a/packages/validation-manager/src/ERC7562Parser.ts b/packages/validation-manager/src/ERC7562Parser.ts index 0e32042d..c801be30 100644 --- a/packages/validation-manager/src/ERC7562Parser.ts +++ b/packages/validation-manager/src/ERC7562Parser.ts @@ -23,6 +23,222 @@ import { ValidationResult } from './IValidationManager' import { ERC7562Call } from './ERC7562Call' import { getOpcodeName } from './enum/EVMOpcodes' +// TODO: Use artifact from the submodule +const RIP7560EntryPointABI = [ + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address' + }, + { + indexed: true, + internalType: 'address', + name: 'paymaster', + type: 'address' + }, + { + indexed: true, + internalType: 'address', + name: 'deployer', + type: 'address' + } + ], + name: 'RIP7560AccountDeployed', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address' + }, + { + indexed: true, + internalType: 'address', + name: 'paymaster', + type: 'address' + }, + { + indexed: false, + internalType: 'uint256', + name: 'nonceKey', + type: 'uint256' + }, + { + indexed: false, + internalType: 'uint256', + name: 'nonceSequence', + type: 'uint256' + }, + { + indexed: false, + internalType: 'uint256', + name: 'executionStatus', + type: 'uint256' + } + ], + name: 'RIP7560TransactionEvent', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address' + }, + { + indexed: true, + internalType: 'address', + name: 'paymaster', + type: 'address' + }, + { + indexed: false, + internalType: 'uint256', + name: 'nonceKey', + type: 'uint256' + }, + { + indexed: false, + internalType: 'uint256', + name: 'nonceSequence', + type: 'uint256' + }, + { + indexed: false, + internalType: 'bytes', + name: 'revertReason', + type: 'bytes' + } + ], + name: 'RIP7560TransactionPostOpRevertReason', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address' + }, + { + indexed: false, + internalType: 'uint256', + name: 'nonceKey', + type: 'uint256' + }, + { + indexed: false, + internalType: 'uint256', + name: 'nonceSequence', + type: 'uint256' + }, + { + indexed: false, + internalType: 'bytes', + name: 'revertReason', + type: 'bytes' + } + ], + name: 'RIP7560TransactionRevertReason', + type: 'event' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'validAfter', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'validUntil', + type: 'uint256' + } + ], + name: 'acceptAccount', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'validAfter', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'validUntil', + type: 'uint256' + }, + { + internalType: 'bytes', + name: 'context', + type: 'bytes' + } + ], + name: 'acceptPaymaster', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'validAfter', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'validUntil', + type: 'uint256' + } + ], + name: 'sigFailAccount', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'validAfter', + type: 'uint256' + }, + { + internalType: 'uint256', + name: 'validUntil', + type: 'uint256' + }, + { + internalType: 'bytes', + name: 'context', + type: 'bytes' + } + ], + name: 'sigFailPaymaster', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + } +] + export interface ERC7562ValidationResults { storageMap: StorageMap ruleViolations: ERC7562Violation[] @@ -45,7 +261,7 @@ export class ERC7562Parser { readonly bailOnViolation: boolean ) {} - private _init (erc7562Call: ERC7562Call) { + private _init (erc7562Call: ERC7562Call): void { this.keccak = erc7562Call.keccak ?? [] this.ruleViolations = [] this.currentEntity = AccountAbstractionEntity.none @@ -102,6 +318,7 @@ export class ERC7562Parser { private _tryDetectKnownMethod (call: ERC7562Call): string { const mergedAbi = Object.values([ + ...RIP7560EntryPointABI, ...SenderCreator__factory.abi, ...IEntryPoint__factory.abi, ...IPaymaster__factory.abi @@ -366,7 +583,11 @@ export class ERC7562Parser { let illegalZeroCodeAccess: any for (const addr of Object.keys(tracerResults.contractSize)) { // [OP-042] - if (addr.toLowerCase() !== userOp.sender.toLowerCase() && addr.toLowerCase() !== this.entryPointAddress.toLowerCase() && tracerResults.contractSize[addr].contractSize <= 2) { + if ( + addr.toLowerCase() !== userOp.sender.toLowerCase() && + // addr.toLowerCase() !== AA_ENTRY_POINT && + addr.toLowerCase() !== this.entryPointAddress.toLowerCase() && + tracerResults.contractSize[addr].contractSize <= 2) { illegalZeroCodeAccess = tracerResults.contractSize[addr] illegalZeroCodeAccess.address = addr this._violationDetected({ @@ -389,9 +610,15 @@ export class ERC7562Parser { checkOp054 (erc7562Call: ERC7562Call): void { const isCallToEntryPoint = this._isCallToEntryPoint(erc7562Call) const knownMethod = this._tryDetectKnownMethod(erc7562Call) + const isEntryPointCallAllowedRIP7560 = knownMethod === 'acceptAccount' || + knownMethod === 'acceptPaymaster' || + knownMethod === 'sigFailAccount' || + knownMethod === 'sigFailPaymaster' const isEntryPointCallAllowedOP052 = knownMethod === 'depositTo' const isEntryPointCallAllowedOP053 = knownMethod === '0x' - const isEntryPointCallAllowed = isEntryPointCallAllowedOP052 || isEntryPointCallAllowedOP053 + const isEntryPointCallAllowed = isEntryPointCallAllowedOP052 || + isEntryPointCallAllowedOP053 || + isEntryPointCallAllowedRIP7560 const isRuleViolated = isCallToEntryPoint && !isEntryPointCallAllowed if (isRuleViolated) { this._violationDetected({ diff --git a/packages/validation-manager/src/index.ts b/packages/validation-manager/src/index.ts index d45fa5a6..4a916422 100644 --- a/packages/validation-manager/src/index.ts +++ b/packages/validation-manager/src/index.ts @@ -3,12 +3,12 @@ import { JsonRpcProvider } from '@ethersproject/providers' import { AddressZero, IEntryPoint__factory, - OperationRIP7560, UserOperation } from '@account-abstraction/utils' import { PreVerificationGasCalculator } from '@account-abstraction/sdk' -import { bundlerJSTracerName, debug_traceCall, eth_traceRip7560Validation } from './GethTracer' +import { bundlerJSTracerName, debug_traceCall } from './GethTracer' +// @ts-ignore import { bundlerCollectorTracer } from './BundlerCollectorTracer' import { ValidateUserOpResult } from './IValidationManager' import { ValidationManager } from './ValidationManager' @@ -37,37 +37,8 @@ export async function supportsDebugTraceCall (provider: JsonRpcProvider, rip7560 } if (rip7560) { - // TODO: remove - const defaultsForRip7560Tx: OperationRIP7560 = { - accessList: [], - builderFee: '0x0', - chainId: '0x539', - value: '0x0', - sender: AddressZero, - nonceKey: '0x0', - nonce: '0x0', - executionData: '0x', - callGasLimit: '0x0', - verificationGasLimit: '0x10000', - maxFeePerGas: '0x100000000', - maxPriorityFeePerGas: '0x100000000', - paymaster: AddressZero, - paymasterData: '0x', - factory: AddressZero, - factoryData: '0x', - paymasterVerificationGasLimit: '0x10000', - paymasterPostOpGasLimit: '0x0', - authorizationData: '0x', - authorizationList: [] - }; - - // TODO: align parameter names across 4337 and 7560 - (defaultsForRip7560Tx as any).deployer = defaultsForRip7560Tx.factory; - (defaultsForRip7560Tx as any).deployerData = defaultsForRip7560Tx.factoryData - // make sure we can trace a call. - const ret = await eth_traceRip7560Validation(provider, defaultsForRip7560Tx - ).catch(e => e) - return ret.traceResults != null + // no need to check for the internal RIP-7560 support + return true } // make sure we can trace a call. const ret = await debug_traceCall(provider,