@bloxchain/contracts 1.0.0-alpha.2 → 1.0.0-alpha.20

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 (42) hide show
  1. package/README.md +7 -7
  2. package/abi/BaseStateMachine.abi.json +85 -45
  3. package/abi/EngineBlox.abi.json +73 -90
  4. package/abi/GuardController.abi.json +252 -806
  5. package/abi/{SimpleVaultDefinitions.abi.json → GuardControllerDefinitions.abi.json} +170 -28
  6. package/abi/IDefinition.abi.json +5 -0
  7. package/abi/RuntimeRBAC.abi.json +155 -218
  8. package/abi/RuntimeRBACDefinitions.abi.json +179 -0
  9. package/abi/SecureOwnable.abi.json +524 -1621
  10. package/abi/SecureOwnableDefinitions.abi.json +5 -0
  11. package/components/README.md +8 -0
  12. package/core/access/RuntimeRBAC.sol +255 -270
  13. package/core/access/interface/IRuntimeRBAC.sol +55 -84
  14. package/core/access/lib/definitions/RuntimeRBACDefinitions.sol +93 -2
  15. package/core/base/BaseStateMachine.sol +193 -107
  16. package/core/base/interface/IBaseStateMachine.sol +153 -153
  17. package/core/execution/GuardController.sol +155 -131
  18. package/core/execution/interface/IGuardController.sol +146 -120
  19. package/core/execution/lib/definitions/GuardControllerDefinitions.sol +193 -43
  20. package/core/lib/EngineBlox.sol +2683 -2322
  21. package/{interfaces → core/lib/interfaces}/IDefinition.sol +49 -49
  22. package/{interfaces → core/lib/interfaces}/IEventForwarder.sol +33 -33
  23. package/{utils → core/lib/utils}/SharedValidation.sol +61 -8
  24. package/core/pattern/Account.sol +84 -0
  25. package/core/security/SecureOwnable.sol +456 -412
  26. package/core/security/interface/ISecureOwnable.sol +105 -104
  27. package/core/security/lib/definitions/SecureOwnableDefinitions.sol +22 -6
  28. package/package.json +5 -5
  29. package/standards/README.md +12 -0
  30. package/standards/behavior/ICopyable.sol +34 -0
  31. package/standards/hooks/IOnActionHook.sol +21 -0
  32. package/abi/AccountBlox.abi.json +0 -5799
  33. package/abi/BareBlox.abi.json +0 -1284
  34. package/abi/RoleBlox.abi.json +0 -4209
  35. package/abi/SecureBlox.abi.json +0 -3828
  36. package/abi/SimpleRWA20.abi.json +0 -5288
  37. package/abi/SimpleRWA20Definitions.abi.json +0 -191
  38. package/abi/SimpleVault.abi.json +0 -4951
  39. package/core/research/BloxchainWallet.sol +0 -306
  40. package/core/research/erc20-blox/ERC20Blox.sol +0 -140
  41. package/core/research/erc20-blox/lib/definitions/ERC20BloxDefinitions.sol +0 -185
  42. package/interfaces/IOnActionHook.sol +0 -79
@@ -1,10 +1,10 @@
1
1
  // SPDX-License-Identifier: MPL-2.0
2
- pragma solidity 0.8.33;
2
+ pragma solidity 0.8.34;
3
3
 
4
4
  import "../base/BaseStateMachine.sol";
5
- import "../../utils/SharedValidation.sol";
5
+ import "../lib/utils/SharedValidation.sol";
6
6
  import "./lib/definitions/GuardControllerDefinitions.sol";
7
- import "../../interfaces/IDefinition.sol";
7
+ import "../lib/interfaces/IDefinition.sol";
8
8
  import "./interface/IGuardController.sol";
9
9
 
