• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

safe-global / safe-smart-account / 14749517005

30 Apr 2025 07:48AM UTC coverage: 93.957%. Remained the same
14749517005

push

github

nlordell
Proofread NatSpec Documentation (#946)

This is a first pass at proofreading the NatSpec documentation.

---------

Co-authored-by: Akshay Patel <akshay.ap95@gmail.com>
Co-authored-by: Shebin John <admin@remedcu.com>

325 of 366 branches covered (88.8%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

3 existing lines in 2 files now uncovered.

499 of 511 relevant lines covered (97.65%)

100.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.86
/contracts/base/ModuleManager.sol
1
// SPDX-License-Identifier: LGPL-3.0-only
2
/* solhint-disable one-contract-per-file */
3
pragma solidity >=0.7.0 <0.9.0;
4
import {SelfAuthorized} from "./../common/SelfAuthorized.sol";
5
import {IERC165} from "./../interfaces/IERC165.sol";
6
import {IModuleManager} from "./../interfaces/IModuleManager.sol";
7
import {Enum} from "./../libraries/Enum.sol";
8
import {Executor} from "./Executor.sol";
9

10
/**
11
 * @title IModuleGuard Interface
12
 */
13
interface IModuleGuard is IERC165 {
14
    /**
15
     * @notice Checks the module transaction details.
16
     * @dev The function needs to implement module transaction validation logic.
17
     * @param to The address to which the transaction is intended.
18
     * @param value The value of the transaction in Wei.
19
     * @param data The transaction data.
20
     * @param operation Operation type (0 for `CALL`, 1 for `DELEGATECALL`) of the module transaction.
21
     * @param module The module involved in the transaction.
22
     * @return moduleTxHash A guard-specific module transaction hash. This value is passed to the matching {checkAfterModuleExecution} call.
23
     */
24
    function checkModuleTransaction(
25
        address to,
26
        uint256 value,
27
        bytes memory data,
28
        Enum.Operation operation,
29
        address module
30
    ) external returns (bytes32 moduleTxHash);
31

32
    /**
33
     * @notice Checks after execution of module transaction.
34
     * @dev The function needs to implement a check after the execution of the module transaction.
35
     * @param txHash The guard-specific module transaction hash returned from the matching {checkModuleTransaction} call.
36
     * @param success The status of the module transaction execution.
37
     */
38
    function checkAfterModuleExecution(bytes32 txHash, bool success) external;
39
}
40

41
/**
42
 * @title Base Module Guard
43
 */
44
abstract contract BaseModuleGuard is IModuleGuard {
45
    /**
46
     * @inheritdoc IERC165
47
     */
48
    function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {
UNCOV
49
        return
×
50
            interfaceId == type(IModuleGuard).interfaceId || // 0x58401ed8
×
51
            interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7
52
    }
53
}
54

55
/**
56
 * @title Module Manager
57
 * @notice A contract managing Safe modules.
58
 * @dev Modules are extensions with unlimited access to a Safe that can be added to a Safe by its owners.
59
        ⚠️⚠️⚠️ WARNING: Modules are a security risk since they can execute arbitrary transactions,
60
        so only trusted and audited modules should be added to a Safe. A malicious module can
61
        completely take over a Safe. ⚠️⚠️⚠️
62
 * @author Stefan George - @Georgi87
63
 * @author Richard Meissner - @rmeissner
64
 */
65
abstract contract ModuleManager is SelfAuthorized, Executor, IModuleManager {
66
    /**
67
     * @dev The sentinel module value in the {modules} linked list.
68
     *      `SENTINEL_MODULES` is used to traverse {modules}, such that:
69
     *      1. `modules[SENTINEL_MODULES]` contains the first module
70
     *      2. `modules[last_module]` points back to `SENTINEL_MODULES`
71
     */
72
    address internal constant SENTINEL_MODULES = address(0x1);
73

74
    /**
75
     * @dev The storage slot used for storing the currently configured module guard.
76
     *      Precomputed value of: `keccak256("module_manager.module_guard.address")`.
77
     */
78
    bytes32 internal constant MODULE_GUARD_STORAGE_SLOT = 0xb104e0b93118902c651344349b610029d694cfdec91c589c91ebafbcd0289947;
79

80
    /**
81
     * @dev The linked list of modules, where `modules[module]` points to the next in the list.
82
     *      A mapping is used to allow for `O(1)` inclusion checks.
83
     */
84
    mapping(address => address) internal modules;
85

86
    /**
87
     * @notice Setup function sets the initial storage of the contract.
88
     *         Optionally executes a delegate call to another contract to setup the modules.
89
     * @param to Optional destination address of the call to execute.
90
     * @param data Optional data of call to execute.
91
     */
92
    function setupModules(address to, bytes memory data) internal {
93
        if (modules[SENTINEL_MODULES] != address(0)) revertWithError("GS100");
177!
94
        modules[SENTINEL_MODULES] = SENTINEL_MODULES;
177✔
95
        if (to != address(0)) {
177✔
96
            if (!isContract(to)) revertWithError("GS002");
18✔
97
            // Setup has to complete successfully or the transaction fails.
98
            if (!execute(to, 0, data, Enum.Operation.DelegateCall, type(uint256).max)) revertWithError("GS000");
16✔
99
        }
100
    }
101

102
    /**
103
     * @notice Runs pre-execution checks for module transactions if a guard is enabled.
104
     * @param to Target address of module transaction.
105
     * @param value Native token value of module transaction.
106
     * @param data Data payload of module transaction.
107
     * @param operation Operation type (0 for `CALL`, 1 for `DELEGATECALL`) of the module transaction.
108
     * @return guard Guard to be used for checking.
109
     * @return guardHash Hash returned from the guard tx check.
110
     */
111
    function preModuleExecution(
112
        address to,
113
        uint256 value,
114
        bytes memory data,
115
        Enum.Operation operation
116
    ) internal returns (address guard, bytes32 guardHash) {
117
        onBeforeExecTransactionFromModule(to, value, data, operation);
52✔
118
        guard = getModuleGuard();
52✔
119

120
        // Only allow-listed modules are allowed.
121
        require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "GS104");
52✔
122

123
        if (guard != address(0)) {
34✔
124
            guardHash = IModuleGuard(guard).checkModuleTransaction(to, value, data, operation, msg.sender);
6✔
125
        }
126
    }
127

128
    /**
129
     * @notice Runs post-execution checks for module transactions if a guard is enabled.
130
     * @dev Emits event based on module transaction success.
131
     * @param guard Guard to be used for checking.
132
     * @param guardHash Hash returned from the guard during pre execution check.
133
     * @param success Boolean flag indicating if the call succeeded.
134
     */
135
    function postModuleExecution(address guard, bytes32 guardHash, bool success) internal {
136
        if (guard != address(0)) {
34✔
137
            IModuleGuard(guard).checkAfterModuleExecution(guardHash, success);
6✔
138
        }
139
        if (success) emit ExecutionFromModuleSuccess(msg.sender);
34✔
140
        else emit ExecutionFromModuleFailure(msg.sender);
141
    }
142

143
    /**
144
     * @inheritdoc IModuleManager
145
     */
146
    function enableModule(address module) public override authorized {
80✔
147
        // Module address cannot be null or sentinel.
148
        if (module == address(0) || module == SENTINEL_MODULES) revertWithError("GS101");
78✔
149
        // Module cannot be added twice.
150
        if (modules[module] != address(0)) revertWithError("GS102");
74✔
151
        modules[module] = modules[SENTINEL_MODULES];
72✔
152
        modules[SENTINEL_MODULES] = module;
72✔
153
        emit EnabledModule(module);
72✔
154
    }
155

156
    /**
157
     * @inheritdoc IModuleManager
158
     */
159
    function disableModule(address prevModule, address module) public override authorized {
16✔
160
        // Validate module address and check that it corresponds to a module index.
161
        if (module == address(0) || module == SENTINEL_MODULES) revertWithError("GS101");
14✔
162
        if (modules[prevModule] != module) revertWithError("GS103");
10✔
163
        modules[prevModule] = modules[module];
4✔
164
        modules[module] = address(0);
4✔
165
        emit DisabledModule(module);
4✔
166
    }
167

168
    /**
169
     * @inheritdoc IModuleManager
170
     */
171
    function execTransactionFromModule(
172
        address to,
173
        uint256 value,
174
        bytes memory data,
175
        Enum.Operation operation
176
    ) external override returns (bool success) {
177
        (address guard, bytes32 guardHash) = preModuleExecution(to, value, data, operation);
31✔
178
        success = execute(to, value, data, operation, type(uint256).max);
21✔
179
        postModuleExecution(guard, guardHash, success);
21✔
180
    }
181

182
    /**
183
     * @inheritdoc IModuleManager
184
     */
185
    function execTransactionFromModuleReturnData(
186
        address to,
187
        uint256 value,
188
        bytes memory data,
189
        Enum.Operation operation
190
    ) external override returns (bool success, bytes memory returnData) {
191
        (address guard, bytes32 guardHash) = preModuleExecution(to, value, data, operation);
21✔
192
        success = execute(to, value, data, operation, type(uint256).max);
13✔
193
        /* solhint-disable no-inline-assembly */
194
        /// @solidity memory-safe-assembly
195
        assembly {
13✔
196
            // Load free memory location.
197
            returnData := mload(0x40)
198
            // We allocate memory for the return data by setting the free memory location to
199
            // current free memory location plus the return data size with an additional 32
200
            // bytes for storing the length of the return data.
201
            mstore(0x40, add(returnData, add(returndatasize(), 0x20)))
202
            // Store the size.
203
            mstore(returnData, returndatasize())
204
            // Store the data.
205
            returndatacopy(add(returnData, 0x20), 0, returndatasize())
206
        }
207
        /* solhint-enable no-inline-assembly */
208
        postModuleExecution(guard, guardHash, success);
13✔
209
    }
210

211
    /**
212
     * @inheritdoc IModuleManager
213
     */
214
    function isModuleEnabled(address module) public view override returns (bool) {
215
        return SENTINEL_MODULES != module && modules[module] != address(0);
44✔
216
    }
217

218
    /**
219
     * @inheritdoc IModuleManager
220
     */
221
    function getModulesPaginated(address start, uint256 pageSize) external view override returns (address[] memory array, address next) {
222
        if (start != SENTINEL_MODULES && !isModuleEnabled(start)) revertWithError("GS105");
44✔
223
        if (pageSize == 0) revertWithError("GS106");
40✔
224
        // Init array with max page size.
225
        array = new address[](pageSize);
38✔
226

227
        // Populate return array.
228
        uint256 moduleCount = 0;
38✔
229
        next = modules[start];
38✔
230
        while (next != address(0) && next != SENTINEL_MODULES && moduleCount < pageSize) {
38✔
231
            array[moduleCount] = next;
34✔
232
            next = modules[next];
34✔
233
            ++moduleCount;
34✔
234
        }
235

236
        // Because of the argument validation, we can assume that the loop will always iterate over the valid module list values
237
        // and the `next` variable will either be an enabled module or a sentinel address (signalling the end).
238
        //
239
        // If we haven't reached the end inside the loop, we need to set the next pointer to the last element of the modules array
240
        // because the `next` variable (which is a module by itself) acting as a pointer to the start of the next page is neither
241
        // included to the current page, nor will it be included in the next one if you pass it as a start.
242
        if (next != SENTINEL_MODULES) {
38✔
243
            next = array[moduleCount - 1];
6✔
244
        }
245
        // Set the correct size of the returned array.
246
        /* solhint-disable no-inline-assembly */
247
        /// @solidity memory-safe-assembly
248
        assembly {
36✔
249
            mstore(array, moduleCount)
250
        }
251
        /* solhint-enable no-inline-assembly */
252
    }
253

254
    /**
255
     * @notice Returns true if `account` appears to be a contract.
256
     * @dev This function will return false if invoked during the constructor of a contract,
257
     *      as the code is not created until after the constructor finishes.
258
     * @param account The address being queried.
259
     */
260
    function isContract(address account) internal view returns (bool) {
261
        uint256 size;
18✔
262
        /* solhint-disable no-inline-assembly */
263
        /// @solidity memory-safe-assembly
264
        assembly {
18✔
265
            size := extcodesize(account)
266
        }
267
        /* solhint-enable no-inline-assembly */
268
        return size > 0;
18✔
269
    }
270

271
    /**
272
     * @inheritdoc IModuleManager
273
     */
274
    function setModuleGuard(address moduleGuard) external override authorized {
16!
275
        if (moduleGuard != address(0) && !IModuleGuard(moduleGuard).supportsInterface(type(IModuleGuard).interfaceId))
16!
UNCOV
276
            revertWithError("GS301");
×
277

278
        bytes32 slot = MODULE_GUARD_STORAGE_SLOT;
14✔
279
        /* solhint-disable no-inline-assembly */
280
        /// @solidity memory-safe-assembly
281
        assembly {
14✔
282
            sstore(slot, moduleGuard)
283
        }
284
        /* solhint-enable no-inline-assembly */
285
        emit ChangedModuleGuard(moduleGuard);
14✔
286
    }
287

288
    /**
289
     * @dev Internal method to retrieve the current module guard.
290
     * @return moduleGuard The address of the module guard.
291
     */
292
    function getModuleGuard() internal view returns (address moduleGuard) {
293
        bytes32 slot = MODULE_GUARD_STORAGE_SLOT;
52✔
294
        /* solhint-disable no-inline-assembly */
295
        /// @solidity memory-safe-assembly
296
        assembly {
52✔
297
            moduleGuard := sload(slot)
298
        }
299
        /* solhint-enable no-inline-assembly */
300
    }
301

302
    /**
303
     * @notice A hook that gets called before execution of {execTransactionFromModule*} methods.
304
     * @param to Destination address of module transaction.
305
     * @param value Native token value of module transaction.
306
     * @param data Data payload of module transaction.
307
     * @param operation Operation type of module transaction.
308
     */
309
    function onBeforeExecTransactionFromModule(address to, uint256 value, bytes memory data, Enum.Operation operation) internal virtual {}
310
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc