@bloxchain/contracts 1.0.0-alpha.6 → 1.0.0

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 (54) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +8 -9
  3. package/abi/BaseStateMachine.abi.json +773 -822
  4. package/abi/EngineBlox.abi.json +562 -552
  5. package/abi/GuardController.abi.json +1597 -1609
  6. package/abi/GuardControllerDefinitions.abi.json +257 -120
  7. package/abi/IDefinition.abi.json +57 -47
  8. package/abi/RuntimeRBAC.abi.json +841 -842
  9. package/abi/RuntimeRBACDefinitions.abi.json +265 -99
  10. package/abi/SecureOwnable.abi.json +1365 -1349
  11. package/abi/SecureOwnableDefinitions.abi.json +174 -164
  12. package/components/README.md +8 -0
  13. package/core/AUDIT.md +45 -0
  14. package/core/access/RuntimeRBAC.sol +130 -61
  15. package/core/access/interface/IRuntimeRBAC.sol +3 -3
  16. package/core/access/lib/definitions/RuntimeRBACDefinitions.sol +67 -3
  17. package/core/base/BaseStateMachine.sol +971 -967
  18. package/core/base/interface/IBaseStateMachine.sol +153 -160
  19. package/core/execution/GuardController.sol +89 -75
  20. package/core/execution/interface/IGuardController.sol +146 -160
  21. package/core/execution/lib/definitions/GuardControllerDefinitions.sol +180 -24
  22. package/core/lib/EngineBlox.sol +577 -327
  23. package/core/lib/interfaces/IDefinition.sol +49 -49
  24. package/core/lib/interfaces/IEventForwarder.sol +4 -2
  25. package/core/lib/utils/SharedValidation.sol +534 -487
  26. package/core/pattern/Account.sol +84 -65
  27. package/core/security/SecureOwnable.sol +446 -390
  28. package/core/security/interface/ISecureOwnable.sol +105 -105
  29. package/core/security/lib/definitions/SecureOwnableDefinitions.sol +49 -17
  30. package/package.json +11 -7
  31. package/standards/README.md +12 -0
  32. package/{core/research → standards/behavior}/ICopyable.sol +3 -11
  33. package/standards/hooks/IOnActionHook.sol +21 -0
  34. package/abi/AccountBlox.abi.json +0 -3916
  35. package/abi/BareBlox.abi.json +0 -1378
  36. package/abi/RoleBlox.abi.json +0 -2983
  37. package/abi/SecureBlox.abi.json +0 -2753
  38. package/abi/SimpleRWA20.abi.json +0 -4032
  39. package/abi/SimpleRWA20Definitions.abi.json +0 -191
  40. package/abi/SimpleVault.abi.json +0 -3407
  41. package/abi/SimpleVaultDefinitions.abi.json +0 -269
  42. package/core/research/BloxchainWallet.sol +0 -292
  43. package/core/research/FactoryBlox/FactoryBlox.sol +0 -346
  44. package/core/research/FactoryBlox/FactoryBloxDefinitions.sol +0 -143
  45. package/core/research/erc1155-blox/ERC1155Blox.sol +0 -169
  46. package/core/research/erc1155-blox/lib/definitions/ERC1155BloxDefinitions.sol +0 -203
  47. package/core/research/erc20-blox/ERC20Blox.sol +0 -167
  48. package/core/research/erc20-blox/lib/definitions/ERC20BloxDefinitions.sol +0 -185
  49. package/core/research/erc721-blox/ERC721Blox.sol +0 -131
  50. package/core/research/erc721-blox/lib/definitions/ERC721BloxDefinitions.sol +0 -172
  51. package/core/research/lending-blox/.gitkeep +0 -1
  52. package/core/research/p2p-blox/P2PBlox.sol +0 -266
  53. package/core/research/p2p-blox/README.md +0 -85
  54. package/core/research/p2p-blox/lib/definitions/P2PBloxDefinitions.sol +0 -19
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MPL-2.0
2
- pragma solidity 0.8.33;
2
+ pragma solidity 0.8.35;
3
3
 
4
4
  // Contract imports
5
5
  import "../base/BaseStateMachine.sol";
