Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions foundry.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"lib/account-abstraction": {
"rev": "4cbc06072cdc19fd60f285c5997f4f7f57a588de"
},
"lib/forge-std": {
"rev": "4f57c59f066a03d13de8c65bb34fca8247f5fcb2"
},
"lib/openzeppelin-contracts": {
"rev": "fcbae5394ae8ad52d8e580a3477db99814b9d565"
}
}
2 changes: 1 addition & 1 deletion lib/account-abstraction
2 changes: 1 addition & 1 deletion lib/openzeppelin-contracts
65 changes: 28 additions & 37 deletions src/ERC4337Checker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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.
Expand All @@ -53,21 +48,16 @@ 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();

for (uint i = 0 ; i < userOps.length; ++i) {
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 {

}
}

Expand All @@ -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)
{
Expand All @@ -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)
{
Expand All @@ -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;
}

Expand All @@ -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)
{
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -203,7 +194,7 @@ contract ERC4337Checker {
if (!validateForbiddenOpcodes(debugSteps, userOp)) {
result = false;
}
if (!validateCall(debugSteps, address(entryPoint), true)) {
if (!validateCall(debugSteps, address(entryPoint), isFromAccount)) {
result = false;
}
if (!validateExtcodeMayNotAccessAddressWithoutCode(debugSteps)) {
Expand All @@ -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)
{
Expand Down Expand Up @@ -410,7 +401,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
}));

Expand Down Expand Up @@ -448,7 +439,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)
{
Expand Down Expand Up @@ -576,8 +567,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)
Expand Down Expand Up @@ -680,18 +671,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);
}
Expand Down
Loading
Loading