forked from alchemyplatform/modular-account
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExecutionInstallDelegate.sol
More file actions
172 lines (141 loc) · 6.84 KB
/
ExecutionInstallDelegate.sol
File metadata and controls
172 lines (141 loc) · 6.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;
import {
ExecutionManifest,
ManifestExecutionHook
} from "@erc6900/reference-implementation/interfaces/IExecutionModule.sol";
import {HookConfig, IModularAccount} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {IModule} from "@erc6900/reference-implementation/interfaces/IModule.sol";
import {HookConfigLib} from "@erc6900/reference-implementation/libraries/HookConfigLib.sol";
import {AccountStorage, ExecutionStorage, getAccountStorage, toSetValue} from "../account/AccountStorage.sol";
import {KnownSelectorsLib} from "../libraries/KnownSelectorsLib.sol";
import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol";
import {ModuleInstallCommonsLib} from "../libraries/ModuleInstallCommonsLib.sol";
/// @title Execution Install Delegate
/// @author Alchemy
/// @notice This contract acts as an external library which is meant to handle execution function installations and
/// uninstallations via delegatecall.
contract ExecutionInstallDelegate {
using LinkedListSetLib for LinkedListSet;
address internal immutable _THIS_ADDRESS;
error OnlyDelegateCall();
error NullModule();
error ExecutionFunctionAlreadySet();
error IModuleFunctionNotAllowed();
error Erc4337FunctionNotAllowed();
modifier onlyDelegateCall() {
if (address(this) == _THIS_ADDRESS) {
revert OnlyDelegateCall();
}
_;
}
constructor() {
_THIS_ADDRESS = address(this);
}
// External Functions
/// @notice Update components according to the manifest.
function installExecution(
address module,
ExecutionManifest calldata manifest,
bytes calldata moduleInstallData
) external onlyDelegateCall {
AccountStorage storage _storage = getAccountStorage();
if (module == address(0)) {
revert NullModule();
}
// Update components according to the manifest.
uint256 length = manifest.executionFunctions.length;
for (uint256 i = 0; i < length; ++i) {
bytes4 selector = manifest.executionFunctions[i].executionSelector;
bool skipRuntimeValidation = manifest.executionFunctions[i].skipRuntimeValidation;
bool allowGlobalValidation = manifest.executionFunctions[i].allowGlobalValidation;
_setExecutionFunction(selector, skipRuntimeValidation, allowGlobalValidation, module);
}
length = manifest.executionHooks.length;
for (uint256 i = 0; i < length; ++i) {
ManifestExecutionHook memory mh = manifest.executionHooks[i];
LinkedListSet storage executionHooks = _storage.executionStorage[mh.executionSelector].executionHooks;
HookConfig hookConfig = HookConfigLib.packExecHook({
_module: module,
_entityId: mh.entityId,
_hasPre: mh.isPreHook,
_hasPost: mh.isPostHook
});
ModuleInstallCommonsLib.addExecHooks(executionHooks, hookConfig);
}
length = manifest.interfaceIds.length;
for (uint256 i = 0; i < length; ++i) {
_storage.supportedIfaces[manifest.interfaceIds[i]] += 1;
}
ModuleInstallCommonsLib.onInstall(module, moduleInstallData, type(IModule).interfaceId);
emit IModularAccount.ExecutionInstalled(module, manifest);
}
/// @notice Remove components according to the manifest, in reverse order (by component type) of their
/// installation.
function uninstallExecution(address module, ExecutionManifest calldata manifest, bytes calldata uninstallData)
external
onlyDelegateCall
{
AccountStorage storage _storage = getAccountStorage();
uint256 length = manifest.executionHooks.length;
for (uint256 i = 0; i < length; ++i) {
ManifestExecutionHook memory mh = manifest.executionHooks[i];
LinkedListSet storage executionHooks = _storage.executionStorage[mh.executionSelector].executionHooks;
HookConfig hookConfig = HookConfigLib.packExecHook({
_module: module,
_entityId: mh.entityId,
_hasPre: mh.isPreHook,
_hasPost: mh.isPostHook
});
_removeExecHooks(executionHooks, hookConfig);
}
length = manifest.executionFunctions.length;
for (uint256 i = 0; i < length; ++i) {
bytes4 selector = manifest.executionFunctions[i].executionSelector;
_removeExecutionFunction(selector);
}
length = manifest.interfaceIds.length;
for (uint256 i = 0; i < length; ++i) {
_storage.supportedIfaces[manifest.interfaceIds[i]] -= 1;
}
// Clear the module storage for the account.
bool onUninstallSuccess = ModuleInstallCommonsLib.onUninstall(module, uninstallData);
emit IModularAccount.ExecutionUninstalled(module, onUninstallSuccess, manifest);
}
// Private Functions
function _setExecutionFunction(
bytes4 selector,
bool skipRuntimeValidation,
bool allowGlobalValidation,
address module
) internal {
ExecutionStorage storage _executionStorage = getAccountStorage().executionStorage[bytes4(selector)];
if (_executionStorage.module != address(0)) {
revert ExecutionFunctionAlreadySet();
}
// Note that there is no check for native function selectors. Installing a function with a colliding
// selector will lead to the installed function being unreachable.
// Make sure incoming execution function is not a function in IModule
if (KnownSelectorsLib.isIModuleFunction(uint32(selector))) {
revert IModuleFunctionNotAllowed();
}
// Also make sure it doesn't collide with functions defined by ERC-4337 and called by the entry point. This
// prevents a malicious module from sneaking in a function with the same selector as e.g.
// `validatePaymasterUserOp` and turning the account into their own personal paymaster.
if (KnownSelectorsLib.isErc4337Function(uint32(selector))) {
revert Erc4337FunctionNotAllowed();
}
_executionStorage.module = module;
_executionStorage.skipRuntimeValidation = skipRuntimeValidation;
_executionStorage.allowGlobalValidation = allowGlobalValidation;
}
function _removeExecutionFunction(bytes4 selector) internal {
ExecutionStorage storage _executionStorage = getAccountStorage().executionStorage[selector];
_executionStorage.module = address(0);
_executionStorage.skipRuntimeValidation = false;
_executionStorage.allowGlobalValidation = false;
}
function _removeExecHooks(LinkedListSet storage hooks, HookConfig hookConfig) internal {
hooks.tryRemove(toSetValue(hookConfig));
}
}