Skip to content

Commit 498ae9c

Browse files
committed
Fix estimations
1 parent a5a7e1e commit 498ae9c

File tree

6 files changed

+356
-4
lines changed

6 files changed

+356
-4
lines changed

deploy.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ for pair in "${pairs[@]}"; do
2626
if [[ -n "$ETHERSCAN_KEY" && -n "$ETHERSCAN_URL" ]]; then
2727
echo "Verifying with $ETHERSCAN_URL using key $ETHERSCAN_KEY"
2828

29-
forge verify-contract 0x0bFA466336b65eEf256ceCfDA1Fb717b8f1B453d src/v07/PimlicoEntryPointSimulations.sol:PimlicoEntryPointSimulations \
29+
forge verify-contract 0x6b11F6aF92A5d705C2788e6DDDfd8B5799d6854d src/v08/PimlicoEntryPointSimulations.sol:PimlicoEntryPointSimulations \
3030
--verifier-url "$ETHERSCAN_URL" \
3131
--etherscan-api-key "$ETHERSCAN_KEY"
3232

33-
forge verify-contract 0xAAa8CA8ae3Bb5DB5D1924de95FFA6b7A1289eCbB src/v07/EntryPointSimulations.sol:EntryPointSimulations \
33+
forge verify-contract 0x66D7B8545c6E93888bdd477362697bB2a4Fbde91 src/v08/EntryPointSimulations.sol:EntryPointSimulations \
3434
--verifier-url "$ETHERSCAN_URL" \
3535
--etherscan-api-key "$ETHERSCAN_KEY"
3636

src/v08/Eip7702Support.sol

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
pragma solidity ^0.8.28;
2+
// SPDX-License-Identifier: MIT
3+
// solhint-disable no-inline-assembly
4+
5+
import "account-abstraction-v8/interfaces/PackedUserOperation.sol";
6+
import "account-abstraction-v8/core/UserOperationLib.sol";
7+
8+
library Eip7702Support {
9+
// EIP-7702 code prefix before delegate address.
10+
bytes3 internal constant EIP7702_PREFIX = 0xef0100;
11+
12+
// EIP-7702 initCode marker, to specify this account is EIP-7702.
13+
bytes2 internal constant INITCODE_EIP7702_MARKER = 0x7702;
14+
15+
using UserOperationLib for PackedUserOperation;
16+
17+
/**
18+
* Get the alternative 'InitCodeHash' value for the UserOp hash calculation when using EIP-7702.
19+
*
20+
* @param userOp - the UserOperation to for the 'InitCodeHash' calculation.
21+
* @return the 'InitCodeHash' value.
22+
*/
23+
function _getEip7702InitCodeHashOverride(PackedUserOperation calldata userOp) internal view returns (bytes32) {
24+
bytes calldata initCode = userOp.initCode;
25+
if (!_isEip7702InitCode(initCode)) {
26+
return 0;
27+
}
28+
address delegate = _getEip7702Delegate(userOp.sender);
29+
if (initCode.length <= 20) {
30+
return keccak256(abi.encodePacked(delegate));
31+
} else {
32+
return keccak256(abi.encodePacked(delegate, initCode[20:]));
33+
}
34+
}
35+
36+
/**
37+
* Check if this 'initCode' is actually an EIP-7702 authorization.
38+
* This is indicated by 'initCode' that starts with INITCODE_EIP7702_MARKER.
39+
*
40+
* @param initCode - the 'initCode' to check.
41+
* @return true if the 'initCode' is EIP-7702 authorization, false otherwise.
42+
*/
43+
function _isEip7702InitCode(bytes calldata initCode) internal pure returns (bool) {
44+
if (initCode.length < 2) {
45+
return false;
46+
}
47+
bytes20 initCodeStart;
48+
// non-empty calldata bytes are always zero-padded to 32-bytes, so can be safely casted to "bytes20"
49+
assembly ("memory-safe") {
50+
initCodeStart := calldataload(initCode.offset)
51+
}
52+
// make sure first 20 bytes of initCode are "0x7702" (padded with zeros)
53+
return initCodeStart == bytes20(INITCODE_EIP7702_MARKER);
54+
}
55+
56+
/**
57+
* Get the EIP-7702 delegate from contract code.
58+
* Must only be used if _isEip7702InitCode(initCode) is true.
59+
*
60+
* @param sender - the EIP-7702 'sender' account to get the delegated contract code address.
61+
* @return the address of the EIP-7702 authorized contract.
62+
*/
63+
function _getEip7702Delegate(address sender) internal view returns (address) {
64+
bytes32 senderCode;
65+
66+
assembly ("memory-safe") {
67+
extcodecopy(sender, 0, 0, 23)
68+
senderCode := mload(0)
69+
}
70+
// To be a valid EIP-7702 delegate, the first 3 bytes are EIP7702_PREFIX
71+
// followed by the delegate address
72+
// We skip the following during estimation
73+
// if (bytes3(senderCode) != EIP7702_PREFIX) {
74+
// // instead of just "not an EIP-7702 delegate", if some info.
75+
// require(sender.code.length > 0, "sender has no code");
76+
// revert("not an EIP-7702 delegate");
77+
// }
78+
return address(bytes20(senderCode << 21));
79+
}
80+
}

