From cb6d163587ad8f88d9aef2f2e6c8ebc8742aeed7 Mon Sep 17 00:00:00 2001 From: Dror Tirosh Date: Thu, 21 Dec 2023 00:10:24 +0200 Subject: [PATCH] AA-222 add delegateAndRevert helper, to simulate code execution from entryPoint. (#365) Minimal "static" call from EntryPoint to simulate special cases. Equivalent to the EntryPointSimulations in purpose, but doesn't require stateOverride. Executes code on behalf of entryPoint using delegateCall, and revert with the result --- contracts/core/EntryPoint.sol | 6 ++++++ contracts/interfaces/IEntryPoint.sol | 9 +++++++++ reports/gas-checker.txt | 12 ++++++------ test/entrypoint.test.ts | 13 +++---------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/contracts/core/EntryPoint.sol b/contracts/core/EntryPoint.sol index 685ff3d56..f619874b3 100644 --- a/contracts/core/EntryPoint.sol +++ b/contracts/core/EntryPoint.sol @@ -753,4 +753,10 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard, data := offset } } + + /// @inheritdoc IEntryPoint + function delegateAndRevert(address target, bytes calldata data) external { + (bool success, bytes memory ret) = target.delegatecall(data); + revert DelegateAndRevert(success, ret); + } } diff --git a/contracts/interfaces/IEntryPoint.sol b/contracts/interfaces/IEntryPoint.sol index 40df5c03b..c815a264e 100644 --- a/contracts/interfaces/IEntryPoint.sol +++ b/contracts/interfaces/IEntryPoint.sol @@ -198,4 +198,13 @@ interface IEntryPoint is IStakeManager, INonceManager { */ function getSenderAddress(bytes memory initCode) external; + error DelegateAndRevert(bool success, bytes ret); + + /** + * Helper method for dry-run testing. + * @dev calling this method, the EntryPoint will make a delegatecall to the given data, and report (via revert) the result. + * The method always revert, so is only useful off-chain for dry run calls, in cases where state-override to replace + * actual EntryPoint code is less convenient. + */ + function delegateAndRevert(address target, bytes calldata data) external; } diff --git a/reports/gas-checker.txt b/reports/gas-checker.txt index 1a8be4620..25d1dcea0 100644 --- a/reports/gas-checker.txt +++ b/reports/gas-checker.txt @@ -16,24 +16,24 @@ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ ║ simple - diff from previous │ 2 │ │ 44187 │ 15173 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple │ 10 │ 479694 │ │ ║ +║ simple │ 10 │ 479742 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ ║ simple - diff from previous │ 11 │ │ 44247 │ 15233 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ ║ simple paymaster │ 1 │ 88187 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster with diff │ 2 │ │ 43138 │ 14124 ║ +║ simple paymaster with diff │ 2 │ │ 43114 │ 14100 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster │ 10 │ 476718 │ │ ║ +║ simple paymaster │ 10 │ 476706 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ simple paymaster with diff │ 11 │ │ 43185 │ 14171 ║ +║ simple paymaster with diff │ 11 │ │ 43173 │ 14159 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ ║ big tx 5k │ 1 │ 182987 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ ║ big tx - diff from previous │ 2 │ │ 144698 │ 19438 ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ big tx 5k │ 10 │ 1485290 │ │ ║ +║ big tx 5k │ 10 │ 1485386 │ │ ║ ╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢ -║ big tx - diff from previous │ 11 │ │ 144699 │ 19439 ║ +║ big tx - diff from previous │ 11 │ │ 144759 │ 19499 ║ ╚════════════════════════════════╧═══════╧═══════════════╧════════════════╧═════════════════════╝ diff --git a/test/entrypoint.test.ts b/test/entrypoint.test.ts index 6485e0fea..dc235517d 100644 --- a/test/entrypoint.test.ts +++ b/test/entrypoint.test.ts @@ -25,7 +25,6 @@ import { SimpleAccountFactory__factory, IStakeManager__factory, INonceManager__factory, - EntryPoint__factory, EntryPoint } from '../typechain' import { @@ -1310,19 +1309,13 @@ describe('EntryPoint', function () { }) it('should return true for pure EntryPoint, IStakeManager and INonceManager interface IDs', async function () { - const epInterface = EntryPoint__factory.createInterface() + const epInterface = IEntryPoint__factory.createInterface() const smInterface = IStakeManager__factory.createInterface() const nmInterface = INonceManager__factory.createInterface() // note: manually generating "pure", solidity-like "type(IEntryPoint).interfaceId" without inherited methods + const inheritedMethods = new Set([...smInterface.fragments, ...nmInterface.fragments].map(f => f.name)) const epPureInterfaceFunctions = [ - ...epInterface.fragments.filter(it => [ - 'handleOps', - 'handleAggregatedOps', - 'getUserOpHash', - 'getSenderAddress', - 'simulateValidation', - 'simulateHandleOp' - ].includes(it.name)) + ...epInterface.fragments.filter(it => !inheritedMethods.has(it.name) && it.type === 'function') ] const epPureInterfaceID = getERC165InterfaceID(epPureInterfaceFunctions) const smInterfaceID = getERC165InterfaceID([...smInterface.fragments])