10
10
  /**
@@ -17,7 +17,6 @@ import "./interface/IGuardController.sol";
17
17
  *
18
18
  * Key Features:
19
19
  * - Core state machine functionality from BaseStateMachine
20
- * - Function schema query support (functionSchemaExists)
21
20
  * - STANDARD execution type only (function selector + params)
22
21
  * - Meta-transaction support for delegated approvals and cancellations
23
22
  * - Payment management for native tokens and ERC20 tokens
@@ -33,7 +32,7 @@ import "./interface/IGuardController.sol";
33
32
  *
34
33
  * Usage Flow:
35
34
  * 1. Deploy GuardController (or combine with RuntimeRBAC/SecureOwnable for role management)
36
- * 2. Function schemas should be registered via definitions or RuntimeRBAC if combined
35
+ * 2. Function schemas are registered via definitions at init or via GuardController guard config batch (REGISTER_FUNCTION)
37
36
  * 3. Create roles and assign function permissions with action bitmaps (via RuntimeRBAC if combined)
38
37
  * 4. Assign wallets to roles (via RuntimeRBAC if combined)
39
38
  * 5. Configure target whitelists per function selector (REQUIRED for execution)
@@ -51,7 +50,7 @@ import "./interface/IGuardController.sol";
51
50
  * - getAllowedTargets: Query whitelisted targets for a function selector
52
51
  *
53
52
  * @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
53
+ * @notice Target whitelist **state** is enforced by **EngineBlox** (`_validateTargetWhitelist`); GuardController configures it via guard batches. Public execute paths here also apply **`_validateNotInternalFunction`**: self-target is limited to **macro** selectors unless extended by inheritors.
55
54
  * @custom:security-contact security@particlecrypto.com
56
55
  */