@@ -12,18 +12,31 @@ import "./interface/IRuntimeRBAC.sol";
12
12
  /**
13
13
  * @title RuntimeRBAC
14
14
  * @dev Minimal Runtime Role-Based Access Control system based on EngineBlox
15
- *
15
+ *
16
16
  * This contract provides essential runtime RBAC functionality:
17
17
  * - Creation of non-protected roles
18
18
  * - Basic wallet assignment to roles
19
19
  * - Function permission management per role
20
20
  * - Integration with EngineBlox for secure operations
21
- *
21
+ *
22
22
  * Key Features:
23
23
  * - Only non-protected roles can be created dynamically
24
24
  * - Protected roles (OWNER, BROADCASTER, RECOVERY) are managed by SecureOwnable
25
25
  * - Minimal interface for core RBAC operations
26
26
  * - Essential role management functions only
27
+ *
28
+ * @custom:security PROTECTED-ROLE POLICY (defense in layers):
29
+ * - RuntimeRBAC is **unauthorized** to modify protected roles (wallet add/revoke/remove).
30
+ * - For ADD_WALLET and REVOKE_WALLET we call _requireRoleNotProtected so batch ops cannot
31
+ * change who holds system roles. For REMOVE_ROLE we rely on EngineBlox.removeRole, which
32
+ * enforces the same policy at the library layer (cannot remove protected roles).
33
+ * - Function-permission updates on protected roles are intentionally supported for flexibility.
34
+ * EngineBlox.removeFunctionFromRole allows removing a grant whenever the schema's **`isGrantRevocable`**
35
+ * is true (including from protected roles); **`GrantNotRevocable`** applies when it is false.
36
+ * - The **only** place to modify system wallets (protected roles) is the SecureOwnable
37
+ * security component (e.g. transferOwnershipRequest, broadcaster/recovery changes).
38
+ * - This layering is intentional: RBAC cannot touch protected roles; SecureOwnable is the
39
+ * single source of truth for system wallet changes.
27
40
  */
