From 3104001a681047995050dcd0329852d5f851b048 Mon Sep 17 00:00:00 2001 From: Mberic Date: Wed, 7 Jan 2026 16:39:57 +0300 Subject: [PATCH] feat: make codebase compatible with v0.8 erc4337 reference implementation --- foundry.lock | 11 ++++ lib/account-abstraction | 2 +- lib/openzeppelin-contracts | 2 +- src/ERC4337Checker.sol | 65 +++++++++++------------- test/ERC4337Checker.t.sol | 97 ++++++++++++++++++------------------ test/mocks/MockAccount.sol | 14 +++--- test/mocks/MockPaymaster.sol | 9 +++- 7 files changed, 103 insertions(+), 97 deletions(-) create mode 100644 foundry.lock diff --git a/foundry.lock b/foundry.lock new file mode 100644 index 0000000..595dcc3 --- /dev/null +++ b/foundry.lock @@ -0,0 +1,11 @@ +{ + "lib/account-abstraction": { + "rev": "4cbc06072cdc19fd60f285c5997f4f7f57a588de" + }, + "lib/forge-std": { + "rev": "4f57c59f066a03d13de8c65bb34fca8247f5fcb2" + }, + "lib/openzeppelin-contracts": { + "rev": "fcbae5394ae8ad52d8e580a3477db99814b9d565" + } +} \ No newline at end of file diff --git a/lib/account-abstraction b/lib/account-abstraction index abff2ac..4cbc060 160000 --- a/lib/account-abstraction +++ b/lib/account-abstraction @@ -1 +1 @@ -Subproject commit abff2aca61a8f0934e533d0d352978055fddbd96 +Subproject commit 4cbc06072cdc19fd60f285c5997f4f7f57a588de diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index fd81a96..fcbae53 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit fd81a96f01cc42ef1c9a5399364968d0e07e9e90 +Subproject commit fcbae5394ae8ad52d8e580a3477db99814b9d565 diff --git a/src/ERC4337Checker.sol b/src/ERC4337Checker.sol index dd35dbe..254b08d 100644 --- a/src/ERC4337Checker.sol +++ b/src/ERC4337Checker.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; import {Vm} from "forge-std/Vm.sol"; -import {UserOperation} from "account-abstraction/interfaces/UserOperation.sol"; -import {EntryPoint} from "account-abstraction/core/EntryPoint.sol"; -import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; +import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol"; +import {EntryPointSimulations} from "account-abstraction/core/EntryPointSimulations.sol"; +import {IEntryPointSimulations} from "account-abstraction/interfaces/IEntryPointSimulations.sol"; import {IStakeManager} from "account-abstraction/interfaces/IStakeManager.sol"; import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.sol"; import "forge-std/console2.sol"; @@ -30,20 +30,15 @@ contract ERC4337Checker { } } - function simulateAndVerifyUserOp(Vm vm, UserOperation memory userOp, EntryPoint entryPoint) external returns (bool) { + function simulateAndVerifyUserOp(Vm vm, PackedUserOperation memory userOp, EntryPointSimulations entryPoint) external returns (bool) { // this starts the recording of the debug trace that will later be analyzed vm.startDebugTraceRecording(); try entryPoint.simulateValidation(userOp) { // the simulateValidation function will always revert. // in this test, we do not really care if it is revert in an expected output or not. - } catch (bytes memory reason) { - // if not fail with ValidationResult error, it is likely to be something unexpected. - if (reason.length < 4 || bytes4(reason) != IEntryPoint.ValidationResult.selector) { - revert(string(abi.encodePacked( - "simulateValidation call failed unexpectedly: ", reason - ))); - } + } catch { + } // collect the recorded opcodes, stack and memory inputs. @@ -53,7 +48,7 @@ contract ERC4337Checker { return validateUserOp(steps, userOp, entryPoint); } - function simulateAndVerifyBundle(Vm vm, UserOperation[] memory userOps, EntryPoint entryPoint) external returns (bool) { + function simulateAndVerifyBundle(Vm vm, PackedUserOperation[] memory userOps, EntryPointSimulations entryPoint) external returns (bool) { // this starts the recording of the debug trace that will later be analyzed vm.startDebugTraceRecording(); @@ -61,13 +56,8 @@ contract ERC4337Checker { try entryPoint.simulateValidation(userOps[i]) { // the simulateValidation function will always revert. // in this test, we do not really care if it is revert in an expected output or not. - } catch (bytes memory reason) { - // if not fail with ValidationResult error, it is likely to be something unexpected. - if (reason.length < 4 || bytes4(reason) != IEntryPoint.ValidationResult.selector) { - revert(string(abi.encodePacked( - "simulateValidation call failed unexpectedly: ", reason - ))); - } + } catch { + } } @@ -79,7 +69,7 @@ contract ERC4337Checker { } - function validateBundle(Vm.DebugStep[] memory debugSteps, UserOperation[] memory userOps, EntryPoint entryPoint) + function validateBundle(Vm.DebugStep[] memory debugSteps, PackedUserOperation[] memory userOps, EntryPointSimulations entryPoint) public returns (bool) { @@ -97,7 +87,7 @@ contract ERC4337Checker { return result; } - function validateUserOp(Vm.DebugStep[] memory debugSteps, UserOperation memory userOp, EntryPoint entryPoint) + function validateUserOp(Vm.DebugStep[] memory debugSteps, PackedUserOperation memory userOp, EntryPointSimulations entryPoint) public returns (bool) { @@ -107,12 +97,12 @@ contract ERC4337Checker { bool result = true; // Validate the opcodes and storages for `validateUserOp()` - if (!validateSteps(senderSteps, userOp, entryPoint)) { + if (!validateSteps(senderSteps, userOp, entryPoint, true)) { result = false; } // Validate the opcodes and storages for `validatePaymasterUserOp()` - if (!validateSteps(paymasterSteps, userOp, entryPoint)) { + if (!validateSteps(paymasterSteps, userOp, entryPoint, false)) { result = false; } @@ -125,8 +115,8 @@ contract ERC4337Checker { */ function validateBundleStorageNoRepeat( Vm.DebugStep[] memory debugSteps, - UserOperation[] memory userOps, - EntryPoint entryPoint + PackedUserOperation[] memory userOps, + EntryPointSimulations entryPoint ) private returns (bool) { @@ -135,7 +125,7 @@ contract ERC4337Checker { bool result = true; for (uint i = 0; i < userOps.length; i++) { - UserOperation memory userOp = userOps[i]; + PackedUserOperation memory userOp = userOps[i]; (Vm.DebugStep[] memory senderSteps, ) = getRelativeDebugSteps(debugSteps, userOp, entryPoint); // a temporary slots, will merge with the main slots after checking @@ -189,8 +179,9 @@ contract ERC4337Checker { function validateSteps( Vm.DebugStep[] memory debugSteps, - UserOperation memory userOp, - EntryPoint entryPoint + PackedUserOperation memory userOp, + EntryPointSimulations entryPoint, + bool isFromAccount ) private returns (bool) @@ -203,7 +194,7 @@ contract ERC4337Checker { if (!validateForbiddenOpcodes(debugSteps)) { result = false; } - if (!validateCall(debugSteps, address(entryPoint), true)) { + if (!validateCall(debugSteps, address(entryPoint), isFromAccount)) { result = false; } if (!validateExtcodeMayNotAccessAddressWithoutCode(debugSteps)) { @@ -219,7 +210,7 @@ contract ERC4337Checker { return result; } - function validateStorage(Vm.DebugStep[] memory debugSteps, UserOperation memory userOp, EntryPoint entryPoint) + function validateStorage(Vm.DebugStep[] memory debugSteps, PackedUserOperation memory userOp, EntryPointSimulations entryPoint) private returns (bool) { @@ -382,7 +373,7 @@ contract ERC4337Checker { } if (isCallToEntryPoint(debugSteps[i], entryPoint)) { failureLogs.push(FailureLog({ - errorMsg: "cannot call EntryPoint methods, except depositTo", + errorMsg: "cannot call EntryPointSimulations methods, except depositTo", contractAddr: debugSteps[i].contractAddr })); @@ -420,7 +411,7 @@ contract ERC4337Checker { return result; } - function validateCreate2(Vm.DebugStep[] memory debugSteps, UserOperation memory userOp) + function validateCreate2(Vm.DebugStep[] memory debugSteps, PackedUserOperation memory userOp) private returns (bool) { @@ -548,8 +539,8 @@ contract ERC4337Checker { function getRelativeDebugSteps( Vm.DebugStep[] memory debugSteps, - UserOperation memory userOp, - EntryPoint entryPoint + PackedUserOperation memory userOp, + EntryPointSimulations entryPoint ) private pure returns (Vm.DebugStep[] memory, Vm.DebugStep[] memory) @@ -652,18 +643,18 @@ contract ERC4337Checker { return associatedSlots; } - function getStakeInfo(address addr, EntryPoint entryPoint) internal view returns (IStakeManager.StakeInfo memory) { + function getStakeInfo(address addr, EntryPointSimulations entryPoint) internal view returns (IStakeManager.StakeInfo memory) { IStakeManager.DepositInfo memory depositInfo = entryPoint.getDepositInfo(addr); return IStakeManager.StakeInfo({stake: depositInfo.stake, unstakeDelaySec: depositInfo.unstakeDelaySec}); } - function getFactoryAddr(UserOperation memory userOp) private pure returns (address) { + function getFactoryAddr(PackedUserOperation memory userOp) private pure returns (address) { bytes memory initCode = userOp.initCode; return initCode.length >= 20 ? address(bytes20(initCode)) : address(0); } - function getPaymasterAddr(UserOperation memory userOp) private pure returns (address) { + function getPaymasterAddr(PackedUserOperation memory userOp) private pure returns (address) { bytes memory pData = userOp.paymasterAndData; return pData.length >= 20 ? address(bytes20(pData)) : address(0); } diff --git a/test/ERC4337Checker.t.sol b/test/ERC4337Checker.t.sol index c682029..bf7bd1d 100644 --- a/test/ERC4337Checker.t.sol +++ b/test/ERC4337Checker.t.sol @@ -3,9 +3,9 @@ pragma solidity ^0.8.13; import {Test, console2} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; -import {EntryPoint} from "account-abstraction/core/EntryPoint.sol"; -import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; -import {UserOperation} from "account-abstraction/interfaces/UserOperation.sol"; +import {EntryPointSimulations} from "account-abstraction/core/EntryPointSimulations.sol"; +import {IEntryPointSimulations} from "account-abstraction/interfaces/IEntryPointSimulations.sol"; +import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol"; import {ERC4337Checker} from "../src/ERC4337Checker.sol"; @@ -13,20 +13,21 @@ import {MockAccount} from "./mocks/MockAccount.sol"; import {MockPaymaster} from "./mocks/MockPaymaster.sol"; contract ERC4337CheckerTest is Test { - EntryPoint public entryPoint; + EntryPointSimulations public entryPoint; MockAccount public mockAccount; MockPaymaster public mockPaymaster; ERC4337Checker public checker; function setUp() public { - entryPoint = new EntryPoint(); + entryPoint = new EntryPointSimulations(); mockAccount = new MockAccount(entryPoint); vm.deal(address(mockAccount), 1 << 128); // give some funds to the mockAccount - mockPaymaster = new MockPaymaster(IEntryPoint(entryPoint)); + mockPaymaster = new MockPaymaster(entryPoint); vm.deal(address(mockPaymaster), 1 << 128); // give some funds to the mockAccount entryPoint.depositTo{value: 1 ether}(address(mockPaymaster)); + entryPoint.depositTo{value: 1 ether}(address(mockAccount)); mockPaymaster.addStake{value: 2 ether}(1); checker = new ERC4337Checker(); @@ -36,15 +37,15 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.NONE ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); - + assertTrue( checker.simulateAndVerifyUserOp(vm, userOp, entryPoint) ); @@ -54,11 +55,11 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.NONE ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -73,11 +74,11 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.FORBIDDEN_OPCODE_BLOCKTIME ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -94,11 +95,11 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.FORBIDDEN_OPCODE_GASPRICE ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -113,11 +114,11 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.FORBIDDEN_OPCODE_GASLIMIT ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -132,11 +133,11 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.FORBIDDEN_OPCODE_COINBASE ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -151,11 +152,11 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.FORBIDDEN_OPCODE_ORIGIN ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -170,11 +171,11 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.FORBIDDEN_OPCODE_INVALID ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -191,11 +192,11 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.OUT_OF_GAS ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -211,11 +212,11 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.ACCESS_EXTCODE_WITH_ADDRESS_NO_CODE ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -231,11 +232,11 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.TOUCH_UNASSOCIATED_STORAGE_SLOT ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -250,16 +251,16 @@ contract ERC4337CheckerTest is Test { function test_accessPaymasterStorageSlotWithoutStake_shouldfail() public { address mockAccountAddr = address(mockAccount); - MockPaymaster noStakePaymaster = new MockPaymaster(IEntryPoint(entryPoint)); + MockPaymaster noStakePaymaster = new MockPaymaster(entryPoint); vm.deal(address(noStakePaymaster), 1 << 128); // give some funds to the mockAccount entryPoint.depositTo{value: 1 ether}(address(noStakePaymaster)); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.NONE ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); @@ -279,16 +280,16 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.NONE ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); - UserOperation[] memory userOps = new UserOperation[](1); + PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; assertTrue( @@ -300,24 +301,24 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.NONE ); - UserOperation memory userOp1 = _getUnsignedOp( + PackedUserOperation memory userOp1 = _getUnsignedOp( mockAccountAddr, encodedCallData ); address mockAccount2Addr = address(new MockAccount(entryPoint)); vm.deal(address(mockAccount2Addr), 1 << 128); - UserOperation memory userOp2 = _getUnsignedOp( + PackedUserOperation memory userOp2 = _getUnsignedOp( mockAccount2Addr, encodedCallData ); - UserOperation[] memory userOps = new UserOperation[](2); + PackedUserOperation[] memory userOps = new PackedUserOperation[](2); userOps[0] = userOp1; userOps[1] = userOp2; @@ -330,16 +331,16 @@ contract ERC4337CheckerTest is Test { address mockAccountAddr = address(mockAccount); bytes memory encodedCallData = abi.encodeWithSelector( - MockAccount.execute.selector, + MockAccount.executeAttack.selector, MockAccount.AttackType.NONE ); - UserOperation memory userOp = _getUnsignedOp( + PackedUserOperation memory userOp = _getUnsignedOp( mockAccountAddr, encodedCallData ); - UserOperation[] memory userOps = new UserOperation[](2); + PackedUserOperation[] memory userOps = new PackedUserOperation[](2); userOps[0] = userOp; userOps[1] = userOp; // duplicated, so storage will conflict @@ -350,19 +351,17 @@ contract ERC4337CheckerTest is Test { checker.printFailureLogs(); } - function _getUnsignedOp(address target, bytes memory innerCallData) private pure returns (UserOperation memory) { - return UserOperation({ + function _getUnsignedOp(address target, bytes memory innerCallData) private pure returns (PackedUserOperation memory) { + return PackedUserOperation({ sender: target, nonce: 0, // Adjust nonce as required initCode: "", callData: innerCallData, - callGasLimit: 1 << 24, - verificationGasLimit: 1 << 24, + accountGasLimits: bytes32((uint256(5_000_000) << 128) | uint256(30_000_000)), preVerificationGas: 1 << 24, - maxFeePerGas: 1 << 8, - maxPriorityFeePerGas: 1 << 8, + gasFees: bytes32((uint256(5_000_000) << 128) | uint256(30_000_000)), paymasterAndData: "", signature: "" }); } -} +} \ No newline at end of file diff --git a/test/mocks/MockAccount.sol b/test/mocks/MockAccount.sol index d33f6b1..893677f 100644 --- a/test/mocks/MockAccount.sol +++ b/test/mocks/MockAccount.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.0; import {BaseAccount} from "account-abstraction/core/BaseAccount.sol"; -import {UserOperation} from "account-abstraction/interfaces/UserOperation.sol"; +import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol"; +import {IEntryPointSimulations} from "account-abstraction/interfaces/IEntryPointSimulations.sol"; import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; import "forge-std/console2.sol"; - contract InvalidActions { uint invalidSlotAccess; @@ -43,10 +43,10 @@ contract MockAccount is BaseAccount { FORBIDDEN_OPCODE_INVALID } - IEntryPoint private _entryPoint; + IEntryPointSimulations private _entryPoint; InvalidActions private invalidActions; - constructor(IEntryPoint entryPoint_) { + constructor(IEntryPointSimulations entryPoint_) { _entryPoint = entryPoint_; invalidActions = new InvalidActions(); } @@ -55,11 +55,11 @@ contract MockAccount is BaseAccount { return _entryPoint; } - function execute(AttackType /*attackType*/) external pure { + function executeAttack(AttackType /*attackType*/) external pure { console2.log("dummy execute"); } - function _validateSignature(UserOperation calldata userOp, bytes32 /*userOpHash*/) + function _validateSignature(PackedUserOperation calldata userOp, bytes32 /*userOpHash*/) internal virtual override @@ -122,7 +122,7 @@ contract MockAccount is BaseAccount { // Ensure the data is long enough to contain both the function selector and the enum argument require(callData.length >= 4 + 32, "Invalid encodedCallData length"); - require(bytes4(callData[:4]) == this.execute.selector, "Invalid function selector"); + require(bytes4(callData[:4]) == this.executeAttack.selector, "Invalid function selector"); // Convert the value to AttackType enum return AttackType(uint256(bytes32(callData[4:]))); diff --git a/test/mocks/MockPaymaster.sol b/test/mocks/MockPaymaster.sol index 7161e08..b7cd47a 100644 --- a/test/mocks/MockPaymaster.sol +++ b/test/mocks/MockPaymaster.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; import "account-abstraction/core/BasePaymaster.sol"; +import {IEntryPointSimulations} from "account-abstraction/interfaces/IEntryPointSimulations.sol"; /** * test paymaster, that pays for everything, without any check. @@ -14,9 +15,9 @@ contract MockPaymaster is BasePaymaster { UseStorage } - constructor(IEntryPoint _entryPoint) BasePaymaster(_entryPoint) {} + constructor(IEntryPointSimulations _entryPoint) BasePaymaster(_entryPoint) {} - function _validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) + function _validatePaymasterUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) internal virtual override view returns (bytes memory context, uint256 validationData) { AttackType attackType = _decodeAttackType(userOp.paymasterAndData); @@ -37,5 +38,9 @@ contract MockPaymaster is BasePaymaster { } return abi.decode(paymasterAndData[20:], (AttackType)); } + + function _validateEntryPointInterface(IEntryPoint _entryPoint) internal override { + // Skip validation for testing + } }