57
56
  abstract contract GuardController is BaseStateMachine {
@@ -72,18 +71,15 @@ abstract contract GuardController is BaseStateMachine {
72
71
  uint256 timeLockPeriodSec,
73
72
  address eventForwarder
74
73
  ) public virtual onlyInitializing {
75
- // Initialize base state machine (only if not already initialized)
76
- if (!_secureState.initialized) {
77
- _initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
78
- }
79
-
74
+ _initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
75
+
80
76
  // Load GuardController-specific definitions
81
77
  IDefinition.RolePermission memory guardControllerPermissions = GuardControllerDefinitions.getRolePermissions();
82
78
  _loadDefinitions(
83
79
  GuardControllerDefinitions.getFunctionSchemas(),
84
80
  guardControllerPermissions.roleHashes,
85
81
  guardControllerPermissions.functionPermissions,
86
- true // Allow protected schemas for factory settings
82
+ true // Enforce all function schemas are protected
87
83
  );
88
84
  }
89
85
 
@@ -102,7 +98,7 @@ abstract contract GuardController is BaseStateMachine {
102
98
  /**
103
99
  * @dev Requests a time-locked execution via EngineBlox workflow
104
100
  * @param target The address of the target contract
105
- * @param value The ETH value to send (0 for standard function calls)
101
+ * @param value The ETH value to send (typically 0 for standard function calls; non-zero is supported for payable edge-case workflows)
106
102
  * @param functionSelector The function selector to execute (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
107
103
  * @param params The encoded parameters for the function (empty for simple native token transfers)
108
104
  * @param gasLimit The gas limit for execution
@@ -110,8 +106,9 @@ abstract contract GuardController is BaseStateMachine {
110
106
  * @return txId The transaction ID for the requested operation
111
107
  * @notice Creates a time-locked transaction that must be approved after the timelock period
112
108
  * @notice Requires EXECUTE_TIME_DELAY_REQUEST permission for the function selector
113
- * @notice For standard function calls: value=0, functionSelector=non-zero, params=encoded data
114
- * @notice For simple native token transfers: value>0, functionSelector=NATIVE_TRANSFER_SELECTOR, params=""
109
+ * @notice Recommended standard calls: value=0, functionSelector=non-zero, params=encoded data
110
+ * @notice Flexible edge case: non-native selectors may intentionally forward ETH to payable targets
111
+ * @notice Native-only convenience flow: value>0, functionSelector=NATIVE_TRANSFER_SELECTOR, params=""
115
112
  */
116
113
  function executeWithTimeLock(
117
114
  address target,
@@ -120,7 +117,7 @@ abstract contract GuardController is BaseStateMachine {
120
117
  bytes memory params,
121
118
  uint256 gasLimit,
122
119
  bytes32 operationType
123
- ) public returns (EngineBlox.TxRecord memory) {
120
+ ) public returns (uint256 txId) {
124
121
  // SECURITY: Prevent access to internal execution functions
125
122
  _validateNotInternalFunction(target, functionSelector);
126
123
 
@@ -134,91 +131,133 @@ abstract contract GuardController is BaseStateMachine {
134
131
  functionSelector,
135
132
  params
136
133
  );
137
- return txRecord;
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 (typically 0 for standard function calls; non-zero is supported for payable edge-case workflows)
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;
138
172
  }
139
173
 
140
174
  /**
141
175
  * @dev Approves and executes a time-locked transaction
142
176
  * @param txId The transaction ID
143
- * @return result The execution result
177
+ * @return txId The transaction ID
144
178
  * @notice Requires STANDARD execution type and EXECUTE_TIME_DELAY_APPROVE permission for the execution function
145
179
  */
146
180
  function approveTimeLockExecution(
147
181
  uint256 txId
148
- ) public returns (EngineBlox.TxRecord memory) {
182
+ ) public returns (uint256) {
149
183
  // SECURITY: Prevent access to internal execution functions
150
184
  EngineBlox.TxRecord memory txRecord = _getSecureState().txRecords[txId];
151
185
  _validateNotInternalFunction(txRecord.params.target, txRecord.params.executionSelector);
152
186
 
153
187
  // Approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
154
- return _approveTransaction(txId);
188
+ txRecord = _approveTransaction(txId);
189
+ return txRecord.txId;
155
190
  }
156
191
 
157
192
  /**
158
193
  * @dev Cancels a time-locked transaction
159
194
  * @param txId The transaction ID
160
- * @return The updated transaction record
195
+ * @return The transaction ID
161
196
  * @notice Requires STANDARD execution type and EXECUTE_TIME_DELAY_CANCEL permission for the execution function
162
197
  */
163
198
  function cancelTimeLockExecution(
164
199
  uint256 txId
165
- ) public returns (EngineBlox.TxRecord memory) {
200
+ ) public returns (uint256) {
166
201
  // SECURITY: Prevent access to internal execution functions
167
202
  EngineBlox.TxRecord memory txRecord = _getSecureState().txRecords[txId];
168
203
  _validateNotInternalFunction(txRecord.params.target, txRecord.params.executionSelector);
169
204
 
170
205
  // Cancel via BaseStateMachine helper (validates permissions in EngineBlox)
171
- return _cancelTransaction(txId);
206
+ txRecord = _cancelTransaction(txId);
207
+ return txRecord.txId;
172
208
  }
173
209
 
174
210
  /**
175
211
  * @dev Approves a time-locked transaction using a meta-transaction
176
212
  * @param metaTx The meta-transaction containing the transaction record and signature
177
- * @return The updated transaction record
213
+ * @return The transaction ID
178
214
  * @notice Requires STANDARD execution type and EXECUTE_META_APPROVE permission for the execution function
179
215
  */
180
216
  function approveTimeLockExecutionWithMetaTx(
181
217
  EngineBlox.MetaTransaction memory metaTx
182
- ) public returns (EngineBlox.TxRecord memory) {
218
+ ) public returns (uint256) {
183
219
  // SECURITY: Prevent access to internal execution functions
184
220
  _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
185
221
 
186
222
  // Approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
187
- return _approveTransactionWithMetaTx(metaTx);
223
+ EngineBlox.TxRecord memory txRecord = _approveTransactionWithMetaTx(metaTx);
224
+ return txRecord.txId;
188
225
  }
189
226
 
190
227
  /**
191
228
  * @dev Cancels a time-locked transaction using a meta-transaction
192
229
  * @param metaTx The meta-transaction containing the transaction record and signature
193
- * @return The updated transaction record
230
+ * @return The transaction ID
194
231
  * @notice Requires STANDARD execution type and EXECUTE_META_CANCEL permission for the execution function
195
232
  */
196
233
  function cancelTimeLockExecutionWithMetaTx(
197
234
  EngineBlox.MetaTransaction memory metaTx
198
- ) public returns (EngineBlox.TxRecord memory) {
235
+ ) public returns (uint256) {
199
236
  // SECURITY: Prevent access to internal execution functions
200
237
  _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
201
238
 
202
239
  // Cancel via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
203
- return _cancelTransactionWithMetaTx(metaTx);
240
+ EngineBlox.TxRecord memory txRecord = _cancelTransactionWithMetaTx(metaTx);
241
+ return txRecord.txId;
204
242
  }
205
243
 
206
244
  /**
207
245
  * @dev Requests and approves a transaction in one step using a meta-transaction
208
246
  * @param metaTx The meta-transaction containing the transaction record and signature
209
- * @return The transaction record after request and approval
247
+ * @return The transaction ID
210
248
  * @notice Requires STANDARD execution type
211
249
  * @notice Validates function schema and permissions for the execution function (same as executeWithTimeLock)
212
250
  * @notice Requires EXECUTE_META_REQUEST_AND_APPROVE permission for the execution function selector
213
251
  */
214
252
  function requestAndApproveExecution(
215
253
  EngineBlox.MetaTransaction memory metaTx
216
- ) public returns (EngineBlox.TxRecord memory) {
254
+ ) public returns (uint256) {
217
255
  // SECURITY: Prevent access to internal execution functions
218
256
  _validateNotInternalFunction(metaTx.txRecord.params.target, metaTx.txRecord.params.executionSelector);
219
257
 
220
258
  // Request and approve via BaseStateMachine helper (validates permissions and whitelist in EngineBlox)
221
- return _requestAndApproveTransaction(metaTx);
259
+ EngineBlox.TxRecord memory txRecord = _requestAndApproveTransaction(metaTx);
260
+ return txRecord.txId;
222
261
  }
223
262
 
224
263
  // Note: Meta-transaction utility functions (createMetaTxParams,
@@ -236,9 +275,8 @@ abstract contract GuardController is BaseStateMachine {
236
275
  * @param functionSelector The function selector to validate
237
276
  * @notice Internal functions use validateInternalCallInternal and should only be called
238
277
  * through the contract's own workflow, not via GuardController
239
- * @notice Blocks all calls to address(this) to prevent bypassing internal-only protection
240
- * @notice Exception: System macro selectors (e.g., NATIVE_TRANSFER_SELECTOR) are allowed
241
- * to target address(this) for system-level operations like native token deposits
278
+ * @notice Complements `EngineBlox._validateTargetWhitelist`, which **allows** `target == address(this)` for registered selectors so internal execution does not require listing `address(this)` on every whitelist. This helper **narrows GuardController’s surface**: self-target is rejected unless `functionSelector` is a **system macro** selector.
279
+ * @notice Exception path: macro selectors (e.g. `NATIVE_TRANSFER_SELECTOR`) may target `address(this)` for system-level operations such as native token flows.
242
280
  */
243
281
  function _validateNotInternalFunction(
244
282
  address target,
@@ -266,22 +304,28 @@ abstract contract GuardController is BaseStateMachine {
266
304
  /**
267
305
  * @dev Requests and approves a Guard configuration batch using a meta-transaction
268
306
  * @param metaTx The meta-transaction
269
- * @return The transaction record
307
+ * @return The transaction ID
270
308
  * @notice OWNER signs, BROADCASTER executes according to GuardControllerDefinitions
271
309
  */
272
310
  function guardConfigBatchRequestAndApprove(
273
311
  EngineBlox.MetaTransaction memory metaTx
274
- ) public returns (EngineBlox.TxRecord memory) {
312
+ ) public returns (uint256) {
275
313
  _validateBroadcaster(msg.sender);
276
-
277
- return _requestAndApproveTransaction(metaTx);
314
+ SharedValidation.validateEmptyPayment(
315
+ metaTx.txRecord.payment.recipient,
316
+ metaTx.txRecord.payment.nativeTokenAmount,
317
+ metaTx.txRecord.payment.erc20TokenAddress,
318
+ metaTx.txRecord.payment.erc20TokenAmount
319
+ );
320
+ EngineBlox.TxRecord memory txRecord = _requestAndApproveTransaction(metaTx);
321
+ return txRecord.txId;
278
322
  }
279
323
 
280
324
  /**
281
325
  * @dev External function that can only be called by the contract itself to execute a Guard configuration batch
282
326
  * @param actions Encoded guard configuration actions
283
327
  */
284
- function executeGuardConfigBatch(GuardControllerDefinitions.GuardConfigAction[] calldata actions) external {
328
+ function executeGuardConfigBatch(IGuardController.GuardConfigAction[] calldata actions) external {
285
329
  _validateExecuteBySelf();
286
330
  _executeGuardConfigBatch(actions);
287
331
  }
@@ -292,48 +336,20 @@ abstract contract GuardController is BaseStateMachine {
292
336
  * @dev Internal helper to execute a Guard configuration batch
293
337
  * @param actions Encoded guard configuration actions
294
338
  */
295
- function _executeGuardConfigBatch(GuardControllerDefinitions.GuardConfigAction[] calldata actions) internal {
339
+ function _executeGuardConfigBatch(IGuardController.GuardConfigAction[] calldata actions) internal {
296
340
  _validateBatchSize(actions.length);
297
341
 
298
342
  for (uint256 i = 0; i < actions.length; i++) {
299
- GuardControllerDefinitions.GuardConfigAction calldata action = actions[i];
300
-
301
- if (action.actionType == GuardControllerDefinitions.GuardConfigActionType.ADD_TARGET_TO_WHITELIST) {
302
- // Decode ADD_TARGET_TO_WHITELIST action data
303
- // Format: (bytes4 functionSelector, address target)
304
- (bytes4 functionSelector, address target) = abi.decode(action.data, (bytes4, address));
305
-
306
- _addTargetToFunctionWhitelist(functionSelector, target);
307
-
308
- _logComponentEvent(_encodeGuardConfigEvent(GuardControllerDefinitions.GuardConfigActionType.ADD_TARGET_TO_WHITELIST, functionSelector, target));
309
- } else if (action.actionType == GuardControllerDefinitions.GuardConfigActionType.REMOVE_TARGET_FROM_WHITELIST) {
310
- // Decode REMOVE_TARGET_FROM_WHITELIST action data
311
- // Format: (bytes4 functionSelector, address target)
312
- (bytes4 functionSelector, address target) = abi.decode(action.data, (bytes4, address));
313
-
314
- _removeTargetFromFunctionWhitelist(functionSelector, target);
315
-
316
- _logComponentEvent(_encodeGuardConfigEvent(GuardControllerDefinitions.GuardConfigActionType.REMOVE_TARGET_FROM_WHITELIST, functionSelector, target));
317
- } else if (action.actionType == GuardControllerDefinitions.GuardConfigActionType.REGISTER_FUNCTION) {
318
- // Decode REGISTER_FUNCTION action data
319
- // Format: (string functionSignature, string operationName, TxAction[] supportedActions)
320
- (
321
- string memory functionSignature,
322
- string memory operationName,
323
- EngineBlox.TxAction[] memory supportedActions
324
- ) = abi.decode(action.data, (string, string, EngineBlox.TxAction[]));
325
-
326
- bytes4 functionSelector = _registerFunction(functionSignature, operationName, supportedActions);
327
-
328
- _logComponentEvent(_encodeGuardConfigEvent(GuardControllerDefinitions.GuardConfigActionType.REGISTER_FUNCTION, functionSelector, address(0)));
329
- } else if (action.actionType == GuardControllerDefinitions.GuardConfigActionType.UNREGISTER_FUNCTION) {
330
- // Decode UNREGISTER_FUNCTION action data
331
- // Format: (bytes4 functionSelector, bool safeRemoval)
332
- (bytes4 functionSelector, bool safeRemoval) = abi.decode(action.data, (bytes4, bool));
333
-
334
- _unregisterFunction(functionSelector, safeRemoval);
335
-
336
- _logComponentEvent(_encodeGuardConfigEvent(GuardControllerDefinitions.GuardConfigActionType.UNREGISTER_FUNCTION, functionSelector, address(0)));
343
+ IGuardController.GuardConfigAction calldata action = actions[i];
344
+
345
+ if (action.actionType == IGuardController.GuardConfigActionType.ADD_TARGET_TO_WHITELIST) {
346
+ _executeAddTargetToWhitelist(action.data);
347
+ } else if (action.actionType == IGuardController.GuardConfigActionType.REMOVE_TARGET_FROM_WHITELIST) {
348
+ _executeRemoveTargetFromWhitelist(action.data);
349
+ } else if (action.actionType == IGuardController.GuardConfigActionType.REGISTER_FUNCTION) {
350
+ _executeRegisterFunction(action.data);
351
+ } else if (action.actionType == IGuardController.GuardConfigActionType.UNREGISTER_FUNCTION) {
352
+ _executeUnregisterFunction(action.data);
337
353
  } else {
338
354
  revert SharedValidation.NotSupported();
339
355
  }
@@ -341,14 +357,62 @@ abstract contract GuardController is BaseStateMachine {
341
357
  }
342
358
 
343
359
  /**
344
- * @dev Encodes guard config event payload for ComponentEvent. Decode as (GuardConfigActionType, bytes4 functionSelector, address target).
360
+ * @dev Executes ADD_TARGET_TO_WHITELIST: adds a target address to a function's call whitelist
361
+ * @param data ABI-encoded (bytes4 functionSelector, address target)
362
+ */
363
+ function _executeAddTargetToWhitelist(bytes calldata data) internal {
364
+ (bytes4 functionSelector, address target) = abi.decode(data, (bytes4, address));
365
+ _addTargetToWhitelist(functionSelector, target);
366
+ _logGuardConfigEvent(IGuardController.GuardConfigActionType.ADD_TARGET_TO_WHITELIST, functionSelector, target);
367
+ }
368
+
369
+ /**
370
+ * @dev Executes REMOVE_TARGET_FROM_WHITELIST: removes a target address from a function's call whitelist
371
+ * @param data ABI-encoded (bytes4 functionSelector, address target)
372
+ */
373
+ function _executeRemoveTargetFromWhitelist(bytes calldata data) internal {
374
+ (bytes4 functionSelector, address target) = abi.decode(data, (bytes4, address));
375
+ _removeTargetFromWhitelist(functionSelector, target);
376
+ _logGuardConfigEvent(IGuardController.GuardConfigActionType.REMOVE_TARGET_FROM_WHITELIST, functionSelector, target);
377
+ }
378
+
379
+ /**
380
+ * @dev Executes REGISTER_FUNCTION: registers a new function schema with signature, operation name, and supported actions
381
+ * @param data ABI-encoded (string functionSignature, string operationName, TxAction[] supportedActions)
382
+ */
383
+ function _executeRegisterFunction(bytes calldata data) internal {
384
+ (
385
+ string memory functionSignature,
386
+ string memory operationName,
387
+ EngineBlox.TxAction[] memory supportedActions
388
+ ) = abi.decode(data, (string, string, EngineBlox.TxAction[]));
389
+
390
+ bytes4 functionSelector = _registerGuardedFunction(functionSignature, operationName, supportedActions);
391
+ _logGuardConfigEvent(IGuardController.GuardConfigActionType.REGISTER_FUNCTION, functionSelector, address(0));
392
+ }
393
+
394
+ /**
395
+ * @dev Executes UNREGISTER_FUNCTION: unregisters a function schema by selector
396
+ * @param data ABI-encoded (bytes4 functionSelector, bool safeRemoval)
397
+ */
398
+ function _executeUnregisterFunction(bytes calldata data) internal {
399
+ (bytes4 functionSelector, bool safeRemoval) = abi.decode(data, (bytes4, bool));
400
+ _unregisterFunction(functionSelector, safeRemoval);
401
+ _logGuardConfigEvent(IGuardController.GuardConfigActionType.UNREGISTER_FUNCTION, functionSelector, address(0));
402
+ }
403
+
404
+ /**
405
+ * @dev Encodes and logs a guard config event via ComponentEvent. Payload decodes as (GuardConfigActionType, bytes4 functionSelector, address target).
406
+ * @param actionType The guard config action type
407
+ * @param functionSelector The function selector (or zero for N/A)
408
+ * @param target The target address (or zero for N/A)
345
409
  */
346
- function _encodeGuardConfigEvent(
347
- GuardControllerDefinitions.GuardConfigActionType actionType,
410
+ function _logGuardConfigEvent(
411
+ IGuardController.GuardConfigActionType actionType,
348
412
  bytes4 functionSelector,
349
413
  address target
350
- ) internal pure returns (bytes memory) {
351
- return abi.encode(actionType, functionSelector, target);
414
+ ) internal {
415
+ _logComponentEvent(abi.encode(actionType, functionSelector, target));
352
416
  }
353
417
 
354
418
  // ============ INTERNAL FUNCTION SCHEMA HELPERS ============
@@ -360,7 +424,7 @@ abstract contract GuardController is BaseStateMachine {
360
424
  * @param supportedActions Array of supported actions
361
425
  * @return functionSelector The derived function selector
362
426
  */
363
- function _registerFunction(
427
+ function _registerGuardedFunction(
364
428
  string memory functionSignature,
365
429
  string memory operationName,
366
430
  EngineBlox.TxAction[] memory supportedActions
@@ -368,63 +432,23 @@ abstract contract GuardController is BaseStateMachine {
368
432
  // Derive function selector from signature
369
433
  functionSelector = bytes4(keccak256(bytes(functionSignature)));
370
434
 
371
- // Validate that function schema doesn't already exist
372
- if (functionSchemaExists(functionSelector)) {
373
- revert SharedValidation.ResourceAlreadyExists(bytes32(functionSelector));
374
- }
375
-
376
435
  // Convert actions array to bitmap
377
436
  uint16 supportedActionsBitmap = _createBitmapFromActions(supportedActions);
378
437
 
379
438
  // Create function schema directly (always non-protected)
380
439
  // Dynamically registered functions are execution selectors (handlerForSelectors must contain self-reference)
381
- bytes4[] memory executionHandlerForSelectors = new bytes4[](1);
382
- executionHandlerForSelectors[0] = functionSelector; // Self-reference for execution selector
383
- _createFunctionSchema(
440
+ // EngineBlox.registerFunction validates schema doesn't already exist (ResourceAlreadyExists)
441
+ bytes4[] memory executionHandlers = new bytes4[](1);
442
+ executionHandlers[0] = functionSelector; // Self-reference for execution selector
443
+ _registerFunction(
384
444
  functionSignature,
385
445
  functionSelector,
386
446
  operationName,
387
447
  supportedActionsBitmap,
448
+ true, // enforceHandlerRelations for dynamically registered execution selectors
388
449
  false, // isProtected = false for dynamically registered functions
389
- executionHandlerForSelectors // handlerForSelectors with self-reference for execution selectors
450
+ executionHandlers // handlerForSelectors with self-reference for execution selectors
390
451
  );
391
452
  }
392
453
 
393
- /**
394
- * @dev Internal helper to unregister a function schema
395
- * @param functionSelector The function selector to unregister
396
- * @param safeRemoval If true, checks for role references before removal
397
- */
398
- function _unregisterFunction(bytes4 functionSelector, bool safeRemoval) internal {
399
- // Load schema and validate it exists
400
- EngineBlox.FunctionSchema storage schema = _getSecureState().functions[functionSelector];
401
- if (schema.functionSelector != functionSelector) {
402
- revert SharedValidation.ResourceNotFound(bytes32(functionSelector));
403
- }
404
-
405
- // Ensure not protected
406
- if (schema.isProtected) {
407
- revert SharedValidation.CannotModifyProtected(bytes32(functionSelector));
408
- }
409
-
410
- // The safeRemoval check is now handled within EngineBlox.removeFunctionSchema
411
- // (avoids getSupportedRolesList/getRoleFunctionPermissions which call _validateAnyRole;
412
- // during meta-tx execution msg.sender is the contract, causing NoPermission)
413
- _removeFunctionSchema(functionSelector, safeRemoval);
414
- }
415
-
416
- /**
417
- * @dev Gets all whitelisted targets for a function selector
418
- * @param functionSelector The function selector
419
- * @return Array of whitelisted target addresses
420
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
421
- */
422
- function getAllowedTargets(
423
- bytes4 functionSelector
424
- ) external view returns (address[] memory) {
425
- _validateAnyRole();
426
- return _getFunctionWhitelistTargets(functionSelector);
427
- }
428
454
  }
429
-
430
-