src/v08/EntryPoint.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import "account-abstraction-v8/core/UserOperationLib.sol";
1212
import "account-abstraction-v8/core/StakeManager.sol";
1313
import "account-abstraction-v8/core/NonceManager.sol";
1414
import "account-abstraction-v8/core/Helpers.sol";
15-
import "account-abstraction-v8/core/SenderCreator.sol";
16-
import "account-abstraction-v8/core/Eip7702Support.sol";
15+
import "./SenderCreator.sol";
16+
import "./Eip7702Support.sol";
1717
import "account-abstraction-v8/utils/Exec.sol";
1818

1919
import "@openzeppelin/contracts-51/utils/ReentrancyGuardTransient.sol";

src/v08/IEntryPoint.sol

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/**
2+
* Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
3+
* Only one instance required on each chain.
4+
*
5+
*/
6+
// SPDX-License-Identifier: MIT
7+
pragma solidity ^0.8.28;
8+
9+
/* solhint-disable avoid-low-level-calls */
10+
/* solhint-disable no-inline-assembly */
11+
/* solhint-disable reason-string */
12+
13+
import "account-abstraction-v8/interfaces/PackedUserOperation.sol";
14+
import "account-abstraction-v8/interfaces/IStakeManager.sol";
15+
import "account-abstraction-v8/interfaces/IAggregator.sol";
16+
import "account-abstraction-v8/interfaces/INonceManager.sol";
17+
import "account-abstraction-v8/interfaces/ISenderCreator.sol";
18+
19+
interface IEntryPoint is IStakeManager, INonceManager {
20+
/**
21+
*
22+
* An event emitted after each successful request.
23+
* @param userOpHash - Unique identifier for the request (hash its entire content, except signature).
24+
* @param sender - The account that generates this request.
25+
* @param paymaster - If non-null, the paymaster that pays for this request.
26+
* @param nonce - The nonce value from the request.
27+
* @param success - True if the sender transaction succeeded, false if reverted.
28+
* @param actualGasCost - Actual amount paid (by account or paymaster) for this UserOperation.
29+
* @param actualGasUsed - Total gas used by this UserOperation (including preVerification, creation,
30+
* validation and execution).
31+
*/
32+
event UserOperationEvent(
33+
bytes32 indexed userOpHash,
34+
address indexed sender,
35+
address indexed paymaster,
36+
uint256 nonce,
37+
bool success,
38+
uint256 actualGasCost,
39+
uint256 actualGasUsed
40+
);
41+
42+
/**
43+
* Account "sender" was deployed.
44+
* @param userOpHash - The userOp that deployed this account. UserOperationEvent will follow.
45+
* @param sender - The account that is deployed
46+
* @param factory - The factory used to deploy this account (in the initCode)
47+
* @param paymaster - The paymaster used by this UserOp
48+
*/
49+
event AccountDeployed(bytes32 indexed userOpHash, address indexed sender, address factory, address paymaster);
50+
51+
/**
52+
* An event emitted if the UserOperation "callData" reverted with non-zero length.
53+
* @param userOpHash - The request unique identifier.
54+
* @param sender - The sender of this request.
55+
* @param nonce - The nonce used in the request.
56+
* @param revertReason - The return bytes from the reverted "callData" call.
57+
*/
58+
event UserOperationRevertReason(
59+
bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason
60+
);
61+
62+
/**
63+
* An event emitted if the UserOperation Paymaster's "postOp" call reverted with non-zero length.
64+
* @param userOpHash - The request unique identifier.
65+
* @param sender - The sender of this request.
66+
* @param nonce - The nonce used in the request.
67+
* @param revertReason - The return bytes from the reverted call to "postOp".
68+
*/
69+
event PostOpRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason);
70+
71+
/**
72+
* UserOp consumed more than prefund. The UserOperation is reverted, and no refund is made.
73+
* @param userOpHash - The request unique identifier.
74+
* @param sender - The sender of this request.
75+
* @param nonce - The nonce used in the request.
76+
*/
77+
event UserOperationPrefundTooLow(bytes32 indexed userOpHash, address indexed sender, uint256 nonce);
78+
79+
/**
80+
* An event emitted by handleOps() and handleAggregatedOps(), before starting the execution loop.
81+
* Any event emitted before this event, is part of the validation.
82+
*/
83+
event BeforeExecution();
84+
85+
/**
86+
* Signature aggregator used by the following UserOperationEvents within this bundle.
87+
* @param aggregator - The aggregator used for the following UserOperationEvents.
88+
*/
89+
event SignatureAggregatorChanged(address indexed aggregator);
90+
91+
/**
92+
* A custom revert error of handleOps andhandleAggregatedOps, to identify the offending op.
93+
* Should be caught in off-chain handleOps/handleAggregatedOps simulation and not happen on-chain.
94+
* Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts.
95+
* NOTE: If simulateValidation passes successfully, there should be no reason for handleOps to fail on it.
96+
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
97+
* @param reason - Revert reason. The string starts with a unique code "AAmn",
98+
* where "m" is "1" for factory, "2" for account and "3" for paymaster issues,
99+
* so a failure can be attributed to the correct entity.
100+
*/
101+
error FailedOp(uint256 opIndex, string reason);
102+
103+
/**
104+
* A custom revert error of handleOps and handleAggregatedOps, to report a revert by account or paymaster.
105+
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
106+
* @param reason - Revert reason. see FailedOp(uint256,string), above
107+
* @param inner - data from inner cought revert reason
108+
* @dev note that inner is truncated to 2048 bytes
109+
*/
110+
error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);
111+
112+
error PostOpReverted(bytes returnData);
113+
114+
/**
115+
* Error case when a signature aggregator fails to verify the aggregated signature it had created.
116+
* @param aggregator The aggregator that failed to verify the signature
117+
*/
118+
error SignatureValidationFailed(address aggregator);
119+
120+
// Return value of getSenderAddress.
121+
error SenderAddressResult(address sender);
122+
123+
// UserOps handled, per aggregator.
124+
struct UserOpsPerAggregator {
125+
PackedUserOperation[] userOps;
126+
// Aggregator address
127+
IAggregator aggregator;
128+
// Aggregated signature
129+
bytes signature;
130+
}
131+
132+
/**
133+
* Execute a batch of UserOperation with Aggregators
134+
* @param opsPerAggregator - The operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts).
135+
* @param beneficiary - The address to receive the fees.
136+
*/
137+
function handleAggregatedOps(UserOpsPerAggregator[] calldata opsPerAggregator, address payable beneficiary)
138+
external;
139+
140+
/**
141+
* Generate a request Id - unique identifier for this request.
142+
* The request ID is a hash over the content of the userOp (except the signature), entrypoint address, chainId and (optionally) 7702 delegate address
143+
* @param userOp - The user operation to generate the request ID for.
144+
* @return hash the hash of this UserOperation
145+
*/
146+
function getUserOpHash(PackedUserOperation calldata userOp) external view returns (bytes32);
147+
148+
/**
149+
* Gas and return values during simulation.
150+
* @param preOpGas - The gas used for validation (including preValidationGas)
151+
* @param prefund - The required prefund for this operation
152+
* @param accountValidationData - returned validationData from account.
153+
* @param paymasterValidationData - return validationData from paymaster.
154+
* @param paymasterContext - Returned by validatePaymasterUserOp (to be passed into postOp)
155+
*/
156+
struct ReturnInfo {
157+
uint256 preOpGas;
158+
uint256 prefund;
159+
uint256 accountValidationData;
160+
uint256 paymasterValidationData;
161+
bytes paymasterContext;
162+
}
163+
164+
/**
165+
* Get counterfactual sender address.
166+
* Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
167+
* This method always revert, and returns the address in SenderAddressResult error.
168+
* @notice this method cannot be used for EIP-7702 derived contracts.
169+
*
170+
* @param initCode - The constructor code to be passed into the UserOperation.
171+
*/
172+
function getSenderAddress(bytes memory initCode) external;
173+
174+
error DelegateAndRevert(bool success, bytes ret);
175+
176+
/**
177+
* Helper method for dry-run testing.
178+
* @dev calling this method, the EntryPoint will make a delegatecall to the given data, and report (via revert) the result.
179+
* The method always revert, so is only useful off-chain for dry run calls, in cases where state-override to replace
180+
* actual EntryPoint code is less convenient.
181+
* @param target a target contract to make a delegatecall from entrypoint
182+
* @param data data to pass to target in a delegatecall
183+
*/
184+
function delegateAndRevert(address target, bytes calldata data) external;
185+
186+
/**
187+
* @notice Retrieves the immutable SenderCreator contract which is responsible for deployment of sender contracts.
188+
*/
189+
function senderCreator() external view returns (ISenderCreator);
190+
}