28
41
  abstract contract RuntimeRBAC is BaseStateMachine, IRuntimeRBAC {
29
42
  using EngineBlox for EngineBlox.SecureOperationState;
@@ -44,18 +57,15 @@ abstract contract RuntimeRBAC is BaseStateMachine, IRuntimeRBAC {
44
57
  uint256 timeLockPeriodSec,
45
58
  address eventForwarder
46
59
  ) public virtual onlyInitializing {
47
- // Initialize base state machine (only if not already initialized)
48
- if (!_secureState.initialized) {
49
- _initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
50
- }
51
-
60
+ _initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
61
+
52
62
  // Load RuntimeRBAC-specific definitions
53
63
  IDefinition.RolePermission memory permissions = RuntimeRBACDefinitions.getRolePermissions();
54
64
  _loadDefinitions(
55
65
  RuntimeRBACDefinitions.getFunctionSchemas(),
56
66
  permissions.roleHashes,
57
67
  permissions.functionPermissions,
58
- true // Allow protected schemas for factory settings
68
+ true // Enforce all function schemas are protected
59
69
  );
60
70
  }
61
71
 
@@ -74,13 +84,19 @@ abstract contract RuntimeRBAC is BaseStateMachine, IRuntimeRBAC {
74
84
  /**
75
85
  * @dev Requests and approves a RBAC configuration batch using a meta-transaction
76
86
  * @param metaTx The meta-transaction
77
- * @return The transaction record
87
+ * @return The transaction ID of the applied batch
78
88
  * @notice OWNER signs, BROADCASTER executes according to RuntimeRBACDefinitions
79
89
  */
80
90
  function roleConfigBatchRequestAndApprove(
81
91
  EngineBlox.MetaTransaction memory metaTx
82
92
  ) public returns (uint256) {
83
93
  _validateBroadcaster(msg.sender);
94
+ SharedValidation.validateEmptyPayment(
95
+ metaTx.txRecord.payment.recipient,
96
+ metaTx.txRecord.payment.nativeTokenAmount,
97
+ metaTx.txRecord.payment.erc20TokenAddress,
98
+ metaTx.txRecord.payment.erc20TokenAmount
99
+ );
84
100
  EngineBlox.TxRecord memory txRecord = _requestAndApproveTransaction(metaTx);
85
101
  return txRecord.txId;
86
102
  }
@@ -88,6 +104,13 @@ abstract contract RuntimeRBAC is BaseStateMachine, IRuntimeRBAC {
88
104
  /**
89
105
  * @dev External function that can only be called by the contract itself to execute a RBAC configuration batch
90
106
  * @param actions Encoded role configuration actions
107
+ *
108
+ * ## Role config batch ordering (required to avoid revert and gas waste)
109
+ *
110
+ * Actions must be ordered so that dependencies are satisfied:
111
+ * - **CREATE_ROLE** must appear before **ADD_WALLET** or **ADD_FUNCTION_TO_ROLE** for the same role; otherwise the role does not exist and the add will revert.
112
+ * - **REMOVE_ROLE** should be used only for an existing role; use **REVOKE_WALLET** first if the role has assigned wallets (optional but recommended for clarity).
113
+ * - For a given role, typical order: CREATE_ROLE → ADD_WALLET / ADD_FUNCTION_TO_ROLE as needed; to remove: REVOKE_WALLET (and REMOVE_FUNCTION_FROM_ROLE) as needed → REMOVE_ROLE.
91
114
  */
92
115
  function executeRoleConfigBatch(IRuntimeRBAC.RoleConfigAction[] calldata actions) external {
93
116
  _validateExecuteBySelf();
@@ -98,6 +121,9 @@ abstract contract RuntimeRBAC is BaseStateMachine, IRuntimeRBAC {
98
121
 
99
122
  /**
100
123
  * @dev Reverts if the role is protected (prevents editing OWNER, BROADCASTER, RECOVERY via batch).
124
+ * Used for ADD_WALLET and REVOKE_WALLET so RuntimeRBAC cannot change who holds system roles.
125
+ * REMOVE_ROLE is not checked here; EngineBlox.removeRole enforces protected-role policy at
126
+ * the library layer. See contract-level @custom:security PROTECTED-ROLE POLICY.
101
127
  * @param roleHash The role hash to check
102
128
  */
103
129
  function _requireRoleNotProtected(bytes32 roleHash) internal view {
@@ -109,66 +135,29 @@ abstract contract RuntimeRBAC is BaseStateMachine, IRuntimeRBAC {
109
135
  /**
110
136
  * @dev Internal helper to execute a RBAC configuration batch
111
137
  * @param actions Encoded role configuration actions
138
+ *
139
+ * @custom:order Required ordering to avoid revert and gas waste:
140
+ * 1. CREATE_ROLE before any ADD_WALLET or ADD_FUNCTION_TO_ROLE for that role.
141
+ * 2. REMOVE_ROLE only for a role that exists; prefer REVOKE_WALLET (and REMOVE_FUNCTION_FROM_ROLE) before REMOVE_ROLE when the role has members.
112
142
  */
113
143
  function _executeRoleConfigBatch(IRuntimeRBAC.RoleConfigAction[] calldata actions) internal {
114
144
  _validateBatchSize(actions.length);
115
145
 
116
- for (uint256 i = 0; i < actions.length; i++) {
146
+ for (uint256 i = 0; i < actions.length; ++i) {
117
147
  IRuntimeRBAC.RoleConfigAction calldata action = actions[i];
118
148
 
119
149
  if (action.actionType == IRuntimeRBAC.RoleConfigActionType.CREATE_ROLE) {
120
- // Decode CREATE_ROLE action data
121
- // Format: (string roleName, uint256 maxWallets)
122
- (
123
- string memory roleName,
124
- uint256 maxWallets
125
- ) = abi.decode(action.data, (string, uint256));
126
-
127
- // Create the role in the secure state with isProtected = false
128
- bytes32 roleHash = _createRole(roleName, maxWallets, false);
129
-
130
- _logComponentEvent(_encodeRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.CREATE_ROLE, roleHash, bytes4(0)));
150
+ _executeCreateRole(action.data);
131
151
  } else if (action.actionType == IRuntimeRBAC.RoleConfigActionType.REMOVE_ROLE) {
132
- // Decode REMOVE_ROLE action data
133
- // Format: (bytes32 roleHash)
134
- (bytes32 roleHash) = abi.decode(action.data, (bytes32));
135
- _removeRole(roleHash);
136
-
137
- _logComponentEvent(_encodeRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.REMOVE_ROLE, roleHash, bytes4(0)));
152
+ _executeRemoveRole(action.data);
138
153
  } else if (action.actionType == IRuntimeRBAC.RoleConfigActionType.ADD_WALLET) {
139
- // Decode ADD_WALLET action data
140
- // Format: (bytes32 roleHash, address wallet)
141
- (bytes32 roleHash, address wallet) = abi.decode(action.data, (bytes32, address));
142
- _requireRoleNotProtected(roleHash);
143
- _assignWallet(roleHash, wallet);
144
-
145
- _logComponentEvent(_encodeRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.ADD_WALLET, roleHash, bytes4(0)));
154
+ _executeAddWallet(action.data);
146
155
  } else if (action.actionType == IRuntimeRBAC.RoleConfigActionType.REVOKE_WALLET) {
147
- // Decode REVOKE_WALLET action data
148
- // Format: (bytes32 roleHash, address wallet)
149
- (bytes32 roleHash, address wallet) = abi.decode(action.data, (bytes32, address));
150
- _requireRoleNotProtected(roleHash);
151
- _revokeWallet(roleHash, wallet);
152
-
153
- _logComponentEvent(_encodeRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.REVOKE_WALLET, roleHash, bytes4(0)));
156
+ _executeRevokeWallet(action.data);
154
157
  } else if (action.actionType == IRuntimeRBAC.RoleConfigActionType.ADD_FUNCTION_TO_ROLE) {
155
- // Decode ADD_FUNCTION_TO_ROLE action data
156
- // Format: (bytes32 roleHash, FunctionPermission functionPermission)
157
- (
158
- bytes32 roleHash,
159
- EngineBlox.FunctionPermission memory functionPermission
160
- ) = abi.decode(action.data, (bytes32, EngineBlox.FunctionPermission));
161
-
162
- _addFunctionToRole(roleHash, functionPermission);
163
-
164
- _logComponentEvent(_encodeRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.ADD_FUNCTION_TO_ROLE, roleHash, functionPermission.functionSelector));
158
+ _executeAddFunctionToRole(action.data);
165
159
  } else if (action.actionType == IRuntimeRBAC.RoleConfigActionType.REMOVE_FUNCTION_FROM_ROLE) {
166
- // Decode REMOVE_FUNCTION_FROM_ROLE action data
167
- // Format: (bytes32 roleHash, bytes4 functionSelector)
168
- (bytes32 roleHash, bytes4 functionSelector) = abi.decode(action.data, (bytes32, bytes4));
169
- _removeFunctionFromRole(roleHash, functionSelector);
170
-
171
- _logComponentEvent(_encodeRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.REMOVE_FUNCTION_FROM_ROLE, roleHash, functionSelector));
160
+ _executeRemoveFunctionFromRole(action.data);
172
161
  } else {
173
162
  revert SharedValidation.NotSupported();
174
163
  }
@@ -176,9 +165,89 @@ abstract contract RuntimeRBAC is BaseStateMachine, IRuntimeRBAC {
176
165
  }
177
166
 
178
167
  /**
179
- * @dev Encodes RBAC config event payload for ComponentEvent. Decode as (RoleConfigActionType, bytes32 roleHash, bytes4 functionSelector).
168
+ * @dev Executes CREATE_ROLE: creates a new non-protected role
169
+ * @param data ABI-encoded (string roleName, uint256 maxWallets)
170
+ */
171
+ function _executeCreateRole(bytes calldata data) internal {
172
+ (string memory roleName, uint256 maxWallets) = abi.decode(data, (string, uint256));
173
+ bytes32 roleHash = _createRole(roleName, maxWallets, false);
174
+ _logRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.CREATE_ROLE, roleHash, bytes4(0), address(0));
175
+ }
176
+
177
+ /**
178
+ * @dev Executes REMOVE_ROLE: removes a role by hash.
179
+ * Protected-role check is enforced in EngineBlox.removeRole (library layer); RuntimeRBAC
180
+ * does not duplicate it here. SecureOwnable is the only component authorized to change
181
+ * system wallets; RBAC is unauthorized to modify protected roles. See @custom:security
182
+ * PROTECTED-ROLE POLICY on the contract.
183
+ * @param data ABI-encoded (bytes32 roleHash)
184
+ */
185
+ function _executeRemoveRole(bytes calldata data) internal {
186
+ (bytes32 roleHash) = abi.decode(data, (bytes32));
187
+ _removeRole(roleHash);
188
+ _logRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.REMOVE_ROLE, roleHash, bytes4(0), address(0));
189
+ }
190
+
191
+ /**
192
+ * @dev Executes ADD_WALLET: assigns a wallet to a role (role must not be protected)
193
+ * @param data ABI-encoded (bytes32 roleHash, address wallet)
194
+ */
195
+ function _executeAddWallet(bytes calldata data) internal {
196
+ (bytes32 roleHash, address wallet) = abi.decode(data, (bytes32, address));
197
+ _requireRoleNotProtected(roleHash);
198
+ _assignWallet(roleHash, wallet);
199
+ _logRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.ADD_WALLET, roleHash, bytes4(0), wallet);
200
+ }
201
+
202
+ /**
203
+ * @dev Executes REVOKE_WALLET: revokes a wallet from a role (role must not be protected)
204
+ * @param data ABI-encoded (bytes32 roleHash, address wallet)
205
+ */
206
+ function _executeRevokeWallet(bytes calldata data) internal {
207
+ (bytes32 roleHash, address wallet) = abi.decode(data, (bytes32, address));
208
+ _requireRoleNotProtected(roleHash);
209
+ _revokeWallet(roleHash, wallet);
210
+ _logRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.REVOKE_WALLET, roleHash, bytes4(0), wallet);
211
+ }
212
+
213
+ /**
214
+ * @dev Executes ADD_FUNCTION_TO_ROLE: adds a function permission to a role.
215
+ * @param data ABI-encoded (bytes32 roleHash, FunctionPermission functionPermission)
216
+ * @custom:security By design we allow adding function permissions to protected roles (OWNER, BROADCASTER, RECOVERY)
217
+ * to retain flexibility to grant new function permissions to system roles; only wallet add/revoke
218
+ * are restricted on protected roles.
219
+ */
220
+ function _executeAddFunctionToRole(bytes calldata data) internal {
221
+ (
222
+ bytes32 roleHash,
223
+ EngineBlox.FunctionPermission memory functionPermission
224
+ ) = abi.decode(data, (bytes32, EngineBlox.FunctionPermission));
225
+ _addFunctionToRole(roleHash, functionPermission);
226
+ _logRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.ADD_FUNCTION_TO_ROLE, roleHash, functionPermission.functionSelector, address(0));
227
+ }
228
+
229
+ /**
230
+ * @dev Executes REMOVE_FUNCTION_FROM_ROLE: removes a function permission from a role.
231
+ * @param data ABI-encoded (bytes32 roleHash, bytes4 functionSelector)
232
+ * @custom:security By design we allow removing function permissions from protected roles (OWNER, BROADCASTER, RECOVERY)
233
+ * for flexibility; only wallet add/revoke are restricted on protected roles. EngineBlox.removeFunctionFromRole
234
+ * enforces **`GrantNotRevocable`** when the schema's **`isGrantRevocable`** is false; when true,
235
+ * grants may be removed from protected roles as well as custom roles.
236
+ */
237
+ function _executeRemoveFunctionFromRole(bytes calldata data) internal {
238
+ (bytes32 roleHash, bytes4 functionSelector) = abi.decode(data, (bytes32, bytes4));
239
+ _removeFunctionFromRole(roleHash, functionSelector);
240
+ _logRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType.REMOVE_FUNCTION_FROM_ROLE, roleHash, functionSelector, address(0));
241
+ }
242
+
243
+ /**
244
+ * @dev Encodes and logs a role config event via ComponentEvent. Payload decodes as (RoleConfigActionType, bytes32 roleHash, bytes4 functionSelector, address wallet).
245
+ * @param action The role config action type
246
+ * @param roleHash The role hash
247
+ * @param selector The function selector (or zero for N/A)
248
+ * @param wallet The wallet address (or zero for actions that do not apply to a wallet)
180
249
  */
