@erc6900/reference-implementation 0.8.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @erc6900/reference-implementation might be problematic. Click here for more details.
- package/package.json +5 -26
- package/preinstall.js +110 -0
- package/LICENSE +0 -21
- package/README.md +0 -76
- package/src/account/AccountExecutor.sol +0 -20
- package/src/account/AccountFactory.sol +0 -126
- package/src/account/AccountStorage.sol +0 -99
- package/src/account/AccountStorageInitializable.sol +0 -67
- package/src/account/ModularAccountView.sol +0 -66
- package/src/account/ModuleManagerInternals.sol +0 -325
- package/src/account/ReferenceModularAccount.sol +0 -769
- package/src/account/SemiModularAccount.sol +0 -214
- package/src/account/SemiModularAccount7702.sol +0 -38
- package/src/helpers/CollectReturnData.sol +0 -14
- package/src/helpers/Constants.sol +0 -11
- package/src/helpers/EmptyCalldataSlice.sol +0 -12
- package/src/helpers/ValidationResHelpers.sol +0 -50
- package/src/interfaces/IERC6900Account.sol +0 -127
- package/src/interfaces/IERC6900AccountView.sol +0 -52
- package/src/interfaces/IERC6900ExecutionHookModule.sol +0 -26
- package/src/interfaces/IERC6900ExecutionModule.sol +0 -37
- package/src/interfaces/IERC6900Module.sol +0 -24
- package/src/interfaces/IERC6900ValidationHookModule.sol +0 -46
- package/src/interfaces/IERC6900ValidationModule.sol +0 -53
- package/src/libraries/HookConfigLib.sol +0 -135
- package/src/libraries/KnownSelectorsLib.sol +0 -65
- package/src/libraries/ModuleEntityLib.sol +0 -37
- package/src/libraries/ModuleStorageLib.sol +0 -62
- package/src/libraries/SparseCalldataSegmentLib.sol +0 -95
- package/src/libraries/ValidationConfigLib.sol +0 -119
- package/src/modules/BaseModule.sol +0 -54
- package/src/modules/ModuleEIP712.sol +0 -29
- package/src/modules/ReplaySafeWrapper.sol +0 -38
- package/src/modules/TokenReceiverModule.sol +0 -87
- package/src/modules/permissions/AllowlistModule.sol +0 -156
- package/src/modules/permissions/ERC20TokenLimitModule.sol +0 -145
- package/src/modules/permissions/NativeTokenLimitModule.sol +0 -154
- package/src/modules/validation/ISingleSignerValidationModule.sol +0 -22
- package/src/modules/validation/SingleSignerValidationModule.sol +0 -138
|
@@ -1,769 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity ^0.8.20;
|
|
3
|
-
|
|
4
|
-
import {BaseAccount} from "@eth-infinitism/account-abstraction/core/BaseAccount.sol";
|
|
5
|
-
import {IAccountExecute} from "@eth-infinitism/account-abstraction/interfaces/IAccountExecute.sol";
|
|
6
|
-
import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";
|
|
7
|
-
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
|
|
8
|
-
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
|
|
9
|
-
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
|
|
10
|
-
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
11
|
-
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
|
12
|
-
|
|
13
|
-
import {collectReturnData} from "../helpers/CollectReturnData.sol";
|
|
14
|
-
import {DIRECT_CALL_VALIDATION_ENTITY_ID} from "../helpers/Constants.sol";
|
|
15
|
-
import {_coalescePreValidation, _coalesceValidation} from "../helpers/ValidationResHelpers.sol";
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
Call,
|
|
19
|
-
IERC6900Account,
|
|
20
|
-
ModuleEntity,
|
|
21
|
-
ValidationConfig,
|
|
22
|
-
ValidationFlags
|
|
23
|
-
} from "../interfaces/IERC6900Account.sol";
|
|
24
|
-
import {IERC6900ExecutionHookModule} from "../interfaces/IERC6900ExecutionHookModule.sol";
|
|
25
|
-
import {ExecutionManifest} from "../interfaces/IERC6900ExecutionModule.sol";
|
|
26
|
-
import {IERC6900ValidationHookModule} from "../interfaces/IERC6900ValidationHookModule.sol";
|
|
27
|
-
import {IERC6900ValidationModule} from "../interfaces/IERC6900ValidationModule.sol";
|
|
28
|
-
import {HookConfig, HookConfigLib} from "../libraries/HookConfigLib.sol";
|
|
29
|
-
import {ModuleEntityLib} from "../libraries/ModuleEntityLib.sol";
|
|
30
|
-
import {SparseCalldataSegmentLib} from "../libraries/SparseCalldataSegmentLib.sol";
|
|
31
|
-
import {ValidationConfigLib} from "../libraries/ValidationConfigLib.sol";
|
|
32
|
-
import {AccountExecutor} from "./AccountExecutor.sol";
|
|
33
|
-
import {AccountStorage, getAccountStorage, toHookConfig, toSetValue} from "./AccountStorage.sol";
|
|
34
|
-
import {AccountStorageInitializable} from "./AccountStorageInitializable.sol";
|
|
35
|
-
import {ModularAccountView} from "./ModularAccountView.sol";
|
|
36
|
-
import {ModuleManagerInternals} from "./ModuleManagerInternals.sol";
|
|
37
|
-
|
|
38
|
-
contract ReferenceModularAccount is
|
|
39
|
-
IERC6900Account,
|
|
40
|
-
AccountExecutor,
|
|
41
|
-
ModularAccountView,
|
|
42
|
-
AccountStorageInitializable,
|
|
43
|
-
BaseAccount,
|
|
44
|
-
IERC165,
|
|
45
|
-
IERC1271,
|
|
46
|
-
IAccountExecute,
|
|
47
|
-
ModuleManagerInternals,
|
|
48
|
-
UUPSUpgradeable
|
|
49
|
-
{
|
|
50
|
-
using EnumerableSet for EnumerableSet.Bytes32Set;
|
|
51
|
-
using ModuleEntityLib for ModuleEntity;
|
|
52
|
-
using ValidationConfigLib for ValidationFlags;
|
|
53
|
-
using HookConfigLib for HookConfig;
|
|
54
|
-
using SparseCalldataSegmentLib for bytes;
|
|
55
|
-
|
|
56
|
-
struct PostExecToRun {
|
|
57
|
-
bytes preExecHookReturnData;
|
|
58
|
-
ModuleEntity postExecHook;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
enum ValidationCheckingType {
|
|
62
|
-
GLOBAL,
|
|
63
|
-
SELECTOR,
|
|
64
|
-
EITHER
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
IEntryPoint private immutable _ENTRY_POINT;
|
|
68
|
-
|
|
69
|
-
// As per the EIP-165 spec, no interface should ever match 0xffffffff
|
|
70
|
-
bytes4 internal constant _INTERFACE_ID_INVALID = 0xffffffff;
|
|
71
|
-
bytes4 internal constant _IERC165_INTERFACE_ID = 0x01ffc9a7;
|
|
72
|
-
|
|
73
|
-
// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
|
|
74
|
-
bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e;
|
|
75
|
-
bytes4 internal constant _1271_INVALID = 0xffffffff;
|
|
76
|
-
|
|
77
|
-
error NotEntryPoint();
|
|
78
|
-
error PostExecHookReverted(address module, uint32 entityId, bytes revertReason);
|
|
79
|
-
error PreExecHookReverted(address module, uint32 entityId, bytes revertReason);
|
|
80
|
-
error PreRuntimeValidationHookFailed(address module, uint32 entityId, bytes revertReason);
|
|
81
|
-
error RequireUserOperationContext();
|
|
82
|
-
error RuntimeValidationFunctionReverted(address module, uint32 entityId, bytes revertReason);
|
|
83
|
-
error SelfCallRecursionDepthExceeded();
|
|
84
|
-
error SignatureValidationInvalid(address module, uint32 entityId);
|
|
85
|
-
error UserOpValidationInvalid(address module, uint32 entityId);
|
|
86
|
-
error UnexpectedAggregator(address module, uint32 entityId, address aggregator);
|
|
87
|
-
error UnrecognizedFunction(bytes4 selector);
|
|
88
|
-
error ValidationFunctionMissing(bytes4 selector);
|
|
89
|
-
|
|
90
|
-
// Wraps execution of a native function with runtime validation and hooks
|
|
91
|
-
// Used for upgradeTo, upgradeToAndCall, execute, executeBatch, installExecution, uninstallExecution
|
|
92
|
-
modifier wrapNativeFunction() {
|
|
93
|
-
(PostExecToRun[] memory postValidatorExecHooks, PostExecToRun[] memory postSelectorExecHooks) =
|
|
94
|
-
_checkPermittedCallerAndAssociatedHooks();
|
|
95
|
-
|
|
96
|
-
_;
|
|
97
|
-
|
|
98
|
-
_doCachedPostExecHooks(postSelectorExecHooks);
|
|
99
|
-
_doCachedPostExecHooks(postValidatorExecHooks);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
constructor(IEntryPoint anEntryPoint) {
|
|
103
|
-
_ENTRY_POINT = anEntryPoint;
|
|
104
|
-
_disableInitializers();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// EXTERNAL FUNCTIONS
|
|
108
|
-
|
|
109
|
-
receive() external payable {}
|
|
110
|
-
|
|
111
|
-
/// @notice Fallback function
|
|
112
|
-
/// @dev We route calls to execution functions based on incoming msg.sig
|
|
113
|
-
/// @dev If there's no module associated with this function selector, revert
|
|
114
|
-
fallback(bytes calldata) external payable returns (bytes memory) {
|
|
115
|
-
address execModule = getAccountStorage().executionStorage[msg.sig].module;
|
|
116
|
-
if (execModule == address(0)) {
|
|
117
|
-
revert UnrecognizedFunction(msg.sig);
|
|
118
|
-
}
|
|
119
|
-
(PostExecToRun[] memory postValidatorExecHooks, PostExecToRun[] memory postSelectorExecHooks) =
|
|
120
|
-
_checkPermittedCallerAndAssociatedHooks();
|
|
121
|
-
|
|
122
|
-
// execute the function, bubbling up any reverts
|
|
123
|
-
(bool execSuccess, bytes memory execReturnData) = execModule.call(msg.data);
|
|
124
|
-
|
|
125
|
-
if (!execSuccess) {
|
|
126
|
-
// Bubble up revert reasons from modules
|
|
127
|
-
assembly ("memory-safe") {
|
|
128
|
-
revert(add(execReturnData, 32), mload(execReturnData))
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
_doCachedPostExecHooks(postSelectorExecHooks);
|
|
133
|
-
_doCachedPostExecHooks(postValidatorExecHooks);
|
|
134
|
-
|
|
135
|
-
return execReturnData;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/// @inheritdoc IAccountExecute
|
|
139
|
-
/// @notice Execution function that allows UO context to be passed to execution hooks
|
|
140
|
-
/// @dev This function is only callable by the EntryPoint
|
|
141
|
-
function executeUserOp(PackedUserOperation calldata userOp, bytes32) external override {
|
|
142
|
-
if (msg.sender != address(_ENTRY_POINT)) {
|
|
143
|
-
revert NotEntryPoint();
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
ModuleEntity userOpValidationFunction = ModuleEntity.wrap(bytes24(userOp.signature[:24]));
|
|
147
|
-
|
|
148
|
-
PostExecToRun[] memory postValidatorExecHooks =
|
|
149
|
-
_doPreHooks(getAccountStorage().validationStorage[userOpValidationFunction].executionHooks, msg.data);
|
|
150
|
-
|
|
151
|
-
(bool success, bytes memory result) = address(this).call(userOp.callData[4:]);
|
|
152
|
-
|
|
153
|
-
if (!success) {
|
|
154
|
-
// Directly bubble up revert messages
|
|
155
|
-
assembly ("memory-safe") {
|
|
156
|
-
revert(add(result, 32), mload(result))
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
_doCachedPostExecHooks(postValidatorExecHooks);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/// @inheritdoc IERC6900Account
|
|
164
|
-
/// @notice May be validated by a global validation.
|
|
165
|
-
function execute(address target, uint256 value, bytes calldata data)
|
|
166
|
-
external
|
|
167
|
-
payable
|
|
168
|
-
override
|
|
169
|
-
wrapNativeFunction
|
|
170
|
-
returns (bytes memory result)
|
|
171
|
-
{
|
|
172
|
-
result = _exec(target, value, data);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/// @inheritdoc IERC6900Account
|
|
176
|
-
/// @notice May be validated by a global validation function.
|
|
177
|
-
function executeBatch(Call[] calldata calls)
|
|
178
|
-
external
|
|
179
|
-
payable
|
|
180
|
-
override
|
|
181
|
-
wrapNativeFunction
|
|
182
|
-
returns (bytes[] memory results)
|
|
183
|
-
{
|
|
184
|
-
uint256 callsLength = calls.length;
|
|
185
|
-
results = new bytes[](callsLength);
|
|
186
|
-
|
|
187
|
-
for (uint256 i = 0; i < callsLength; ++i) {
|
|
188
|
-
results[i] = _exec(calls[i].target, calls[i].value, calls[i].data);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/// @inheritdoc IERC6900Account
|
|
193
|
-
function executeWithRuntimeValidation(bytes calldata data, bytes calldata authorization)
|
|
194
|
-
external
|
|
195
|
-
payable
|
|
196
|
-
returns (bytes memory)
|
|
197
|
-
{
|
|
198
|
-
// Revert if the provided `authorization` less than 21 bytes long, rather than right-padding.
|
|
199
|
-
ModuleEntity runtimeValidationFunction = ModuleEntity.wrap(bytes24(authorization[:24]));
|
|
200
|
-
|
|
201
|
-
// Check if the runtime validation function is allowed to be called
|
|
202
|
-
bool isGlobalValidation = uint8(authorization[24]) == 1;
|
|
203
|
-
_checkIfValidationAppliesCallData(
|
|
204
|
-
data,
|
|
205
|
-
runtimeValidationFunction,
|
|
206
|
-
isGlobalValidation ? ValidationCheckingType.GLOBAL : ValidationCheckingType.SELECTOR
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
_doRuntimeValidation(runtimeValidationFunction, data, authorization[25:]);
|
|
210
|
-
|
|
211
|
-
// If runtime validation passes, run exec hooks associated with the validator
|
|
212
|
-
PostExecToRun[] memory postValidatorExecHooks =
|
|
213
|
-
_doPreHooks(getAccountStorage().validationStorage[runtimeValidationFunction].executionHooks, data);
|
|
214
|
-
|
|
215
|
-
// Execute the call
|
|
216
|
-
(bool success, bytes memory returnData) = address(this).call(data);
|
|
217
|
-
|
|
218
|
-
if (!success) {
|
|
219
|
-
assembly ("memory-safe") {
|
|
220
|
-
revert(add(returnData, 32), mload(returnData))
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
_doCachedPostExecHooks(postValidatorExecHooks);
|
|
225
|
-
|
|
226
|
-
return returnData;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/// @inheritdoc IERC6900Account
|
|
230
|
-
/// @notice May be validated by a global validation.
|
|
231
|
-
function installExecution(
|
|
232
|
-
address module,
|
|
233
|
-
ExecutionManifest calldata manifest,
|
|
234
|
-
bytes calldata moduleInstallData
|
|
235
|
-
) external override wrapNativeFunction {
|
|
236
|
-
_installExecution(module, manifest, moduleInstallData);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/// @inheritdoc IERC6900Account
|
|
240
|
-
/// @notice May be validated by a global validation.
|
|
241
|
-
function uninstallExecution(
|
|
242
|
-
address module,
|
|
243
|
-
ExecutionManifest calldata manifest,
|
|
244
|
-
bytes calldata moduleUninstallData
|
|
245
|
-
) external override wrapNativeFunction {
|
|
246
|
-
_uninstallExecution(module, manifest, moduleUninstallData);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/// @notice Initializes the account with a validation function added to the global pool.
|
|
250
|
-
/// @dev This function is only callable once.
|
|
251
|
-
function initializeWithValidation(
|
|
252
|
-
ValidationConfig validationConfig,
|
|
253
|
-
bytes4[] calldata selectors,
|
|
254
|
-
bytes calldata installData,
|
|
255
|
-
bytes[] calldata hooks
|
|
256
|
-
) external virtual initializer {
|
|
257
|
-
_installValidation(validationConfig, selectors, installData, hooks);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/// @inheritdoc IERC6900Account
|
|
261
|
-
/// @notice May be validated by a global validation.
|
|
262
|
-
/// @dev This function can be used to update (to a certain degree) previously installed validation functions.
|
|
263
|
-
/// - preValidationHook, executionHooks, and selectors can be added later. Though they won't be deleted.
|
|
264
|
-
/// - isGlobal and isSignatureValidation can also be updated later.
|
|
265
|
-
function installValidation(
|
|
266
|
-
ValidationConfig validationConfig,
|
|
267
|
-
bytes4[] calldata selectors,
|
|
268
|
-
bytes calldata installData,
|
|
269
|
-
bytes[] calldata hooks
|
|
270
|
-
) external wrapNativeFunction {
|
|
271
|
-
_installValidation(validationConfig, selectors, installData, hooks);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/// @inheritdoc IERC6900Account
|
|
275
|
-
/// @notice May be validated by a global validation.
|
|
276
|
-
function uninstallValidation(
|
|
277
|
-
ModuleEntity validationFunction,
|
|
278
|
-
bytes calldata uninstallData,
|
|
279
|
-
bytes[] calldata hookUninstallData
|
|
280
|
-
) external wrapNativeFunction {
|
|
281
|
-
_uninstallValidation(validationFunction, uninstallData, hookUninstallData);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/// @notice ERC165 introspection
|
|
285
|
-
/// @dev returns true for `IERC165.interfaceId` and false for `0xFFFFFFFF`
|
|
286
|
-
/// @param interfaceId interface id to check against
|
|
287
|
-
/// @return bool support for specific interface
|
|
288
|
-
function supportsInterface(bytes4 interfaceId) external view override returns (bool) {
|
|
289
|
-
if (interfaceId == _INTERFACE_ID_INVALID) {
|
|
290
|
-
return false;
|
|
291
|
-
}
|
|
292
|
-
if (interfaceId == _IERC165_INTERFACE_ID) {
|
|
293
|
-
return true;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return getAccountStorage().supportedIfaces[interfaceId] > 0;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/// @inheritdoc IERC6900Account
|
|
300
|
-
function accountId() external pure virtual returns (string memory) {
|
|
301
|
-
return "erc6900.reference-modular-account.0.8.0";
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/// @inheritdoc UUPSUpgradeable
|
|
305
|
-
/// @notice May be validated by a global validation.
|
|
306
|
-
function upgradeToAndCall(address newImplementation, bytes memory data)
|
|
307
|
-
public
|
|
308
|
-
payable
|
|
309
|
-
virtual
|
|
310
|
-
override
|
|
311
|
-
onlyProxy
|
|
312
|
-
wrapNativeFunction
|
|
313
|
-
{
|
|
314
|
-
super.upgradeToAndCall(newImplementation, data);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function isValidSignature(bytes32 hash, bytes calldata signature) public view override returns (bytes4) {
|
|
318
|
-
ModuleEntity sigValidation = ModuleEntity.wrap(bytes24(signature));
|
|
319
|
-
signature = signature[24:];
|
|
320
|
-
|
|
321
|
-
HookConfig[] memory preSignatureValidationHooks =
|
|
322
|
-
getAccountStorage().validationStorage[sigValidation].validationHooks;
|
|
323
|
-
|
|
324
|
-
for (uint256 i = 0; i < preSignatureValidationHooks.length; ++i) {
|
|
325
|
-
(address hookModule, uint32 hookEntityId) = preSignatureValidationHooks[i].moduleEntity().unpack();
|
|
326
|
-
|
|
327
|
-
bytes memory currentSignatureSegment;
|
|
328
|
-
|
|
329
|
-
(currentSignatureSegment, signature) = signature.advanceSegmentIfAtIndex(uint8(i));
|
|
330
|
-
|
|
331
|
-
// If this reverts, bubble up revert reason.
|
|
332
|
-
IERC6900ValidationHookModule(hookModule).preSignatureValidationHook(
|
|
333
|
-
hookEntityId, msg.sender, hash, currentSignatureSegment
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
signature = signature.getFinalSegment();
|
|
338
|
-
|
|
339
|
-
return _exec1271Validation(sigValidation, hash, signature);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/// @notice Gets the entry point for this account
|
|
343
|
-
/// @return entryPoint The entry point for this account
|
|
344
|
-
function entryPoint() public view override returns (IEntryPoint) {
|
|
345
|
-
return _ENTRY_POINT;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// INTERNAL FUNCTIONS
|
|
349
|
-
|
|
350
|
-
// Parent function validateUserOp enforces that this call can only be made by the EntryPoint
|
|
351
|
-
function _validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
|
|
352
|
-
internal
|
|
353
|
-
override
|
|
354
|
-
returns (uint256 validationData)
|
|
355
|
-
{
|
|
356
|
-
if (userOp.callData.length < 4) {
|
|
357
|
-
revert UnrecognizedFunction(bytes4(userOp.callData));
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Revert if the provided `authorization` less than 21 bytes long, rather than right-padding.
|
|
361
|
-
ModuleEntity userOpValidationFunction = ModuleEntity.wrap(bytes24(userOp.signature[:24]));
|
|
362
|
-
bool isGlobalValidation = uint8(userOp.signature[24]) == 1;
|
|
363
|
-
|
|
364
|
-
_checkIfValidationAppliesCallData(
|
|
365
|
-
userOp.callData,
|
|
366
|
-
userOpValidationFunction,
|
|
367
|
-
isGlobalValidation ? ValidationCheckingType.GLOBAL : ValidationCheckingType.SELECTOR
|
|
368
|
-
);
|
|
369
|
-
|
|
370
|
-
// Check if there are execution hooks associated with the validator, and revert if the call isn't to
|
|
371
|
-
// `executeUserOp`
|
|
372
|
-
// This check must be here because if context isn't passed, we can't tell in execution which hooks should
|
|
373
|
-
// have ran
|
|
374
|
-
if (
|
|
375
|
-
getAccountStorage().validationStorage[userOpValidationFunction].executionHooks.length() > 0
|
|
376
|
-
&& bytes4(userOp.callData[:4]) != this.executeUserOp.selector
|
|
377
|
-
) {
|
|
378
|
-
revert RequireUserOperationContext();
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
validationData = _doUserOpValidation(userOpValidationFunction, userOp, userOp.signature[25:], userOpHash);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// To support gas estimation, we don't fail early when the failure is caused by a signature failure
|
|
385
|
-
function _doUserOpValidation(
|
|
386
|
-
ModuleEntity userOpValidationFunction,
|
|
387
|
-
PackedUserOperation memory userOp,
|
|
388
|
-
bytes calldata signature,
|
|
389
|
-
bytes32 userOpHash
|
|
390
|
-
) internal returns (uint256) {
|
|
391
|
-
uint256 validationRes;
|
|
392
|
-
|
|
393
|
-
// Do preUserOpValidation hooks
|
|
394
|
-
HookConfig[] memory preUserOpValidationHooks =
|
|
395
|
-
getAccountStorage().validationStorage[userOpValidationFunction].validationHooks;
|
|
396
|
-
|
|
397
|
-
for (uint256 i = 0; i < preUserOpValidationHooks.length; ++i) {
|
|
398
|
-
(userOp.signature, signature) = signature.advanceSegmentIfAtIndex(uint8(i));
|
|
399
|
-
|
|
400
|
-
(address module, uint32 entityId) = preUserOpValidationHooks[i].moduleEntity().unpack();
|
|
401
|
-
uint256 currentValidationRes =
|
|
402
|
-
IERC6900ValidationHookModule(module).preUserOpValidationHook(entityId, userOp, userOpHash);
|
|
403
|
-
|
|
404
|
-
if (uint160(currentValidationRes) > 1) {
|
|
405
|
-
// If the aggregator is not 0 or 1, it is an unexpected value
|
|
406
|
-
revert UnexpectedAggregator(module, entityId, address(uint160(currentValidationRes)));
|
|
407
|
-
}
|
|
408
|
-
validationRes = _coalescePreValidation(validationRes, currentValidationRes);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Run the user op validation function
|
|
412
|
-
{
|
|
413
|
-
userOp.signature = signature.getFinalSegment();
|
|
414
|
-
|
|
415
|
-
uint256 currentValidationRes = _execUserOpValidation(userOpValidationFunction, userOp, userOpHash);
|
|
416
|
-
|
|
417
|
-
if (preUserOpValidationHooks.length != 0) {
|
|
418
|
-
// If we have other validation data we need to coalesce with
|
|
419
|
-
validationRes = _coalesceValidation(validationRes, currentValidationRes);
|
|
420
|
-
} else {
|
|
421
|
-
validationRes = currentValidationRes;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
return validationRes;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
function _doRuntimeValidation(
|
|
429
|
-
ModuleEntity runtimeValidationFunction,
|
|
430
|
-
bytes calldata callData,
|
|
431
|
-
bytes calldata authorizationData
|
|
432
|
-
) internal {
|
|
433
|
-
// run all preRuntimeValidation hooks
|
|
434
|
-
HookConfig[] memory preRuntimeValidationHooks =
|
|
435
|
-
getAccountStorage().validationStorage[runtimeValidationFunction].validationHooks;
|
|
436
|
-
|
|
437
|
-
for (uint256 i = 0; i < preRuntimeValidationHooks.length; ++i) {
|
|
438
|
-
bytes memory currentAuthSegment;
|
|
439
|
-
|
|
440
|
-
(currentAuthSegment, authorizationData) = authorizationData.advanceSegmentIfAtIndex(uint8(i));
|
|
441
|
-
|
|
442
|
-
_doPreRuntimeValidationHook(preRuntimeValidationHooks[i].moduleEntity(), callData, currentAuthSegment);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
authorizationData = authorizationData.getFinalSegment();
|
|
446
|
-
|
|
447
|
-
_execRuntimeValidation(runtimeValidationFunction, callData, authorizationData);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
function _doPreHooks(EnumerableSet.Bytes32Set storage executionHooks, bytes memory data)
|
|
451
|
-
internal
|
|
452
|
-
returns (PostExecToRun[] memory postHooksToRun)
|
|
453
|
-
{
|
|
454
|
-
uint256 hooksLength = executionHooks.length();
|
|
455
|
-
// Overallocate on length - not all of this may get filled up. We set the correct length later.
|
|
456
|
-
postHooksToRun = new PostExecToRun[](hooksLength);
|
|
457
|
-
|
|
458
|
-
// Copy all post hooks to the array. This happens before any pre hooks are run, so we can
|
|
459
|
-
// be sure that the set of hooks to run will not be affected by state changes mid-execution.
|
|
460
|
-
for (uint256 i = 0; i < hooksLength; ++i) {
|
|
461
|
-
HookConfig hookConfig = toHookConfig(executionHooks.at(i));
|
|
462
|
-
if (hookConfig.hasPostHook()) {
|
|
463
|
-
postHooksToRun[i].postExecHook = hookConfig.moduleEntity();
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// Run the pre hooks and copy their return data to the post hooks array, if an associated post exec hook
|
|
468
|
-
// exists.
|
|
469
|
-
for (uint256 i = 0; i < hooksLength; ++i) {
|
|
470
|
-
HookConfig hookConfig = toHookConfig(executionHooks.at(i));
|
|
471
|
-
|
|
472
|
-
if (hookConfig.hasPreHook()) {
|
|
473
|
-
bytes memory preExecHookReturnData;
|
|
474
|
-
|
|
475
|
-
preExecHookReturnData = _runPreExecHook(hookConfig.moduleEntity(), data);
|
|
476
|
-
|
|
477
|
-
// If there is an associated post exec hook, save the return data.
|
|
478
|
-
if (hookConfig.hasPostHook()) {
|
|
479
|
-
postHooksToRun[i].preExecHookReturnData = preExecHookReturnData;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
function _runPreExecHook(ModuleEntity preExecHook, bytes memory data)
|
|
486
|
-
internal
|
|
487
|
-
returns (bytes memory preExecHookReturnData)
|
|
488
|
-
{
|
|
489
|
-
(address module, uint32 entityId) = preExecHook.unpack();
|
|
490
|
-
try IERC6900ExecutionHookModule(module).preExecutionHook(entityId, msg.sender, msg.value, data) returns (
|
|
491
|
-
bytes memory returnData
|
|
492
|
-
) {
|
|
493
|
-
preExecHookReturnData = returnData;
|
|
494
|
-
} catch {
|
|
495
|
-
bytes memory revertReason = collectReturnData();
|
|
496
|
-
revert PreExecHookReverted(module, entityId, revertReason);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/// @dev Associated post hooks are run in reverse order of their pre hooks.
|
|
501
|
-
function _doCachedPostExecHooks(PostExecToRun[] memory postHooksToRun) internal {
|
|
502
|
-
uint256 postHooksToRunLength = postHooksToRun.length;
|
|
503
|
-
for (uint256 i = postHooksToRunLength; i > 0;) {
|
|
504
|
-
// Decrement here, instead of in the loop body, to handle the case where length is 0.
|
|
505
|
-
--i;
|
|
506
|
-
|
|
507
|
-
PostExecToRun memory postHookToRun = postHooksToRun[i];
|
|
508
|
-
|
|
509
|
-
if (postHookToRun.postExecHook.isEmpty()) {
|
|
510
|
-
// This is an empty post hook, from a pre-only hook, so we skip it.
|
|
511
|
-
continue;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
(address module, uint32 entityId) = postHookToRun.postExecHook.unpack();
|
|
515
|
-
/* solhint-disable no-empty-blocks */
|
|
516
|
-
try IERC6900ExecutionHookModule(module).postExecutionHook(
|
|
517
|
-
entityId, postHookToRun.preExecHookReturnData
|
|
518
|
-
) {} catch {
|
|
519
|
-
bytes memory revertReason = collectReturnData();
|
|
520
|
-
revert PostExecHookReverted(module, entityId, revertReason);
|
|
521
|
-
}
|
|
522
|
-
/* solhint-enable no-empty-blocks */
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
function _doPreRuntimeValidationHook(
|
|
527
|
-
ModuleEntity validationHook,
|
|
528
|
-
bytes memory callData,
|
|
529
|
-
bytes memory currentAuthData
|
|
530
|
-
) internal {
|
|
531
|
-
(address hookModule, uint32 hookEntityId) = validationHook.unpack();
|
|
532
|
-
try IERC6900ValidationHookModule(hookModule).preRuntimeValidationHook(
|
|
533
|
-
hookEntityId, msg.sender, msg.value, callData, currentAuthData
|
|
534
|
-
)
|
|
535
|
-
// forgefmt: disable-start
|
|
536
|
-
// solhint-disable-next-line no-empty-blocks
|
|
537
|
-
{} catch{
|
|
538
|
-
// forgefmt: disable-end
|
|
539
|
-
bytes memory revertReason = collectReturnData();
|
|
540
|
-
revert PreRuntimeValidationHookFailed(hookModule, hookEntityId, revertReason);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// solhint-disable-next-line no-empty-blocks
|
|
545
|
-
function _authorizeUpgrade(address newImplementation) internal override {}
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* Order of operations:
|
|
549
|
-
* 1. Check if the sender is the entry point, the account itself, or the selector called is public.
|
|
550
|
-
* - Yes: Return an empty array, there are no post executionHooks.
|
|
551
|
-
* - No: Continue
|
|
552
|
-
* 2. Check if the called selector (msg.sig) is included in the set of selectors the msg.sender can
|
|
553
|
-
* directly call.
|
|
554
|
-
* - Yes: Continue
|
|
555
|
-
* - No: Revert, the caller is not allowed to call this selector
|
|
556
|
-
* 3. If there are runtime validation hooks associated with this caller-sig combination, run them.
|
|
557
|
-
* 4. Run the pre executionHooks associated with this caller-sig combination, and return the
|
|
558
|
-
* post executionHooks to run later.
|
|
559
|
-
*/
|
|
560
|
-
function _checkPermittedCallerAndAssociatedHooks()
|
|
561
|
-
internal
|
|
562
|
-
returns (PostExecToRun[] memory, PostExecToRun[] memory)
|
|
563
|
-
{
|
|
564
|
-
AccountStorage storage _storage = getAccountStorage();
|
|
565
|
-
PostExecToRun[] memory postValidatorExecutionHooks;
|
|
566
|
-
|
|
567
|
-
// We only need to handle execution hooks when the sender is not the entry point or the account itself,
|
|
568
|
-
// and the selector isn't public.
|
|
569
|
-
if (
|
|
570
|
-
msg.sender != address(_ENTRY_POINT) && msg.sender != address(this)
|
|
571
|
-
&& !_storage.executionStorage[msg.sig].skipRuntimeValidation
|
|
572
|
-
) {
|
|
573
|
-
ModuleEntity directCallValidationKey =
|
|
574
|
-
ModuleEntityLib.pack(msg.sender, DIRECT_CALL_VALIDATION_ENTITY_ID);
|
|
575
|
-
|
|
576
|
-
_checkIfValidationAppliesCallData(msg.data, directCallValidationKey, ValidationCheckingType.EITHER);
|
|
577
|
-
|
|
578
|
-
// Direct call is allowed, run associated execution & validation hooks
|
|
579
|
-
|
|
580
|
-
// Validation hooks
|
|
581
|
-
HookConfig[] memory preRuntimeValidationHooks =
|
|
582
|
-
_storage.validationStorage[directCallValidationKey].validationHooks;
|
|
583
|
-
|
|
584
|
-
uint256 hookLen = preRuntimeValidationHooks.length;
|
|
585
|
-
for (uint256 i = 0; i < hookLen; ++i) {
|
|
586
|
-
_doPreRuntimeValidationHook(preRuntimeValidationHooks[i].moduleEntity(), msg.data, "");
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// Execution hooks associated with the validator
|
|
590
|
-
postValidatorExecutionHooks =
|
|
591
|
-
_doPreHooks(_storage.validationStorage[directCallValidationKey].executionHooks, msg.data);
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// Exec hooks associated with the selector
|
|
595
|
-
PostExecToRun[] memory postSelectorExecutionHooks =
|
|
596
|
-
_doPreHooks(_storage.executionStorage[msg.sig].executionHooks, msg.data);
|
|
597
|
-
|
|
598
|
-
return (postValidatorExecutionHooks, postSelectorExecutionHooks);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function _execUserOpValidation(
|
|
602
|
-
ModuleEntity userOpValidationFunction,
|
|
603
|
-
PackedUserOperation memory userOp,
|
|
604
|
-
bytes32 userOpHash
|
|
605
|
-
) internal virtual returns (uint256) {
|
|
606
|
-
AccountStorage storage _storage = getAccountStorage();
|
|
607
|
-
|
|
608
|
-
(address module, uint32 entityId) = userOpValidationFunction.unpack();
|
|
609
|
-
|
|
610
|
-
if (!_storage.validationStorage[userOpValidationFunction].validationFlags.isUserOpValidation()) {
|
|
611
|
-
revert UserOpValidationInvalid(module, entityId);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
return IERC6900ValidationModule(module).validateUserOp(entityId, userOp, userOpHash);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function _execRuntimeValidation(
|
|
618
|
-
ModuleEntity runtimeValidationFunction,
|
|
619
|
-
bytes calldata callData,
|
|
620
|
-
bytes calldata authorization
|
|
621
|
-
) internal virtual {
|
|
622
|
-
(address module, uint32 entityId) = runtimeValidationFunction.unpack();
|
|
623
|
-
|
|
624
|
-
try IERC6900ValidationModule(module).validateRuntime(
|
|
625
|
-
address(this), entityId, msg.sender, msg.value, callData, authorization
|
|
626
|
-
)
|
|
627
|
-
// forgefmt: disable-start
|
|
628
|
-
// solhint-disable-next-line no-empty-blocks
|
|
629
|
-
{} catch{
|
|
630
|
-
// forgefmt: disable-end
|
|
631
|
-
bytes memory revertReason = collectReturnData();
|
|
632
|
-
revert RuntimeValidationFunctionReverted(module, entityId, revertReason);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function _exec1271Validation(ModuleEntity sigValidation, bytes32 hash, bytes calldata signature)
|
|
637
|
-
internal
|
|
638
|
-
view
|
|
639
|
-
virtual
|
|
640
|
-
returns (bytes4)
|
|
641
|
-
{
|
|
642
|
-
AccountStorage storage _storage = getAccountStorage();
|
|
643
|
-
|
|
644
|
-
(address module, uint32 entityId) = sigValidation.unpack();
|
|
645
|
-
if (!_storage.validationStorage[sigValidation].validationFlags.isSignatureValidation()) {
|
|
646
|
-
revert SignatureValidationInvalid(module, entityId);
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
if (
|
|
650
|
-
IERC6900ValidationModule(module).validateSignature(
|
|
651
|
-
address(this), entityId, msg.sender, hash, signature
|
|
652
|
-
) == _1271_MAGIC_VALUE
|
|
653
|
-
) {
|
|
654
|
-
return _1271_MAGIC_VALUE;
|
|
655
|
-
}
|
|
656
|
-
return _1271_INVALID;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
function _globalValidationAllowed(bytes4 selector) internal view virtual returns (bool) {
|
|
660
|
-
if (
|
|
661
|
-
selector == this.execute.selector || selector == this.executeBatch.selector
|
|
662
|
-
|| selector == this.installExecution.selector || selector == this.uninstallExecution.selector
|
|
663
|
-
|| selector == this.installValidation.selector || selector == this.uninstallValidation.selector
|
|
664
|
-
|| selector == this.upgradeToAndCall.selector
|
|
665
|
-
) {
|
|
666
|
-
return true;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
return getAccountStorage().executionStorage[selector].allowGlobalValidation;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
function _isValidationGlobal(ModuleEntity validationFunction) internal view virtual returns (bool) {
|
|
673
|
-
return getAccountStorage().validationStorage[validationFunction].validationFlags.isGlobal();
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
function _checkIfValidationAppliesCallData(
|
|
677
|
-
bytes calldata callData,
|
|
678
|
-
ModuleEntity validationFunction,
|
|
679
|
-
ValidationCheckingType checkingType
|
|
680
|
-
) internal view {
|
|
681
|
-
bytes4 outerSelector = bytes4(callData[:4]);
|
|
682
|
-
if (outerSelector == this.executeUserOp.selector) {
|
|
683
|
-
// If the selector is executeUserOp, pull the actual selector from the following data,
|
|
684
|
-
// and trim the calldata to ensure the self-call decoding is still accurate.
|
|
685
|
-
callData = callData[4:];
|
|
686
|
-
outerSelector = bytes4(callData[:4]);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
_checkIfValidationAppliesSelector(outerSelector, validationFunction, checkingType);
|
|
690
|
-
|
|
691
|
-
if (outerSelector == IERC6900Account.execute.selector) {
|
|
692
|
-
(address target,,) = abi.decode(callData[4:], (address, uint256, bytes));
|
|
693
|
-
|
|
694
|
-
if (target == address(this)) {
|
|
695
|
-
// There is no point to call `execute` to recurse exactly once - this is equivalent to just having
|
|
696
|
-
// the calldata as a top-level call.
|
|
697
|
-
revert SelfCallRecursionDepthExceeded();
|
|
698
|
-
}
|
|
699
|
-
} else if (outerSelector == IERC6900Account.executeBatch.selector) {
|
|
700
|
-
// executeBatch may be used to batch account actions together, by targetting the account itself.
|
|
701
|
-
// If this is done, we must ensure all of the inner calls are allowed by the provided validation
|
|
702
|
-
// function.
|
|
703
|
-
|
|
704
|
-
(Call[] memory calls) = abi.decode(callData[4:], (Call[]));
|
|
705
|
-
|
|
706
|
-
for (uint256 i = 0; i < calls.length; ++i) {
|
|
707
|
-
if (calls[i].target == address(this)) {
|
|
708
|
-
bytes4 nestedSelector = bytes4(calls[i].data);
|
|
709
|
-
|
|
710
|
-
if (
|
|
711
|
-
nestedSelector == IERC6900Account.execute.selector
|
|
712
|
-
|| nestedSelector == IERC6900Account.executeBatch.selector
|
|
713
|
-
) {
|
|
714
|
-
// To prevent arbitrarily-deep recursive checking, we limit the depth of self-calls to one
|
|
715
|
-
// for the purposes of batching.
|
|
716
|
-
// This means that all self-calls must occur at the top level of the batch.
|
|
717
|
-
// Note that modules of other contracts using `executeWithRuntimeValidation` may still
|
|
718
|
-
// independently call into this account with a different validation function, allowing
|
|
719
|
-
// composition of multiple batches.
|
|
720
|
-
revert SelfCallRecursionDepthExceeded();
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
_checkIfValidationAppliesSelector(nestedSelector, validationFunction, checkingType);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
function _checkIfValidationAppliesSelector(
|
|
730
|
-
bytes4 selector,
|
|
731
|
-
ModuleEntity validationFunction,
|
|
732
|
-
ValidationCheckingType checkingType
|
|
733
|
-
) internal view {
|
|
734
|
-
// Check that the provided validation function is applicable to the selector
|
|
735
|
-
|
|
736
|
-
if (checkingType == ValidationCheckingType.GLOBAL) {
|
|
737
|
-
if (!_globalValidationApplies(selector, validationFunction)) {
|
|
738
|
-
revert ValidationFunctionMissing(selector);
|
|
739
|
-
}
|
|
740
|
-
} else if (checkingType == ValidationCheckingType.SELECTOR) {
|
|
741
|
-
if (!_selectorValidationApplies(selector, validationFunction)) {
|
|
742
|
-
revert ValidationFunctionMissing(selector);
|
|
743
|
-
}
|
|
744
|
-
} else {
|
|
745
|
-
if (
|
|
746
|
-
!_globalValidationApplies(selector, validationFunction)
|
|
747
|
-
&& !_selectorValidationApplies(selector, validationFunction)
|
|
748
|
-
) {
|
|
749
|
-
revert ValidationFunctionMissing(selector);
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
function _globalValidationApplies(bytes4 selector, ModuleEntity validationFunction)
|
|
755
|
-
internal
|
|
756
|
-
view
|
|
757
|
-
returns (bool)
|
|
758
|
-
{
|
|
759
|
-
return _globalValidationAllowed(selector) && _isValidationGlobal(validationFunction);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
function _selectorValidationApplies(bytes4 selector, ModuleEntity validationFunction)
|
|
763
|
-
internal
|
|
764
|
-
view
|
|
765
|
-
returns (bool)
|
|
766
|
-
{
|
|
767
|
-
return getAccountStorage().validationStorage[validationFunction].selectors.contains(toSetValue(selector));
|
|
768
|
-
}
|
|
769
|
-
}
|