@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.

Files changed (39) hide show
  1. package/package.json +5 -26
  2. package/preinstall.js +110 -0
  3. package/LICENSE +0 -21
  4. package/README.md +0 -76
  5. package/src/account/AccountExecutor.sol +0 -20
  6. package/src/account/AccountFactory.sol +0 -126
  7. package/src/account/AccountStorage.sol +0 -99
  8. package/src/account/AccountStorageInitializable.sol +0 -67
  9. package/src/account/ModularAccountView.sol +0 -66
  10. package/src/account/ModuleManagerInternals.sol +0 -325
  11. package/src/account/ReferenceModularAccount.sol +0 -769
  12. package/src/account/SemiModularAccount.sol +0 -214
  13. package/src/account/SemiModularAccount7702.sol +0 -38
  14. package/src/helpers/CollectReturnData.sol +0 -14
  15. package/src/helpers/Constants.sol +0 -11
  16. package/src/helpers/EmptyCalldataSlice.sol +0 -12
  17. package/src/helpers/ValidationResHelpers.sol +0 -50
  18. package/src/interfaces/IERC6900Account.sol +0 -127
  19. package/src/interfaces/IERC6900AccountView.sol +0 -52
  20. package/src/interfaces/IERC6900ExecutionHookModule.sol +0 -26
  21. package/src/interfaces/IERC6900ExecutionModule.sol +0 -37
  22. package/src/interfaces/IERC6900Module.sol +0 -24
  23. package/src/interfaces/IERC6900ValidationHookModule.sol +0 -46
  24. package/src/interfaces/IERC6900ValidationModule.sol +0 -53
  25. package/src/libraries/HookConfigLib.sol +0 -135
  26. package/src/libraries/KnownSelectorsLib.sol +0 -65
  27. package/src/libraries/ModuleEntityLib.sol +0 -37
  28. package/src/libraries/ModuleStorageLib.sol +0 -62
  29. package/src/libraries/SparseCalldataSegmentLib.sol +0 -95
  30. package/src/libraries/ValidationConfigLib.sol +0 -119
  31. package/src/modules/BaseModule.sol +0 -54
  32. package/src/modules/ModuleEIP712.sol +0 -29
  33. package/src/modules/ReplaySafeWrapper.sol +0 -38
  34. package/src/modules/TokenReceiverModule.sol +0 -87
  35. package/src/modules/permissions/AllowlistModule.sol +0 -156
  36. package/src/modules/permissions/ERC20TokenLimitModule.sol +0 -145
  37. package/src/modules/permissions/NativeTokenLimitModule.sol +0 -154
  38. package/src/modules/validation/ISingleSignerValidationModule.sol +0 -22
  39. 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
- }