181
- function _encodeRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType action, bytes32 roleHash, bytes4 selector) internal pure returns (bytes memory) {
182
- return abi.encode(action, roleHash, selector);
250
+ function _logRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType action, bytes32 roleHash, bytes4 selector, address wallet) internal {
251
+ _logComponentEvent(abi.encode(action, roleHash, selector, wallet));
183
252
  }
184
253
  }
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MPL-2.0
2
- pragma solidity 0.8.33;
2
+ pragma solidity 0.8.35;
3
3
 
4
4
  import "../../lib/EngineBlox.sol";
5
5
 
@@ -12,7 +12,7 @@ import "../../lib/EngineBlox.sol";
12
12
  *
13
13
  * Key Features:
14
14
  * - Batch-based role configuration (atomic operations)
15
- * - Runtime function schema registration
15
+ * - Role and permission management (function schema registration is handled by GuardController)
16
16
  * - Integration with EngineBlox for secure operations
17
17
  * - Query functions for role and permission inspection
18
18
  *
@@ -47,7 +47,7 @@ interface IRuntimeRBAC {
47
47
  /**
48
48
  * @dev Requests and approves a RBAC configuration batch using a meta-transaction
49
49
  * @param metaTx The meta-transaction
50
- * @return The transaction record
50
+ * @return The transaction ID of the applied batch
51
51
  */
52
52
  function roleConfigBatchRequestAndApprove(
53
53
  EngineBlox.MetaTransaction memory metaTx
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MPL-2.0
2
- pragma solidity 0.8.33;
2
+ pragma solidity 0.8.35;
3
3
 
4
4
  import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
5
5
  import "../../../lib/EngineBlox.sol";
@@ -30,7 +30,7 @@ library RuntimeRBACDefinitions {
30
30
  bytes4 public constant ROLE_CONFIG_BATCH_META_SELECTOR =
31
31
  bytes4(
32
32
  keccak256(
33
- "roleConfigBatchRequestAndApprove(((uint256,uint256,uint8,(address,address,uint256,uint256,bytes32,bytes4,bytes),bytes32,bytes,(address,uint256,address,uint256)),(uint256,uint256,address,bytes4,uint8,uint256,uint256,address),bytes32,bytes,bytes))"
33
+ "roleConfigBatchRequestAndApprove(((uint256,uint256,uint8,(address,address,uint256,uint256,bytes32,bytes4,bytes),bytes32,bytes32,(address,uint256,address,uint256)),(uint256,uint256,address,bytes4,uint8,uint256,uint256,address),bytes32,bytes,bytes))"
34
34
  )
35
35
  );
36
36
 
@@ -57,12 +57,14 @@ library RuntimeRBACDefinitions {
57
57
  handlerForSelectors[0] = ROLE_CONFIG_BATCH_EXECUTE_SELECTOR;
58
58
 
59
59
  schemas[0] = EngineBlox.FunctionSchema({
60
- functionSignature: "roleConfigBatchRequestAndApprove(((uint256,uint256,uint8,(address,address,uint256,uint256,bytes32,bytes4,bytes),bytes32,bytes,(address,uint256,address,uint256)),(uint256,uint256,address,bytes4,uint8,uint256,uint256,address),bytes32,bytes,bytes))",
60
+ functionSignature: "roleConfigBatchRequestAndApprove(((uint256,uint256,uint8,(address,address,uint256,uint256,bytes32,bytes4,bytes),bytes32,bytes32,(address,uint256,address,uint256)),(uint256,uint256,address,bytes4,uint8,uint256,uint256,address),bytes32,bytes,bytes))",
61
61
  functionSelector: ROLE_CONFIG_BATCH_META_SELECTOR,
62
62
  operationType: ROLE_CONFIG_BATCH,
63
63
  operationName: "ROLE_CONFIG_BATCH",
64
64
  supportedActionsBitmap: EngineBlox.createBitmapFromActions(metaRequestApproveActions),
65
+ enforceHandlerRelations: true,
65
66
  isProtected: true,
67
+ isGrantRevocable: false,
66
68
  handlerForSelectors: handlerForSelectors
67
69
  });
68
70
 
@@ -83,7 +85,9 @@ library RuntimeRBACDefinitions {
83
85
  operationType: ROLE_CONFIG_BATCH,
84
86
  operationName: "ROLE_CONFIG_BATCH",
85
87
  supportedActionsBitmap: EngineBlox.createBitmapFromActions(executionActions),
88
+ enforceHandlerRelations: false,
86
89
  isProtected: true,
90
+ isGrantRevocable: false,
87
91
  handlerForSelectors: executionHandlerForSelectors
88
92
  });
89
93
 
@@ -197,6 +201,66 @@ library RuntimeRBACDefinitions {
197
201
  formats[5] = "(bytes32 roleHash, bytes4 functionSelector)";
198
202
  }
199
203
 
204
+ // ============ ROLE CONFIG ACTION DATA ENCODERS ============
205
+ // Use these helpers to build action.data for each RoleConfigActionType without reading the contract.
206
+ // Each encoder returns bytes suitable for RoleConfigAction(actionType, data).
207
+
208
+ /**
209
+ * @dev Encodes data for CREATE_ROLE. Use with RoleConfigActionType.CREATE_ROLE.
210
+ * @param roleName Name of the role to create
211
+ * @param maxWallets Maximum number of wallets that can be assigned to this role
212
+ */
213
+ function encodeCreateRole(string memory roleName, uint256 maxWallets) public pure returns (bytes memory) {
214
+ return abi.encode(roleName, maxWallets);
215
+ }
216
+
217
+ /**
218
+ * @dev Encodes data for REMOVE_ROLE. Use with RoleConfigActionType.REMOVE_ROLE.
219
+ * @param roleHash keccak256 hash of the role name
220
+ */
221
+ function encodeRemoveRole(bytes32 roleHash) public pure returns (bytes memory) {
222
+ return abi.encode(roleHash);
223
+ }
224
+
225
+ /**
226
+ * @dev Encodes data for ADD_WALLET. Use with RoleConfigActionType.ADD_WALLET.
227
+ * @param roleHash Role to add the wallet to
228
+ * @param wallet Address to assign to the role
229
+ */
230
+ function encodeAddWallet(bytes32 roleHash, address wallet) public pure returns (bytes memory) {
231
+ return abi.encode(roleHash, wallet);
232
+ }
233
+
234
+ /**
235
+ * @dev Encodes data for REVOKE_WALLET. Use with RoleConfigActionType.REVOKE_WALLET.
236
+ * @param roleHash Role to revoke the wallet from
237
+ * @param wallet Address to revoke
238
+ */
239
+ function encodeRevokeWallet(bytes32 roleHash, address wallet) public pure returns (bytes memory) {
240
+ return abi.encode(roleHash, wallet);
241
+ }
242
+
243
+ /**
244
+ * @dev Encodes data for ADD_FUNCTION_TO_ROLE. Use with RoleConfigActionType.ADD_FUNCTION_TO_ROLE.
245
+ * @param roleHash Role to grant the function permission to
246
+ * @param functionPermission FunctionPermission (functionSelector, grantedActionsBitmap, handlerForSelectors)
247
+ */
248
+ function encodeAddFunctionToRole(
249
+ bytes32 roleHash,
250
+ EngineBlox.FunctionPermission memory functionPermission
251
+ ) public pure returns (bytes memory) {
252
+ return abi.encode(roleHash, functionPermission);
253
+ }
254
+
255
+ /**
256
+ * @dev Encodes data for REMOVE_FUNCTION_FROM_ROLE. Use with RoleConfigActionType.REMOVE_FUNCTION_FROM_ROLE.
257
+ * @param roleHash Role to remove the function from
258
+ * @param functionSelector Selector of the function to remove
259
+ */
260
+ function encodeRemoveFunctionFromRole(bytes32 roleHash, bytes4 functionSelector) public pure returns (bytes memory) {
261
+ return abi.encode(roleHash, functionSelector);
262
+ }
263
+
200
264
  /**
201
265
  * @dev Creates execution params for a RBAC configuration batch (pure helper for EngineBlox).
202
266
  * @param actions Encoded role configuration actions (IRuntimeRBAC.RoleConfigAction[] layout)