@bloxchain/contracts 1.0.0-alpha

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.
Files changed (34) hide show
  1. package/README.md +49 -0
  2. package/abi/BareBlox.abi.json +1341 -0
  3. package/abi/BaseStateMachine.abi.json +1308 -0
  4. package/abi/ControlBlox.abi.json +6210 -0
  5. package/abi/EngineBlox.abi.json +872 -0
  6. package/abi/GuardController.abi.json +3045 -0
  7. package/abi/IDefinition.abi.json +94 -0
  8. package/abi/RoleBlox.abi.json +4569 -0
  9. package/abi/RuntimeRBAC.abi.json +1857 -0
  10. package/abi/RuntimeRBACDefinitions.abi.json +133 -0
  11. package/abi/SecureBlox.abi.json +4085 -0
  12. package/abi/SecureOwnable.abi.json +4085 -0
  13. package/abi/SecureOwnableDefinitions.abi.json +354 -0
  14. package/abi/SimpleRWA20.abi.json +5545 -0
  15. package/abi/SimpleRWA20Definitions.abi.json +172 -0
  16. package/abi/SimpleVault.abi.json +5208 -0
  17. package/abi/SimpleVaultDefinitions.abi.json +250 -0
  18. package/contracts/core/access/RuntimeRBAC.sol +344 -0
  19. package/contracts/core/access/interface/IRuntimeRBAC.sol +108 -0
  20. package/contracts/core/access/lib/definitions/RuntimeRBACDefinitions.sol +168 -0
  21. package/contracts/core/base/BaseStateMachine.sol +834 -0
  22. package/contracts/core/base/interface/IBaseStateMachine.sol +153 -0
  23. package/contracts/core/execution/GuardController.sol +507 -0
  24. package/contracts/core/execution/interface/IGuardController.sol +120 -0
  25. package/contracts/core/execution/lib/definitions/GuardControllerDefinitions.sol +401 -0
  26. package/contracts/core/lib/EngineBlox.sol +2283 -0
  27. package/contracts/core/security/SecureOwnable.sol +419 -0
  28. package/contracts/core/security/interface/ISecureOwnable.sol +118 -0
  29. package/contracts/core/security/lib/definitions/SecureOwnableDefinitions.sol +757 -0
  30. package/contracts/interfaces/IDefinition.sol +40 -0
  31. package/contracts/interfaces/IEventForwarder.sol +33 -0
  32. package/contracts/interfaces/IOnActionHook.sol +79 -0
  33. package/contracts/utils/SharedValidation.sol +486 -0
  34. package/package.json +47 -0
