@bloxchain/contracts 1.0.0-alpha.13 → 1.0.0-alpha.15

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.
@@ -1,458 +1,447 @@
1
- // SPDX-License-Identifier: MPL-2.0
2
- pragma solidity 0.8.34;
3
-
4
- import "../base/BaseStateMachine.sol";
5
- import "../lib/utils/SharedValidation.sol";
6
- import "./lib/definitions/GuardControllerDefinitions.sol";
7
- import "../lib/interfaces/IDefinition.sol";
8
- import "./interface/IGuardController.sol";
9
-
10
- /**
11
- * @title GuardController
12
- * @dev Lightweight controller for generic contract delegation with full EngineBlox workflows
13
- *
14
- * This contract provides a complete solution for delegating control to external addresses.
15
- * It extends BaseStateMachine for core state machine functionality and supports all EngineBlox
16
- * execution patterns including time-locked transactions, meta-transactions, and payment management.
17
- *
18
- * Key Features:
19
- * - Core state machine functionality from BaseStateMachine
20
- * - Function schema query support (functionSchemaExists)
21
- * - STANDARD execution type only (function selector + params)
22
- * - Meta-transaction support for delegated approvals and cancellations
23
- * - Payment management for native tokens and ERC20 tokens
24
- * - Role-based access control with action-level permissions
25
- * - Target address whitelist per function selector (defense-in-depth security layer)
26
- *
27
- * Security Features:
28
- * - Target whitelist: Strict security - restricts which contract addresses can be called per function selector
29
- * - Prevents exploitation of global function selector permissions by limiting valid target contracts
30
- * - Strict enforcement: Target MUST be explicitly whitelisted for the function selector
31
- * - If whitelist is empty (no entries), no targets are allowed - explicit deny for security
32
- * - Target whitelist is ALWAYS checked - no backward compatibility fallback
33
- *
34
- * Usage Flow:
35
- * 1. Deploy GuardController (or combine with RuntimeRBAC/SecureOwnable for role management)
36
- * 2. Function schemas should be registered via definitions or RuntimeRBAC if combined
37
- * 3. Create roles and assign function permissions with action bitmaps (via RuntimeRBAC if combined)
38
- * 4. Assign wallets to roles (via RuntimeRBAC if combined)
39
- * 5. Configure target whitelists per function selector (REQUIRED for execution)
40
- * 6. Execute operations via time-lock workflows based on action permissions
41
- * 7. Target whitelist is ALWAYS validated before execution - target must be in whitelist
42
- * 8. Target contract validates access (ownership/role-based)
43
- *
44
- * Workflows Available:
45
- * - Standard execution: function selector + params
46
- * - Time-locked approval: request + approve workflow
47
- * - Meta-transaction workflows: signed approvals/cancellations
48
- *
49
- * Whitelist Management:
50
- * - executeGuardConfigBatch: Batch execution for adding/removing targets from whitelist (OWNER_ROLE only)
51
- * - getAllowedTargets: Query whitelisted targets for a function selector
52
- *
53
- * @notice This contract is modular and can be combined with RuntimeRBAC and SecureOwnable
54
- * @notice Target whitelist is a GuardController-specific security feature, not part of EngineBlox library
55
- * @custom:security-contact security@particlecrypto.com
56
- */
57
- abstract contract GuardController is BaseStateMachine {
58
- using EngineBlox for EngineBlox.SecureOperationState;
59
-
60
- /**
61
- * @notice Initializer to initialize GuardController
62
- * @param initialOwner The initial owner address
63
- * @param broadcaster The broadcaster address
64
- * @param recovery The recovery address
65
- * @param timeLockPeriodSec The timelock period in seconds
66
- * @param eventForwarder The event forwarder address
67
- */
68
- function initialize(
69
- address initialOwner,
70
- address broadcaster,
71
- address recovery,
72
- uint256 timeLockPeriodSec,
73
- address eventForwarder
74
- ) public virtual onlyInitializing {
75
- _initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
76
-
77
- // Load GuardController-specific definitions
78
- IDefinition.RolePermission memory guardControllerPermissions = GuardControllerDefinitions.getRolePermissions();
79
- _loadDefinitions(
80
- GuardControllerDefinitions.getFunctionSchemas(),
81
- guardControllerPermissions.roleHashes,
82
- guardControllerPermissions.functionPermissions,
83
- true // Enforce all function schemas are protected
84
- );
85
- }
86
-
87
- // ============ INTERFACE SUPPORT ============
88
-
89
- /**
90
- * @dev See {IERC165-supportsInterface}.
91
- * @notice Adds IGuardController interface ID for component detection
92
- */
93
- function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
94
- return interfaceId == type(IGuardController).interfaceId || super.supportsInterface(interfaceId);
95
- }
96
-
97
- // ============ EXECUTION FUNCTIONS ============
98
-
99
- /**
100
- * @dev Requests a time-locked execution via EngineBlox workflow
101
- * @param target The address of the target contract
102
- * @param value The ETH value to send (0 for standard function calls)
103
- * @param functionSelector The function selector to execute (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
104
- * @param params The encoded parameters for the function (empty for simple native token transfers)
105
- * @param gasLimit The gas limit for execution
106
- * @param operationType The operation type hash
107
- * @return txId The transaction ID for the requested operation
108
- * @notice Creates a time-locked transaction that must be approved after the timelock period
109
- * @notice Requires EXECUTE_TIME_DELAY_REQUEST permission for the function selector
110
- * @notice For standard function calls: value=0, functionSelector=non-zero, params=encoded data
111
- * @notice For simple native token transfers: value>0, functionSelector=NATIVE_TRANSFER_SELECTOR, params=""
112
- */
113
- function executeWithTimeLock(
114
- address target,
115
- uint256 value,
116
- bytes4 functionSelector,
117
- bytes memory params,
118
- uint256 gasLimit,
119
- bytes32 operationType
120
- ) public returns (uint256 txId) {
121
- // SECURITY: Prevent access to internal execution functions
122
- _validateNotInternalFunction(target, functionSelector);
123
-
124
- // Request via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
125
- EngineBlox.TxRecord memory txRecord = _requestTransaction(
126
- msg.sender,
127
- target,
128
- value,
129
- gasLimit,
130
- operationType,
131
- functionSelector,
132
- params
133
- );
134
- return txRecord.txId;
135
- }
136
-
137
- /**
138
- * @dev Requests a time-locked execution with payment details attached (same permissions as executeWithTimeLock)
139
- * @param target The address of the target contract
140
- * @param value The ETH value to send (0 for standard function calls)
141
- * @param functionSelector The function selector to execute (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
142
- * @param params The encoded parameters for the function (empty for simple native token transfers)
143
- * @param gasLimit The gas limit for execution
144
- * @param operationType The operation type hash
145
- * @param paymentDetails The payment details to attach to the transaction
146
- * @return txId The transaction ID for the requested operation (use getTransaction(txId) for full record)
147
- * @notice Creates a time-locked transaction with payment that must be approved after the timelock period
148
- * @notice Reuses EXECUTE_TIME_DELAY_REQUEST permission for the execution selector (no new definitions)
149
- * @notice Approval/cancel use same flows as executeWithTimeLock (approveTimeLockExecution, cancelTimeLockExecution)
150
- */
151
- function executeWithPayment(
152
- address target,
153
- uint256 value,
154
- bytes4 functionSelector,
155
- bytes memory params,
156
- uint256 gasLimit,
157
- bytes32 operationType,
158
- EngineBlox.PaymentDetails memory paymentDetails
159
- ) public returns (uint256 txId) {
160
- _validateNotInternalFunction(target, functionSelector);
161
- EngineBlox.TxRecord memory txRecord = _requestTransactionWithPayment(
162
- msg.sender,
163
- target,
164
- value,
165
- gasLimit,
166
- operationType,
167
- functionSelector,
168
- params,
169
- paymentDetails
170
- );
171
- return txRecord.txId;
172
- }
173
-
174
- /**
175
- * @dev Approves and executes a time-locked transaction
176
- * @param txId The transaction ID
177
- * @return result The execution result
178
- * @notice Requires STANDARD execution type and EXECUTE_TIME_DELAY_APPROVE permission for the execution function
179
- */
180
- function approveTimeLockExecution(
181
- uint256 txId
182
- ) public returns (uint256) {
183
- // SECURITY: Prevent access to internal execution functions
184
- EngineBlox.TxRecord memory txRecord = _getSecureState().txRecords[txId];
185
- _validateNotInternalFunction(txRecord.params.target, txRecord.params.executionSelector);
186
-
187
- // Approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
188
- txRecord = _approveTransaction(txId);
189
- return txRecord.txId;
190
- }
191
-
192
- /**
193
- * @dev Cancels a time-locked transaction
194
- * @param txId The transaction ID
195
- * @return The updated transaction record
196
- * @notice Requires STANDARD execution type and EXECUTE_TIME_DELAY_CANCEL permission for the execution function
197
- */
198
- function cancelTimeLockExecution(
199
- uint256 txId
200
- ) public returns (uint256) {
201
- // SECURITY: Prevent access to internal execution functions
202
- EngineBlox.TxRecord memory txRecord = _getSecureState().txRecords[txId];
203
- _validateNotInternalFunction(txRecord.params.target, txRecord.params.executionSelector);
204
-
205
- // Cancel via BaseStateMachine helper (validates permissions in EngineBlox)
206
- txRecord = _cancelTransaction(txId);
207
- return txRecord.txId;
208
- }
209
-
210
- /**
211
- * @dev Approves a time-locked transaction using a meta-transaction
212
- * @param metaTx The meta-transaction containing the transaction record and signature
213
- * @return The updated transaction record
214
- * @notice Requires STANDARD execution type and EXECUTE_META_APPROVE permission for the execution function
215
- */
216
- function approveTimeLockExecutionWithMetaTx(
217
- EngineBlox.MetaTransaction memory metaTx
218
- ) public returns (uint256) {
219
- // SECURITY: Prevent access to internal execution functions
220
- _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
221
-
222
- // Approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
223
- EngineBlox.TxRecord memory txRecord = _approveTransactionWithMetaTx(metaTx);
224
- return txRecord.txId;
225
- }
226
-
227
- /**
228
- * @dev Cancels a time-locked transaction using a meta-transaction
229
- * @param metaTx The meta-transaction containing the transaction record and signature
230
- * @return The updated transaction record
231
- * @notice Requires STANDARD execution type and EXECUTE_META_CANCEL permission for the execution function
232
- */
233
- function cancelTimeLockExecutionWithMetaTx(
234
- EngineBlox.MetaTransaction memory metaTx
235
- ) public returns (uint256) {
236
- // SECURITY: Prevent access to internal execution functions
237
- _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
238
-
239
- // Cancel via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
240
- EngineBlox.TxRecord memory txRecord = _cancelTransactionWithMetaTx(metaTx);
241
- return txRecord.txId;
242
- }
243
-
244
- /**
245
- * @dev Requests and approves a transaction in one step using a meta-transaction
246
- * @param metaTx The meta-transaction containing the transaction record and signature
247
- * @return The transaction record after request and approval
248
- * @notice Requires STANDARD execution type
249
- * @notice Validates function schema and permissions for the execution function (same as executeWithTimeLock)
250
- * @notice Requires EXECUTE_META_REQUEST_AND_APPROVE permission for the execution function selector
251
- */
252
- function requestAndApproveExecution(
253
- EngineBlox.MetaTransaction memory metaTx
254
- ) public returns (uint256) {
255
- // SECURITY: Prevent access to internal execution functions
256
- _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
257
-
258
- // Request and approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
259
- EngineBlox.TxRecord memory txRecord = _requestAndApproveTransaction(metaTx);
260
- return txRecord.txId;
261
- }
262
-
263
- // Note: Meta-transaction utility functions (createMetaTxParams,
264
- // generateUnsignedMetaTransactionForNew, generateUnsignedMetaTransactionForExisting)
265
- // are already available through inheritance from BaseStateMachine
266
- //
267
- // Note: Permission validation is handled by EngineBlox library functions
268
- // which validate both function schema existence and RBAC permissions for execution selectors
269
-
270
- // ============ INTERNAL VALIDATION HELPERS ============
271
-
272
- /**
273
- * @dev Validates that GuardController is not attempting to access internal execution functions
274
- * @param target The target contract address
275
- * @param functionSelector The function selector to validate
276
- * @notice Internal functions use validateInternalCallInternal and should only be called
277
- * through the contract's own workflow, not via GuardController
278
- * @notice Blocks all calls to address(this) to prevent bypassing internal-only protection
279
- * @notice Exception: System macro selectors (e.g., NATIVE_TRANSFER_SELECTOR) are allowed
280
- * to target address(this) for system-level operations like native token deposits
281
- */
282
- function _validateNotInternalFunction(
283
- address target,
284
- bytes4 functionSelector
285
- ) internal view {
286
- // SECURITY: Prevent GuardController from accessing internal execution functions
287
- // Internal functions use validateInternalCallInternal and should only be called
288
- // through the contract's own workflow, not via GuardController
289
-
290
- // If target is this contract, we need to validate the function selector
291
- if (target == address(this)) {
292
- // Allow system macro selectors (e.g., NATIVE_TRANSFER_SELECTOR for native token deposits)
293
- // These are special system-level operations that are safe to execute on address(this)
294
- if (_isMacroSelector(functionSelector)) {
295
- return; // Allow system macro selectors
296
- }
297
-
298
- // Block all other calls to address(this) to prevent bypassing internal-only protection
299
- revert SharedValidation.InternalFunctionNotAccessible(functionSelector);
300
- }
301
- }
302
-
303
- // ============ GUARD CONFIGURATION BATCH INTERFACE ============
304
-
305
- /**
306
- * @dev Requests and approves a Guard configuration batch using a meta-transaction
307
- * @param metaTx The meta-transaction
308
- * @return The transaction record
309
- * @notice OWNER signs, BROADCASTER executes according to GuardControllerDefinitions
310
- */
311
- function guardConfigBatchRequestAndApprove(
312
- EngineBlox.MetaTransaction memory metaTx
313
- ) public returns (uint256) {
314
- _validateBroadcaster(msg.sender);
315
- EngineBlox.TxRecord memory txRecord = _requestAndApproveTransaction(metaTx);
316
- return txRecord.txId;
317
- }
318
-
319
- /**
320
- * @dev External function that can only be called by the contract itself to execute a Guard configuration batch
321
- * @param actions Encoded guard configuration actions
322
- */
323
- function executeGuardConfigBatch(IGuardController.GuardConfigAction[] calldata actions) external {
324
- _validateExecuteBySelf();
325
- _executeGuardConfigBatch(actions);
326
- }
327
-
328
- // ============ HELPER FUNCTIONS ============
329
-
330
- /**
331
- * @dev Internal helper to execute a Guard configuration batch
332
- * @param actions Encoded guard configuration actions
333
- */
334
- function _executeGuardConfigBatch(IGuardController.GuardConfigAction[] calldata actions) internal {
335
- _validateBatchSize(actions.length);
336
-
337
- for (uint256 i = 0; i < actions.length; i++) {
338
- IGuardController.GuardConfigAction calldata action = actions[i];
339
-
340
- if (action.actionType == IGuardController.GuardConfigActionType.ADD_TARGET_TO_WHITELIST) {
341
- _executeAddTargetToWhitelist(action.data);
342
- } else if (action.actionType == IGuardController.GuardConfigActionType.REMOVE_TARGET_FROM_WHITELIST) {
343
- _executeRemoveTargetFromWhitelist(action.data);
344
- } else if (action.actionType == IGuardController.GuardConfigActionType.REGISTER_FUNCTION) {
345
- _executeRegisterFunction(action.data);
346
- } else if (action.actionType == IGuardController.GuardConfigActionType.UNREGISTER_FUNCTION) {
347
- _executeUnregisterFunction(action.data);
348
- } else {
349
- revert SharedValidation.NotSupported();
350
- }
351
- }
352
- }
353
-
354
- /**
355
- * @dev Executes ADD_TARGET_TO_WHITELIST: adds a target address to a function's call whitelist
356
- * @param data ABI-encoded (bytes4 functionSelector, address target)
357
- */
358
- function _executeAddTargetToWhitelist(bytes calldata data) internal {
359
- (bytes4 functionSelector, address target) = abi.decode(data, (bytes4, address));
360
- _addTargetToFunctionWhitelist(functionSelector, target);
361
- _logGuardConfigEvent(IGuardController.GuardConfigActionType.ADD_TARGET_TO_WHITELIST, functionSelector, target);
362
- }
363
-
364
- /**
365
- * @dev Executes REMOVE_TARGET_FROM_WHITELIST: removes a target address from a function's call whitelist
366
- * @param data ABI-encoded (bytes4 functionSelector, address target)
367
- */
368
- function _executeRemoveTargetFromWhitelist(bytes calldata data) internal {
369
- (bytes4 functionSelector, address target) = abi.decode(data, (bytes4, address));
370
- _removeTargetFromFunctionWhitelist(functionSelector, target);
371
- _logGuardConfigEvent(IGuardController.GuardConfigActionType.REMOVE_TARGET_FROM_WHITELIST, functionSelector, target);
372
- }
373
-
374
- /**
375
- * @dev Executes REGISTER_FUNCTION: registers a new function schema with signature, operation name, and supported actions
376
- * @param data ABI-encoded (string functionSignature, string operationName, TxAction[] supportedActions)
377
- */
378
- function _executeRegisterFunction(bytes calldata data) internal {
379
- (
380
- string memory functionSignature,
381
- string memory operationName,
382
- EngineBlox.TxAction[] memory supportedActions
383
- ) = abi.decode(data, (string, string, EngineBlox.TxAction[]));
384
-
385
- bytes4 functionSelector = _registerFunction(functionSignature, operationName, supportedActions);
386
- _logGuardConfigEvent(IGuardController.GuardConfigActionType.REGISTER_FUNCTION, functionSelector, address(0));
387
- }
388
-
389
- /**
390
- * @dev Executes UNREGISTER_FUNCTION: unregisters a function schema by selector
391
- * @param data ABI-encoded (bytes4 functionSelector, bool safeRemoval)
392
- */
393
- function _executeUnregisterFunction(bytes calldata data) internal {
394
- (bytes4 functionSelector, bool safeRemoval) = abi.decode(data, (bytes4, bool));
395
- _unregisterFunction(functionSelector, safeRemoval);
396
- _logGuardConfigEvent(IGuardController.GuardConfigActionType.UNREGISTER_FUNCTION, functionSelector, address(0));
397
- }
398
-
399
- /**
400
- * @dev Encodes and logs a guard config event via ComponentEvent. Payload decodes as (GuardConfigActionType, bytes4 functionSelector, address target).
401
- * @param actionType The guard config action type
402
- * @param functionSelector The function selector (or zero for N/A)
403
- * @param target The target address (or zero for N/A)
404
- */
405
- function _logGuardConfigEvent(
406
- IGuardController.GuardConfigActionType actionType,
407
- bytes4 functionSelector,
408
- address target
409
- ) internal {
410
- _logComponentEvent(abi.encode(actionType, functionSelector, target));
411
- }
412
-
413
- // ============ INTERNAL FUNCTION SCHEMA HELPERS ============
414
-
415
- /**
416
- * @dev Internal helper to register a new function schema
417
- * @param functionSignature The function signature
418
- * @param operationName The operation name
419
- * @param supportedActions Array of supported actions
420
- * @return functionSelector The derived function selector
421
- */
422
- function _registerFunction(
423
- string memory functionSignature,
424
- string memory operationName,
425
- EngineBlox.TxAction[] memory supportedActions
426
- ) internal returns (bytes4 functionSelector) {
427
- // Derive function selector from signature
428
- functionSelector = bytes4(keccak256(bytes(functionSignature)));
429
-
430
- // Convert actions array to bitmap
431
- uint16 supportedActionsBitmap = _createBitmapFromActions(supportedActions);
432
-
433
- // Create function schema directly (always non-protected)
434
- // Dynamically registered functions are execution selectors (handlerForSelectors must contain self-reference)
435
- // EngineBlox.createFunctionSchema validates schema doesn't already exist (ResourceAlreadyExists)
436
- bytes4[] memory executionHandlerForSelectors = new bytes4[](1);
437
- executionHandlerForSelectors[0] = functionSelector; // Self-reference for execution selector
438
- _createFunctionSchema(
439
- functionSignature,
440
- functionSelector,
441
- operationName,
442
- supportedActionsBitmap,
443
- false, // isProtected = false for dynamically registered functions
444
- executionHandlerForSelectors // handlerForSelectors with self-reference for execution selectors
445
- );
446
- }
447
-
448
- /**
449
- * @dev Internal helper to unregister a function schema
450
- * @param functionSelector The function selector to unregister
451
- * @param safeRemoval If true, checks for role references before removal
452
- * @notice EngineBlox.removeFunctionSchema validates schema existence (ResourceNotFound) and protected status (CannotModifyProtected)
453
- */
454
- function _unregisterFunction(bytes4 functionSelector, bool safeRemoval) internal {
455
- _removeFunctionSchema(functionSelector, safeRemoval);
456
- }
457
-
458
- }
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ pragma solidity 0.8.34;
3
+
4
+ import "../base/BaseStateMachine.sol";
5
+ import "../lib/utils/SharedValidation.sol";
6
+ import "./lib/definitions/GuardControllerDefinitions.sol";
7
+ import "../lib/interfaces/IDefinition.sol";
8
+ import "./interface/IGuardController.sol";
9
+
10
+ /**
11
+ * @title GuardController
12
+ * @dev Lightweight controller for generic contract delegation with full EngineBlox workflows
13
+ *
14
+ * This contract provides a complete solution for delegating control to external addresses.
15
+ * It extends BaseStateMachine for core state machine functionality and supports all EngineBlox
16
+ * execution patterns including time-locked transactions, meta-transactions, and payment management.
17
+ *
18
+ * Key Features:
19
+ * - Core state machine functionality from BaseStateMachine
20
+ * - STANDARD execution type only (function selector + params)
21
+ * - Meta-transaction support for delegated approvals and cancellations
22
+ * - Payment management for native tokens and ERC20 tokens
23
+ * - Role-based access control with action-level permissions
24
+ * - Target address whitelist per function selector (defense-in-depth security layer)
25
+ *
26
+ * Security Features:
27
+ * - Target whitelist: Strict security - restricts which contract addresses can be called per function selector
28
+ * - Prevents exploitation of global function selector permissions by limiting valid target contracts
29
+ * - Strict enforcement: Target MUST be explicitly whitelisted for the function selector
30
+ * - If whitelist is empty (no entries), no targets are allowed - explicit deny for security
31
+ * - Target whitelist is ALWAYS checked - no backward compatibility fallback
32
+ *
33
+ * Usage Flow:
34
+ * 1. Deploy GuardController (or combine with RuntimeRBAC/SecureOwnable for role management)
35
+ * 2. Function schemas should be registered via definitions or RuntimeRBAC if combined
36
+ * 3. Create roles and assign function permissions with action bitmaps (via RuntimeRBAC if combined)
37
+ * 4. Assign wallets to roles (via RuntimeRBAC if combined)
38
+ * 5. Configure target whitelists per function selector (REQUIRED for execution)
39
+ * 6. Execute operations via time-lock workflows based on action permissions
40
+ * 7. Target whitelist is ALWAYS validated before execution - target must be in whitelist
41
+ * 8. Target contract validates access (ownership/role-based)
42
+ *
43
+ * Workflows Available:
44
+ * - Standard execution: function selector + params
45
+ * - Time-locked approval: request + approve workflow
46
+ * - Meta-transaction workflows: signed approvals/cancellations
47
+ *
48
+ * Whitelist Management:
49
+ * - executeGuardConfigBatch: Batch execution for adding/removing targets from whitelist (OWNER_ROLE only)
50
+ * - getAllowedTargets: Query whitelisted targets for a function selector
51
+ *
52
+ * @notice This contract is modular and can be combined with RuntimeRBAC and SecureOwnable
53
+ * @notice Target whitelist is a GuardController-specific security feature, not part of EngineBlox library
54
+ * @custom:security-contact security@particlecrypto.com
55
+ */
56
+ abstract contract GuardController is BaseStateMachine {
57
+ using EngineBlox for EngineBlox.SecureOperationState;
58
+
59
+ /**
60
+ * @notice Initializer to initialize GuardController
61
+ * @param initialOwner The initial owner address
62
+ * @param broadcaster The broadcaster address
63
+ * @param recovery The recovery address
64
+ * @param timeLockPeriodSec The timelock period in seconds
65
+ * @param eventForwarder The event forwarder address
66
+ */
67
+ function initialize(
68
+ address initialOwner,
69
+ address broadcaster,
70
+ address recovery,
71
+ uint256 timeLockPeriodSec,
72
+ address eventForwarder
73
+ ) public virtual onlyInitializing {
74
+ _initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
75
+
76
+ // Load GuardController-specific definitions
77
+ IDefinition.RolePermission memory guardControllerPermissions = GuardControllerDefinitions.getRolePermissions();
78
+ _loadDefinitions(
79
+ GuardControllerDefinitions.getFunctionSchemas(),
80
+ guardControllerPermissions.roleHashes,
81
+ guardControllerPermissions.functionPermissions,
82
+ true // Enforce all function schemas are protected
83
+ );
84
+ }
85
+
86
+ // ============ INTERFACE SUPPORT ============
87
+
88
+ /**
89
+ * @dev See {IERC165-supportsInterface}.
90
+ * @notice Adds IGuardController interface ID for component detection
91
+ */
92
+ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
93
+ return interfaceId == type(IGuardController).interfaceId || super.supportsInterface(interfaceId);
94
+ }
95
+
96
+ // ============ EXECUTION FUNCTIONS ============
97
+
98
+ /**
99
+ * @dev Requests a time-locked execution via EngineBlox workflow
100
+ * @param target The address of the target contract
101
+ * @param value The ETH value to send (0 for standard function calls)
102
+ * @param functionSelector The function selector to execute (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
103
+ * @param params The encoded parameters for the function (empty for simple native token transfers)
104
+ * @param gasLimit The gas limit for execution
105
+ * @param operationType The operation type hash
106
+ * @return txId The transaction ID for the requested operation
107
+ * @notice Creates a time-locked transaction that must be approved after the timelock period
108
+ * @notice Requires EXECUTE_TIME_DELAY_REQUEST permission for the function selector
109
+ * @notice For standard function calls: value=0, functionSelector=non-zero, params=encoded data
110
+ * @notice For simple native token transfers: value>0, functionSelector=NATIVE_TRANSFER_SELECTOR, params=""
111
+ */
112
+ function executeWithTimeLock(
113
+ address target,
114
+ uint256 value,
115
+ bytes4 functionSelector,
116
+ bytes memory params,
117
+ uint256 gasLimit,
118
+ bytes32 operationType
119
+ ) public returns (uint256 txId) {
120
+ // SECURITY: Prevent access to internal execution functions
121
+ _validateNotInternalFunction(target, functionSelector);
122
+
123
+ // Request via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
124
+ EngineBlox.TxRecord memory txRecord = _requestTransaction(
125
+ msg.sender,
126
+ target,
127
+ value,
128
+ gasLimit,
129
+ operationType,
130
+ functionSelector,
131
+ params
132
+ );
133
+ return txRecord.txId;
134
+ }
135
+
136
+ /**
137
+ * @dev Requests a time-locked execution with payment details attached (same permissions as executeWithTimeLock)
138
+ * @param target The address of the target contract
139
+ * @param value The ETH value to send (0 for standard function calls)
140
+ * @param functionSelector The function selector to execute (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
141
+ * @param params The encoded parameters for the function (empty for simple native token transfers)
142
+ * @param gasLimit The gas limit for execution
143
+ * @param operationType The operation type hash
144
+ * @param paymentDetails The payment details to attach to the transaction
145
+ * @return txId The transaction ID for the requested operation (use getTransaction(txId) for full record)
146
+ * @notice Creates a time-locked transaction with payment that must be approved after the timelock period
147
+ * @notice Reuses EXECUTE_TIME_DELAY_REQUEST permission for the execution selector (no new definitions)
148
+ * @notice Approval/cancel use same flows as executeWithTimeLock (approveTimeLockExecution, cancelTimeLockExecution)
149
+ */
150
+ function executeWithPayment(
151
+ address target,
152
+ uint256 value,
153
+ bytes4 functionSelector,
154
+ bytes memory params,
155
+ uint256 gasLimit,
156
+ bytes32 operationType,
157
+ EngineBlox.PaymentDetails memory paymentDetails
158
+ ) public returns (uint256 txId) {
159
+ _validateNotInternalFunction(target, functionSelector);
160
+ EngineBlox.TxRecord memory txRecord = _requestTransactionWithPayment(
161
+ msg.sender,
162
+ target,
163
+ value,
164
+ gasLimit,
165
+ operationType,
166
+ functionSelector,
167
+ params,
168
+ paymentDetails
169
+ );
170
+ return txRecord.txId;
171
+ }
172
+
173
+ /**
174
+ * @dev Approves and executes a time-locked transaction
175
+ * @param txId The transaction ID
176
+ * @return result The execution result
177
+ * @notice Requires STANDARD execution type and EXECUTE_TIME_DELAY_APPROVE permission for the execution function
178
+ */
179
+ function approveTimeLockExecution(
180
+ uint256 txId
181
+ ) public returns (uint256) {
182
+ // SECURITY: Prevent access to internal execution functions
183
+ EngineBlox.TxRecord memory txRecord = _getSecureState().txRecords[txId];
184
+ _validateNotInternalFunction(txRecord.params.target, txRecord.params.executionSelector);
185
+
186
+ // Approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
187
+ txRecord = _approveTransaction(txId);
188
+ return txRecord.txId;
189
+ }
190
+
191
+ /**
192
+ * @dev Cancels a time-locked transaction
193
+ * @param txId The transaction ID
194
+ * @return The updated transaction record
195
+ * @notice Requires STANDARD execution type and EXECUTE_TIME_DELAY_CANCEL permission for the execution function
196
+ */
197
+ function cancelTimeLockExecution(
198
+ uint256 txId
199
+ ) public returns (uint256) {
200
+ // SECURITY: Prevent access to internal execution functions
201
+ EngineBlox.TxRecord memory txRecord = _getSecureState().txRecords[txId];
202
+ _validateNotInternalFunction(txRecord.params.target, txRecord.params.executionSelector);
203
+
204
+ // Cancel via BaseStateMachine helper (validates permissions in EngineBlox)
205
+ txRecord = _cancelTransaction(txId);
206
+ return txRecord.txId;
207
+ }
208
+
209
+ /**
210
+ * @dev Approves a time-locked transaction using a meta-transaction
211
+ * @param metaTx The meta-transaction containing the transaction record and signature
212
+ * @return The updated transaction record
213
+ * @notice Requires STANDARD execution type and EXECUTE_META_APPROVE permission for the execution function
214
+ */
215
+ function approveTimeLockExecutionWithMetaTx(
216
+ EngineBlox.MetaTransaction memory metaTx
217
+ ) public returns (uint256) {
218
+ // SECURITY: Prevent access to internal execution functions
219
+ _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
220
+
221
+ // Approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
222
+ EngineBlox.TxRecord memory txRecord = _approveTransactionWithMetaTx(metaTx);
223
+ return txRecord.txId;
224
+ }
225
+
226
+ /**
227
+ * @dev Cancels a time-locked transaction using a meta-transaction
228
+ * @param metaTx The meta-transaction containing the transaction record and signature
229
+ * @return The updated transaction record
230
+ * @notice Requires STANDARD execution type and EXECUTE_META_CANCEL permission for the execution function
231
+ */
232
+ function cancelTimeLockExecutionWithMetaTx(
233
+ EngineBlox.MetaTransaction memory metaTx
234
+ ) public returns (uint256) {
235
+ // SECURITY: Prevent access to internal execution functions
236
+ _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
237
+
238
+ // Cancel via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
239
+ EngineBlox.TxRecord memory txRecord = _cancelTransactionWithMetaTx(metaTx);
240
+ return txRecord.txId;
241
+ }
242
+
243
+ /**
244
+ * @dev Requests and approves a transaction in one step using a meta-transaction
245
+ * @param metaTx The meta-transaction containing the transaction record and signature
246
+ * @return The transaction record after request and approval
247
+ * @notice Requires STANDARD execution type
248
+ * @notice Validates function schema and permissions for the execution function (same as executeWithTimeLock)
249
+ * @notice Requires EXECUTE_META_REQUEST_AND_APPROVE permission for the execution function selector
250
+ */
251
+ function requestAndApproveExecution(
252
+ EngineBlox.MetaTransaction memory metaTx
253
+ ) public returns (uint256) {
254
+ // SECURITY: Prevent access to internal execution functions
255
+ _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
256
+
257
+ // Request and approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
258
+ EngineBlox.TxRecord memory txRecord = _requestAndApproveTransaction(metaTx);
259
+ return txRecord.txId;
260
+ }
261
+
262
+ // Note: Meta-transaction utility functions (createMetaTxParams,
263
+ // generateUnsignedMetaTransactionForNew, generateUnsignedMetaTransactionForExisting)
264
+ // are already available through inheritance from BaseStateMachine
265
+ //
266
+ // Note: Permission validation is handled by EngineBlox library functions
267
+ // which validate both function schema existence and RBAC permissions for execution selectors
268
+
269
+ // ============ INTERNAL VALIDATION HELPERS ============
270
+
271
+ /**
272
+ * @dev Validates that GuardController is not attempting to access internal execution functions
273
+ * @param target The target contract address
274
+ * @param functionSelector The function selector to validate
275
+ * @notice Internal functions use validateInternalCallInternal and should only be called
276
+ * through the contract's own workflow, not via GuardController
277
+ * @notice Blocks all calls to address(this) to prevent bypassing internal-only protection
278
+ * @notice Exception: System macro selectors (e.g., NATIVE_TRANSFER_SELECTOR) are allowed
279
+ * to target address(this) for system-level operations like native token deposits
280
+ */
281
+ function _validateNotInternalFunction(
282
+ address target,
283
+ bytes4 functionSelector
284
+ ) internal view {
285
+ // SECURITY: Prevent GuardController from accessing internal execution functions
286
+ // Internal functions use validateInternalCallInternal and should only be called
287
+ // through the contract's own workflow, not via GuardController
288
+
289
+ // If target is this contract, we need to validate the function selector
290
+ if (target == address(this)) {
291
+ // Allow system macro selectors (e.g., NATIVE_TRANSFER_SELECTOR for native token deposits)
292
+ // These are special system-level operations that are safe to execute on address(this)
293
+ if (_isMacroSelector(functionSelector)) {
294
+ return; // Allow system macro selectors
295
+ }
296
+
297
+ // Block all other calls to address(this) to prevent bypassing internal-only protection
298
+ revert SharedValidation.InternalFunctionNotAccessible(functionSelector);
299
+ }
300
+ }
301
+
302
+ // ============ GUARD CONFIGURATION BATCH INTERFACE ============
303
+
304
+ /**
305
+ * @dev Requests and approves a Guard configuration batch using a meta-transaction
306
+ * @param metaTx The meta-transaction
307
+ * @return The transaction record
308
+ * @notice OWNER signs, BROADCASTER executes according to GuardControllerDefinitions
309
+ */
310
+ function guardConfigBatchRequestAndApprove(
311
+ EngineBlox.MetaTransaction memory metaTx
312
+ ) public returns (uint256) {
313
+ _validateBroadcaster(msg.sender);
314
+ EngineBlox.TxRecord memory txRecord = _requestAndApproveTransaction(metaTx);
315
+ return txRecord.txId;
316
+ }
317
+
318
+ /**
319
+ * @dev External function that can only be called by the contract itself to execute a Guard configuration batch
320
+ * @param actions Encoded guard configuration actions
321
+ */
322
+ function executeGuardConfigBatch(IGuardController.GuardConfigAction[] calldata actions) external {
323
+ _validateExecuteBySelf();
324
+ _executeGuardConfigBatch(actions);
325
+ }
326
+
327
+ // ============ HELPER FUNCTIONS ============
328
+
329
+ /**
330
+ * @dev Internal helper to execute a Guard configuration batch
331
+ * @param actions Encoded guard configuration actions
332
+ */
333
+ function _executeGuardConfigBatch(IGuardController.GuardConfigAction[] calldata actions) internal {
334
+ _validateBatchSize(actions.length);
335
+
336
+ for (uint256 i = 0; i < actions.length; i++) {
337
+ IGuardController.GuardConfigAction calldata action = actions[i];
338
+
339
+ if (action.actionType == IGuardController.GuardConfigActionType.ADD_TARGET_TO_WHITELIST) {
340
+ _executeAddTargetToWhitelist(action.data);
341
+ } else if (action.actionType == IGuardController.GuardConfigActionType.REMOVE_TARGET_FROM_WHITELIST) {
342
+ _executeRemoveTargetFromWhitelist(action.data);
343
+ } else if (action.actionType == IGuardController.GuardConfigActionType.REGISTER_FUNCTION) {
344
+ _executeRegisterFunction(action.data);
345
+ } else if (action.actionType == IGuardController.GuardConfigActionType.UNREGISTER_FUNCTION) {
346
+ _executeUnregisterFunction(action.data);
347
+ } else {
348
+ revert SharedValidation.NotSupported();
349
+ }
350
+ }
351
+ }
352
+
353
+ /**
354
+ * @dev Executes ADD_TARGET_TO_WHITELIST: adds a target address to a function's call whitelist
355
+ * @param data ABI-encoded (bytes4 functionSelector, address target)
356
+ */
357
+ function _executeAddTargetToWhitelist(bytes calldata data) internal {
358
+ (bytes4 functionSelector, address target) = abi.decode(data, (bytes4, address));
359
+ _addTargetToWhitelist(functionSelector, target);
360
+ _logGuardConfigEvent(IGuardController.GuardConfigActionType.ADD_TARGET_TO_WHITELIST, functionSelector, target);
361
+ }
362
+
363
+ /**
364
+ * @dev Executes REMOVE_TARGET_FROM_WHITELIST: removes a target address from a function's call whitelist
365
+ * @param data ABI-encoded (bytes4 functionSelector, address target)
366
+ */
367
+ function _executeRemoveTargetFromWhitelist(bytes calldata data) internal {
368
+ (bytes4 functionSelector, address target) = abi.decode(data, (bytes4, address));
369
+ _removeTargetFromWhitelist(functionSelector, target);
370
+ _logGuardConfigEvent(IGuardController.GuardConfigActionType.REMOVE_TARGET_FROM_WHITELIST, functionSelector, target);
371
+ }
372
+
373
+ /**
374
+ * @dev Executes REGISTER_FUNCTION: registers a new function schema with signature, operation name, and supported actions
375
+ * @param data ABI-encoded (string functionSignature, string operationName, TxAction[] supportedActions)
376
+ */
377
+ function _executeRegisterFunction(bytes calldata data) internal {
378
+ (
379
+ string memory functionSignature,
380
+ string memory operationName,
381
+ EngineBlox.TxAction[] memory supportedActions
382
+ ) = abi.decode(data, (string, string, EngineBlox.TxAction[]));
383
+
384
+ bytes4 functionSelector = _registerGuardedFunction(functionSignature, operationName, supportedActions);
385
+ _logGuardConfigEvent(IGuardController.GuardConfigActionType.REGISTER_FUNCTION, functionSelector, address(0));
386
+ }
387
+
388
+ /**
389
+ * @dev Executes UNREGISTER_FUNCTION: unregisters a function schema by selector
390
+ * @param data ABI-encoded (bytes4 functionSelector, bool safeRemoval)
391
+ */
392
+ function _executeUnregisterFunction(bytes calldata data) internal {
393
+ (bytes4 functionSelector, bool safeRemoval) = abi.decode(data, (bytes4, bool));
394
+ _unregisterFunction(functionSelector, safeRemoval);
395
+ _logGuardConfigEvent(IGuardController.GuardConfigActionType.UNREGISTER_FUNCTION, functionSelector, address(0));
396
+ }
397
+
398
+ /**
399
+ * @dev Encodes and logs a guard config event via ComponentEvent. Payload decodes as (GuardConfigActionType, bytes4 functionSelector, address target).
400
+ * @param actionType The guard config action type
401
+ * @param functionSelector The function selector (or zero for N/A)
402
+ * @param target The target address (or zero for N/A)
403
+ */
404
+ function _logGuardConfigEvent(
405
+ IGuardController.GuardConfigActionType actionType,
406
+ bytes4 functionSelector,
407
+ address target
408
+ ) internal {
409
+ _logComponentEvent(abi.encode(actionType, functionSelector, target));
410
+ }
411
+
412
+ // ============ INTERNAL FUNCTION SCHEMA HELPERS ============
413
+
414
+ /**
415
+ * @dev Internal helper to register a new function schema
416
+ * @param functionSignature The function signature
417
+ * @param operationName The operation name
418
+ * @param supportedActions Array of supported actions
419
+ * @return functionSelector The derived function selector
420
+ */
421
+ function _registerGuardedFunction(
422
+ string memory functionSignature,
423
+ string memory operationName,
424
+ EngineBlox.TxAction[] memory supportedActions
425
+ ) internal returns (bytes4 functionSelector) {
426
+ // Derive function selector from signature
427
+ functionSelector = bytes4(keccak256(bytes(functionSignature)));
428
+
429
+ // Convert actions array to bitmap
430
+ uint16 supportedActionsBitmap = _createBitmapFromActions(supportedActions);
431
+
432
+ // Create function schema directly (always non-protected)
433
+ // Dynamically registered functions are execution selectors (handlerForSelectors must contain self-reference)
434
+ // EngineBlox.registerFunction validates schema doesn't already exist (ResourceAlreadyExists)
435
+ bytes4[] memory executionHandlers = new bytes4[](1);
436
+ executionHandlers[0] = functionSelector; // Self-reference for execution selector
437
+ _registerFunction(
438
+ functionSignature,
439
+ functionSelector,
440
+ operationName,
441
+ supportedActionsBitmap,
442
+ false, // isProtected = false for dynamically registered functions
443
+ executionHandlers // handlerForSelectors with self-reference for execution selectors
444
+ );
445
+ }
446
+
447
+ }