src/v08/SenderCreator.sol

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity ^0.8.28;
3+
/* solhint-disable avoid-low-level-calls */
4+
/* solhint-disable no-inline-assembly */
5+
6+
import "account-abstraction-v8/interfaces/ISenderCreator.sol";
7+
import "account-abstraction-v8/interfaces/IEntryPoint.sol";
8+
import "account-abstraction-v8/utils/Exec.sol";
9+
10+
/**
11+
* Helper contract for EntryPoint, to call userOp.initCode from a "neutral" address,
12+
* which is explicitly not the entryPoint itself.
13+
*/
14+
contract SenderCreator is ISenderCreator {
15+
address public immutable entryPoint;
16+
17+
constructor() {
18+
entryPoint = msg.sender;
19+
}
20+
21+
uint256 private constant REVERT_REASON_MAX_LEN = 2048;
22+
23+
/**
24+
* Call the "initCode" factory to create and return the sender account address.
25+
* @param initCode - The initCode value from a UserOp. contains 20 bytes of factory address,
26+
* followed by calldata.
27+
* @return sender - The returned address of the created account, or zero address on failure.
28+
*/
29+
function createSender(bytes calldata initCode) external returns (address sender) {
30+
// require(msg.sender == entryPoint, "AA97 should call from EntryPoint");
31+
address factory = address(bytes20(initCode[0:20]));
32+
33+
bytes memory initCallData = initCode[20:];
34+
bool success;
35+
assembly ("memory-safe") {
36+
success := call(gas(), factory, 0, add(initCallData, 0x20), mload(initCallData), 0, 32)
37+
if success { sender := mload(0) }
38+
}
39+
}
40+
41+
/// @inheritdoc ISenderCreator
42+
function initEip7702Sender(address sender, bytes memory initCallData) external {
43+
// require(msg.sender == entryPoint, "AA97 should call from EntryPoint");
44+
bool success;
45+
assembly ("memory-safe") {
46+
success := call(gas(), sender, 0, add(initCallData, 0x20), mload(initCallData), 0, 0)
47+
}
48+
if (!success) {
49+
bytes memory result = Exec.getReturnData(REVERT_REASON_MAX_LEN);
50+
revert IEntryPoint.FailedOpWithRevert(0, "AA13 EIP7702 sender init failed", result);
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)