@@ -0,0 +1,153 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ pragma solidity 0.8.33;
3
+
4
+ // Contracts imports
5
+ import "../../lib/EngineBlox.sol";
6
+
7
+ /**
8
+ * @title IBaseStateMachine
9
+ * @dev Interface for BaseStateMachine functionality
10
+ */
11
+ interface IBaseStateMachine {
12
+ // ============ CORE TRANSACTION MANAGEMENT ============
13
+
14
+ /**
15
+ * @dev Creates meta-transaction parameters with specified values
16
+ * @param handlerContract The contract that will handle the meta-transaction
17
+ * @param handlerSelector The function selector for the handler
18
+ * @param action The transaction action type
19
+ * @param deadline The timestamp after which the meta-transaction expires
20
+ * @param maxGasPrice The maximum gas price allowed for execution
21
+ * @param signer The address that will sign the meta-transaction
22
+ * @return The formatted meta-transaction parameters
23
+ */
24
+ function createMetaTxParams(
25
+ address handlerContract,
26
+ bytes4 handlerSelector,
27
+ EngineBlox.TxAction action,
28
+ uint256 deadline,
29
+ uint256 maxGasPrice,
30
+ address signer
31
+ ) external view returns (EngineBlox.MetaTxParams memory);
32
+
33
+ /**
34
+ * @dev Generates an unsigned meta-transaction for a new operation
35
+ * @param requester The address requesting the operation
36
+ * @param target The target contract address
37
+ * @param value The ETH value to send
38
+ * @param gasLimit The gas limit for execution
39
+ * @param operationType The type of operation
40
+ * @param executionSelector The function selector to execute (0x00000000 for simple ETH transfers)
41
+ * @param executionParams The encoded parameters for the function (empty for simple ETH transfers)
42
+ * @param metaTxParams The meta-transaction parameters
43
+ * @return The unsigned meta-transaction
44
+ */
45
+ function generateUnsignedMetaTransactionForNew(
46
+ address requester,
47
+ address target,
48
+ uint256 value,
49
+ uint256 gasLimit,
50
+ bytes32 operationType,
51
+ bytes4 executionSelector,
52
+ bytes memory executionParams,
53
+ EngineBlox.MetaTxParams memory metaTxParams
54
+ ) external view returns (EngineBlox.MetaTransaction memory);
55
+
56
+ /**
57
+ * @dev Generates an unsigned meta-transaction for an existing transaction
58
+ * @param txId The ID of the existing transaction
59
+ * @param metaTxParams The meta-transaction parameters
60
+ * @return The unsigned meta-transaction
61
+ */
62
+ function generateUnsignedMetaTransactionForExisting(
63
+ uint256 txId,
64
+ EngineBlox.MetaTxParams memory metaTxParams
65
+ ) external view returns (EngineBlox.MetaTransaction memory);
66
+
67
+ // ============ STATE QUERIES ============
68
+
69
+ /**
70
+ * @dev Gets transaction history within a specified range
71
+ * @param fromTxId The starting transaction ID (inclusive)
72
+ * @param toTxId The ending transaction ID (inclusive)
73
+ * @return The transaction history within the specified range
74
+ */
75
+ function getTransactionHistory(uint256 fromTxId, uint256 toTxId) external view returns (EngineBlox.TxRecord[] memory);
76
+
77
+ /**
78
+ * @dev Gets a transaction by ID
79
+ * @param txId The transaction ID
80
+ * @return The transaction record
81
+ */
82
+ function getTransaction(uint256 txId) external view returns (EngineBlox.TxRecord memory);
83
+
84
+ /**
85
+ * @dev Gets all pending transaction IDs
86
+ * @return Array of pending transaction IDs
87
+ */
88
+ function getPendingTransactions() external view returns (uint256[] memory);
89
+
90
+ // ============ ROLE AND PERMISSION QUERIES ============
91
+
92
+ /**
93
+ * @dev Returns if a wallet is authorized for a role
94
+ * @param roleHash The hash of the role to check
95
+ * @param wallet The wallet address to check
96
+ * @return True if the wallet is authorized for the role, false otherwise
97
+ */
98
+ function hasRole(bytes32 roleHash, address wallet) external view returns (bool);
99
+
100
+ /**
101
+ * @dev Returns if an action is supported by a function
102
+ * @param functionSelector The function selector to check
103
+ * @param action The action to check
104
+ * @return True if the action is supported by the function, false otherwise
105
+ */
106
+ function isActionSupportedByFunction(bytes4 functionSelector, EngineBlox.TxAction action) external view returns (bool);
107
+
108
+ /**
109
+ * @dev Gets the function permissions for a specific role
110
+ * @param roleHash The hash of the role to get permissions for
111
+ * @return The function permissions array for the role
112
+ */
113
+ function getActiveRolePermissions(bytes32 roleHash) external view returns (EngineBlox.FunctionPermission[] memory);
114
+
115
+ /**
116
+ * @dev Gets the current nonce for a specific signer
117
+ * @param signer The address of the signer
118
+ * @return The current nonce for the signer
119
+ */
120
+ function getSignerNonce(address signer) external view returns (uint256);
121
+
122
+ // ============ SYSTEM STATE QUERIES ============
123
+
124
+ /**
125
+ * @dev Returns the supported operation types
126
+ * @return The supported operation types
127
+ */
128
+ function getSupportedOperationTypes() external view returns (bytes32[] memory);
129
+
130
+ /**
131
+ * @dev Returns the supported roles list
132
+ * @return The supported roles list
133
+ */
134
+ function getSupportedRoles() external view returns (bytes32[] memory);
135
+
136
+ /**
137
+ * @dev Returns the supported functions list
138
+ * @return The supported functions list
139
+ */
140
+ function getSupportedFunctions() external view returns (bytes4[] memory);
141
+
142
+ /**
143
+ * @dev Returns the time lock period
144
+ * @return The time lock period in seconds
145
+ */
146
+ function getTimeLockPeriodSec() external view returns (uint256);
147
+
148
+ /**
149
+ * @dev Returns whether the contract is initialized
150
+ * @return bool True if the contract is initialized, false otherwise
151
+ */
152
+ function initialized() external view returns (bool);
153
+ }
@@ -0,0 +1,507 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ pragma solidity 0.8.33;
3
+
4
+ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
5
+ import "../base/BaseStateMachine.sol";
6
+ import "../../utils/SharedValidation.sol";
7
+ import "./lib/definitions/GuardControllerDefinitions.sol";
8
+ import "../../interfaces/IDefinition.sol";
9
+ import "./interface/IGuardController.sol";
10
+
11
+ /**
12
+ * @title GuardController
13
+ * @dev Lightweight controller for generic contract delegation with full EngineBlox workflows
14
+ *
15
+ * This contract provides a complete solution for delegating control to external addresses.
16
+ * It extends BaseStateMachine for core state machine functionality and supports all EngineBlox
17
+ * execution patterns including time-locked transactions, meta-transactions, and payment management.
18
+ *
19
+ * Key Features:
20
+ * - Core state machine functionality from BaseStateMachine
21
+ * - Function schema query support (functionSchemaExists)
22
+ * - STANDARD execution type only (function selector + params)
23
+ * - Meta-transaction support for delegated approvals and cancellations
24
+ * - Payment management for native tokens and ERC20 tokens
25
+ * - Role-based access control with action-level permissions
26
+ * - Target address whitelist per function selector (defense-in-depth security layer)
27
+ *
28
+ * Security Features:
29
+ * - Target whitelist: Strict security - restricts which contract addresses can be called per function selector
30
+ * - Prevents exploitation of global function selector permissions by limiting valid target contracts
31
+ * - Strict enforcement: Target MUST be explicitly whitelisted for the function selector
32
+ * - If whitelist is empty (no entries), no targets are allowed - explicit deny for security
33
+ * - Target whitelist is ALWAYS checked - no backward compatibility fallback
34
+ *
35
+ * Usage Flow:
36
+ * 1. Deploy GuardController (or combine with RuntimeRBAC/SecureOwnable for role management)
37
+ * 2. Function schemas should be registered via definitions or RuntimeRBAC if combined
38
+ * 3. Create roles and assign function permissions with action bitmaps (via RuntimeRBAC if combined)
39
+ * 4. Assign wallets to roles (via RuntimeRBAC if combined)
40
+ * 5. Configure target whitelists per function selector (REQUIRED for execution)
41
+ * 6. Execute operations via time-lock workflows based on action permissions
42
+ * 7. Target whitelist is ALWAYS validated before execution - target must be in whitelist
43
+ * 8. Target contract validates access (ownership/role-based)
44
+ *
45
+ * Workflows Available:
46
+ * - Standard execution: function selector + params
47
+ * - Time-locked approval: request + approve workflow
48
+ * - Meta-transaction workflows: signed approvals/cancellations
49
+ *
50
+ * Whitelist Management:
51
+ * - executeGuardConfigBatch: Batch execution for adding/removing targets from whitelist (OWNER_ROLE only)
52
+ * - getAllowedTargets: Query whitelisted targets for a function selector
53
+ *
54
+ * @notice This contract is modular and can be combined with RuntimeRBAC and SecureOwnable
55
+ * @notice Target whitelist is a GuardController-specific security feature, not part of EngineBlox library
56
+ * @custom:security-contact security@particlecrypto.com
57
+ */
58
+ abstract contract GuardController is BaseStateMachine {
59
+ using EngineBlox for EngineBlox.SecureOperationState;
60
+
61
+ /**
62
+ * @dev Action types for batched Guard configuration
63
+ */
64
+ enum GuardConfigActionType {
65
+ ADD_TARGET_TO_WHITELIST,
66
+ REMOVE_TARGET_FROM_WHITELIST,
67
+ REGISTER_FUNCTION,
68
+ UNREGISTER_FUNCTION
69
+ }
70
+
71
+ /**
72
+ * @dev Encodes a single Guard configuration action in a batch
73
+ */
74
+ struct GuardConfigAction {
75
+ GuardConfigActionType actionType;
76
+ bytes data;
77
+ }
78
+
79
+ // ============ EVENTS ============
80
+
81
+ /**
82
+ * @dev Unified event for all Guard configuration changes applied via batches
83
+ *
84
+ * - actionType: the high-level type of configuration action
85
+ * - functionSelector: affected function selector (if applicable, otherwise 0)
86
+ * - target: affected target address (if applicable, otherwise 0)
87
+ * - data: optional action-specific payload (kept minimal for size; decoded off-chain if needed)
88
+ */
89
+ event GuardConfigApplied(
90
+ GuardConfigActionType indexed actionType,
91
+ bytes4 indexed functionSelector,
92
+ address indexed target,
93
+ bytes data
94
+ );
95
+
96
+ /**
97
+ * @notice Initializer to initialize GuardController
98
+ * @param initialOwner The initial owner address
99
+ * @param broadcaster The broadcaster address
100
+ * @param recovery The recovery address
101
+ * @param timeLockPeriodSec The timelock period in seconds
102
+ * @param eventForwarder The event forwarder address
103
+ */
104
+ function initialize(
105
+ address initialOwner,
106
+ address broadcaster,
107
+ address recovery,
108
+ uint256 timeLockPeriodSec,
109
+ address eventForwarder
110
+ ) public virtual onlyInitializing {
111
+ // Initialize base state machine (only if not already initialized)
112
+ if (!_secureState.initialized) {
113
+ _initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
114
+ }
115
+
116
+ // Load GuardController-specific definitions
117
+ IDefinition.RolePermission memory guardControllerPermissions = GuardControllerDefinitions.getRolePermissions();
118
+ _loadDefinitions(
119
+ GuardControllerDefinitions.getFunctionSchemas(),
120
+ guardControllerPermissions.roleHashes,
121
+ guardControllerPermissions.functionPermissions
122
+ );
123
+ }
124
+
125
+ // ============ INTERFACE SUPPORT ============
126
+
127
+ /**
128
+ * @dev See {IERC165-supportsInterface}.
129
+ * @notice Adds IGuardController interface ID for component detection
130
+ */
131
+ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
132
+ return interfaceId == type(IGuardController).interfaceId || super.supportsInterface(interfaceId);
133
+ }
134
+
135
+ // ============ EXECUTION FUNCTIONS ============
136
+
137
+ /**
138
+ * @dev Requests a time-locked execution via EngineBlox workflow
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
+ * @return txId The transaction ID for the requested operation
146
+ * @notice Creates a time-locked transaction that must be approved after the timelock period
147
+ * @notice Requires EXECUTE_TIME_DELAY_REQUEST permission for the function selector
148
+ * @notice For standard function calls: value=0, functionSelector=non-zero, params=encoded data
149
+ * @notice For simple native token transfers: value>0, functionSelector=NATIVE_TRANSFER_SELECTOR, params=""
150
+ */
151
+ function executeWithTimeLock(
152
+ address target,
153
+ uint256 value,
154
+ bytes4 functionSelector,
155
+ bytes memory params,
156
+ uint256 gasLimit,
157
+ bytes32 operationType
158
+ ) public returns (EngineBlox.TxRecord memory) {
159
+ // Validate inputs
160
+ SharedValidation.validateNotZeroAddress(target);
161
+
162
+ // SECURITY: Prevent access to internal execution functions
163
+ _validateNotInternalFunction(target, functionSelector);
164
+
165
+ // Request via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
166
+ EngineBlox.TxRecord memory txRecord = _requestTransaction(
167
+ msg.sender,
168
+ target,
169
+ value,
170
+ gasLimit,
171
+ operationType,
172
+ functionSelector,
173
+ params
174
+ );
175
+ return txRecord;
176
+ }
177
+
178
+ /**
179
+ * @dev Approves and executes a time-locked transaction
180
+ * @param txId The transaction ID
181
+ * @return result The execution result
182
+ * @notice Requires STANDARD execution type and EXECUTE_TIME_DELAY_APPROVE permission for the execution function
183
+ */
184
+ function approveTimeLockExecution(
185
+ uint256 txId
186
+ ) public returns (EngineBlox.TxRecord memory) {
187
+ // SECURITY: Prevent access to internal execution functions
188
+ EngineBlox.TxRecord memory txRecord = _getSecureState().txRecords[txId];
189
+ _validateNotInternalFunction(txRecord.params.target, txRecord.params.executionSelector);
190
+
191
+ // Approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
192
+ return _approveTransaction(txId);
193
+ }
194
+
195
+ /**
196
+ * @dev Cancels a time-locked transaction
197
+ * @param txId The transaction ID
198
+ * @return The updated transaction record
199
+ * @notice Requires STANDARD execution type and EXECUTE_TIME_DELAY_CANCEL permission for the execution function
200
+ */
201
+ function cancelTimeLockExecution(
202
+ uint256 txId
203
+ ) public returns (EngineBlox.TxRecord memory) {
204
+ // SECURITY: Prevent access to internal execution functions
205
+ EngineBlox.TxRecord memory txRecord = _getSecureState().txRecords[txId];
206
+ _validateNotInternalFunction(txRecord.params.target, txRecord.params.executionSelector);
207
+
208
+ // Cancel via BaseStateMachine helper (validates permissions in EngineBlox)
209
+ return _cancelTransaction(txId);
210
+ }
211
+
212
+ /**
213
+ * @dev Approves a time-locked transaction using a meta-transaction
214
+ * @param metaTx The meta-transaction containing the transaction record and signature
215
+ * @return The updated transaction record
216
+ * @notice Requires STANDARD execution type and EXECUTE_META_APPROVE permission for the execution function
217
+ */
218
+ function approveTimeLockExecutionWithMetaTx(
219
+ EngineBlox.MetaTransaction memory metaTx
220
+ ) public returns (EngineBlox.TxRecord memory) {
221
+ // SECURITY: Prevent access to internal execution functions
222
+ _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
223
+
224
+ // Approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
225
+ return _approveTransactionWithMetaTx(metaTx);
226
+ }
227
+
228
+ /**
229
+ * @dev Cancels a time-locked transaction using a meta-transaction
230
+ * @param metaTx The meta-transaction containing the transaction record and signature
231
+ * @return The updated transaction record
232
+ * @notice Requires STANDARD execution type and EXECUTE_META_CANCEL permission for the execution function
233
+ */
234
+ function cancelTimeLockExecutionWithMetaTx(
235
+ EngineBlox.MetaTransaction memory metaTx
236
+ ) public returns (EngineBlox.TxRecord memory) {
237
+ // SECURITY: Prevent access to internal execution functions
238
+ _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
239
+
240
+ // Cancel via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
241
+ return _cancelTransactionWithMetaTx(metaTx);
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 (EngineBlox.TxRecord memory) {
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
+ return _requestAndApproveTransaction(metaTx);
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 Checks if a function selector is a known system macro selector
273
+ * @param functionSelector The function selector to check
274
+ * @return true if the selector is a known system macro selector, false otherwise
275
+ * @notice System macro selectors are special selectors that represent system-level operations
276
+ * and are allowed to bypass certain security restrictions
277
+ * @notice Currently known macro selectors:
278
+ * - NATIVE_TRANSFER_SELECTOR: For native token transfers
279
+ */
280
+ function _isSystemMacroSelector(bytes4 functionSelector) internal pure returns (bool) {
281
+ return functionSelector == EngineBlox.NATIVE_TRANSFER_SELECTOR
282
+ || functionSelector == EngineBlox.UPDATE_PAYMENT_SELECTOR;
283
+ }
284
+
285
+ /**
286
+ * @dev Validates that GuardController is not attempting to access internal execution functions
287
+ * @param target The target contract address
288
+ * @param functionSelector The function selector to validate
289
+ * @notice Internal functions use validateInternalCallInternal and should only be called
290
+ * through the contract's own workflow, not via GuardController
291
+ * @notice Blocks all calls to address(this) to prevent bypassing internal-only protection
292
+ * @notice Exception: System macro selectors (e.g., NATIVE_TRANSFER_SELECTOR) are allowed
293
+ * to target address(this) for system-level operations like native token deposits
294
+ */
295
+ function _validateNotInternalFunction(
296
+ address target,
297
+ bytes4 functionSelector
298
+ ) internal view {
299
+ // SECURITY: Prevent GuardController from accessing internal execution functions
300
+ // Internal functions use validateInternalCallInternal and should only be called
301
+ // through the contract's own workflow, not via GuardController
302
+
303
+ // If target is this contract, we need to validate the function selector
304
+ if (target == address(this)) {
305
+ // Allow system macro selectors (e.g., NATIVE_TRANSFER_SELECTOR for native token deposits)
306
+ // These are special system-level operations that are safe to execute on address(this)
307
+ if (_isSystemMacroSelector(functionSelector)) {
308
+ return; // Allow system macro selectors
309
+ }
310
+
311
+ // Block all other calls to address(this) to prevent bypassing internal-only protection
312
+ revert SharedValidation.InternalFunctionNotAccessible(functionSelector);
313
+ }
314
+ }
315
+
316
+ // ============ GUARD CONFIGURATION BATCH INTERFACE ============
317
+
318
+ /**
319
+ * @dev Creates execution params for a Guard configuration batch
320
+ * @param actions Encoded guard configuration actions
321
+ * @return The execution params for EngineBlox
322
+ */
323
+ function guardConfigBatchExecutionParams(
324
+ GuardConfigAction[] memory actions
325
+ ) public pure returns (bytes memory) {
326
+ return abi.encode(actions);
327
+ }
328
+
329
+ /**
330
+ * @dev Requests and approves a Guard configuration batch using a meta-transaction
331
+ * @param metaTx The meta-transaction
332
+ * @return The transaction record
333
+ * @notice OWNER signs, BROADCASTER executes according to GuardControllerDefinitions
334
+ */
335
+ function guardConfigBatchRequestAndApprove(
336
+ EngineBlox.MetaTransaction memory metaTx
337
+ ) public returns (EngineBlox.TxRecord memory) {
338
+ _validateBroadcaster(msg.sender);
339
+ SharedValidation.validateOwnerIsSigner(metaTx.params.signer, owner());
340
+
341
+ return _requestAndApproveTransaction(metaTx);
342
+ }
343
+
344
+ /**
345
+ * @dev External function that can only be called by the contract itself to execute a Guard configuration batch
346
+ * @param actions Encoded guard configuration actions
347
+ */
348
+ function executeGuardConfigBatch(GuardConfigAction[] calldata actions) external {
349
+ SharedValidation.validateInternalCall(address(this));
350
+ _executeGuardConfigBatch(actions);
351
+ }
352
+
353
+ // ============ HELPER FUNCTIONS ============
354
+
355
+ /**
356
+ * @dev Internal helper to execute a Guard configuration batch
357
+ * @param actions Encoded guard configuration actions
358
+ */
359
+ function _executeGuardConfigBatch(GuardConfigAction[] calldata actions) internal {
360
+ // Validate batch size limit
361
+ SharedValidation.validateBatchSize(
362
+ actions.length,
363
+ EngineBlox.MAX_BATCH_SIZE
364
+ );
365
+
366
+ for (uint256 i = 0; i < actions.length; i++) {
367
+ GuardConfigAction calldata action = actions[i];
368
+
369
+ if (action.actionType == GuardConfigActionType.ADD_TARGET_TO_WHITELIST) {
370
+ // Decode ADD_TARGET_TO_WHITELIST action data
371
+ // Format: (bytes4 functionSelector, address target)
372
+ (bytes4 functionSelector, address target) = abi.decode(action.data, (bytes4, address));
373
+
374
+ _addTargetToFunctionWhitelist(functionSelector, target);
375
+
376
+ emit GuardConfigApplied(
377
+ GuardConfigActionType.ADD_TARGET_TO_WHITELIST,
378
+ functionSelector,
379
+ target,
380
+ "" // optional: could encode additional data if needed
381
+ );
382
+ } else if (action.actionType == GuardConfigActionType.REMOVE_TARGET_FROM_WHITELIST) {
383
+ // Decode REMOVE_TARGET_FROM_WHITELIST action data
384
+ // Format: (bytes4 functionSelector, address target)
385
+ (bytes4 functionSelector, address target) = abi.decode(action.data, (bytes4, address));
386
+
387
+ _removeTargetFromFunctionWhitelist(functionSelector, target);
388
+
389
+ emit GuardConfigApplied(
390
+ GuardConfigActionType.REMOVE_TARGET_FROM_WHITELIST,
391
+ functionSelector,
392
+ target,
393
+ ""
394
+ );
395
+ } else if (action.actionType == GuardConfigActionType.REGISTER_FUNCTION) {
396
+ // Decode REGISTER_FUNCTION action data
397
+ // Format: (string functionSignature, string operationName, TxAction[] supportedActions)
398
+ (
399
+ string memory functionSignature,
400
+ string memory operationName,
401
+ EngineBlox.TxAction[] memory supportedActions
402
+ ) = abi.decode(action.data, (string, string, EngineBlox.TxAction[]));
403
+
404
+ bytes4 functionSelector = _registerFunction(functionSignature, operationName, supportedActions);
405
+
406
+ emit GuardConfigApplied(
407
+ GuardConfigActionType.REGISTER_FUNCTION,
408
+ functionSelector,
409
+ address(0),
410
+ "" // optional: abi.encode(operationName)
411
+ );
412
+ } else if (action.actionType == GuardConfigActionType.UNREGISTER_FUNCTION) {
413
+ // Decode UNREGISTER_FUNCTION action data
414
+ // Format: (bytes4 functionSelector, bool safeRemoval)
415
+ (bytes4 functionSelector, bool safeRemoval) = abi.decode(action.data, (bytes4, bool));
416
+
417
+ _unregisterFunction(functionSelector, safeRemoval);
418
+
419
+ emit GuardConfigApplied(
420
+ GuardConfigActionType.UNREGISTER_FUNCTION,
421
+ functionSelector,
422
+ address(0),
423
+ ""
424
+ );
425
+ } else {
426
+ revert SharedValidation.NotSupported();
427
+ }
428
+ }
429
+ }
430
+
431
+ // ============ INTERNAL FUNCTION SCHEMA HELPERS ============
432
+
433
+ /**
434
+ * @dev Internal helper to register a new function schema
435
+ * @param functionSignature The function signature
436
+ * @param operationName The operation name
437
+ * @param supportedActions Array of supported actions
438
+ * @return functionSelector The derived function selector
439
+ */
440
+ function _registerFunction(
441
+ string memory functionSignature,
442
+ string memory operationName,
443
+ EngineBlox.TxAction[] memory supportedActions
444
+ ) internal returns (bytes4 functionSelector) {
445
+ // Derive function selector from signature
446
+ functionSelector = bytes4(keccak256(bytes(functionSignature)));
447
+
448
+ // Validate that function schema doesn't already exist
449
+ if (functionSchemaExists(functionSelector)) {
450
+ revert SharedValidation.ResourceAlreadyExists(bytes32(functionSelector));
451
+ }
452
+
453
+ // Convert actions array to bitmap
454
+ uint16 supportedActionsBitmap = _createBitmapFromActions(supportedActions);
455
+
456
+ // Create function schema directly (always non-protected)
457
+ // Dynamically registered functions are execution selectors (handlerForSelectors must contain self-reference)
458
+ bytes4[] memory executionHandlerForSelectors = new bytes4[](1);
459
+ executionHandlerForSelectors[0] = functionSelector; // Self-reference for execution selector
460
+ _createFunctionSchema(
461
+ functionSignature,
462
+ functionSelector,
463
+ operationName,
464
+ supportedActionsBitmap,
465
+ false, // isProtected = false for dynamically registered functions
466
+ executionHandlerForSelectors // handlerForSelectors with self-reference for execution selectors
467
+ );
468
+ }
469
+
470
+ /**
471
+ * @dev Internal helper to unregister a function schema
472
+ * @param functionSelector The function selector to unregister
473
+ * @param safeRemoval If true, checks for role references before removal
474
+ */
475
+ function _unregisterFunction(bytes4 functionSelector, bool safeRemoval) internal {
476
+ // Load schema and validate it exists
477
+ EngineBlox.FunctionSchema storage schema = _getSecureState().functions[functionSelector];
478
+ if (schema.functionSelector != functionSelector) {
479
+ revert SharedValidation.ResourceNotFound(bytes32(functionSelector));
480
+ }
481
+
482
+ // Ensure not protected
483
+ if (schema.isProtected) {
484
+ revert SharedValidation.CannotModifyProtected(bytes32(functionSelector));
485
+ }
486
+
487
+ // The safeRemoval check is now handled within EngineBlox.removeFunctionSchema
488
+ // (avoids getSupportedRolesList/getRoleFunctionPermissions which call _validateAnyRole;
489
+ // during meta-tx execution msg.sender is the contract, causing NoPermission)
490
+ _removeFunctionSchema(functionSelector, safeRemoval);
491
+ }
492
+
493
+ /**
494
+ * @dev Gets all whitelisted targets for a function selector
495
+ * @param functionSelector The function selector
496
+ * @return Array of whitelisted target addresses
497
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
498
+ */
499
+ function getAllowedTargets(
500
+ bytes4 functionSelector
501
+ ) external view returns (address[] memory) {
502
+ _validateAnyRole();
503
+ return _getFunctionWhitelistTargets(functionSelector);
504
+ }
505
+ }
506
+
507
+