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

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 (43) hide show
  1. package/README.md +7 -7
  2. package/abi/BaseStateMachine.abi.json +798 -753
  3. package/abi/EngineBlox.abi.json +566 -576
  4. package/abi/GuardController.abi.json +1546 -2095
  5. package/abi/GuardControllerDefinitions.abi.json +416 -0
  6. package/abi/IDefinition.abi.json +57 -47
  7. package/abi/RuntimeRBAC.abi.json +901 -959
  8. package/abi/RuntimeRBACDefinitions.abi.json +265 -81
  9. package/abi/SecureOwnable.abi.json +1522 -2581
  10. package/abi/SecureOwnableDefinitions.abi.json +174 -164
  11. package/components/README.md +8 -0
  12. package/core/access/RuntimeRBAC.sol +253 -270
  13. package/core/access/interface/IRuntimeRBAC.sol +55 -84
  14. package/core/access/lib/definitions/RuntimeRBACDefinitions.sol +97 -4
  15. package/core/base/BaseStateMachine.sol +198 -108
  16. package/core/base/interface/IBaseStateMachine.sol +153 -153
  17. package/core/execution/GuardController.sol +156 -131
  18. package/core/execution/interface/IGuardController.sol +146 -120
  19. package/core/execution/lib/definitions/GuardControllerDefinitions.sol +207 -45
  20. package/core/lib/EngineBlox.sol +2636 -2322
  21. package/{interfaces → core/lib/interfaces}/IDefinition.sol +49 -49
  22. package/{interfaces → core/lib/interfaces}/IEventForwarder.sol +5 -3
  23. package/{utils → core/lib/utils}/SharedValidation.sol +69 -22
  24. package/core/pattern/Account.sol +84 -0
  25. package/core/security/SecureOwnable.sol +180 -146
  26. package/core/security/interface/ISecureOwnable.sol +105 -104
  27. package/core/security/lib/definitions/SecureOwnableDefinitions.sol +818 -786
  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/abi/SimpleVaultDefinitions.abi.json +0 -269
  40. package/core/research/BloxchainWallet.sol +0 -306
  41. package/core/research/erc20-blox/ERC20Blox.sol +0 -140
  42. package/core/research/erc20-blox/lib/definitions/ERC20BloxDefinitions.sol +0 -185
  43. package/interfaces/IOnActionHook.sol +0 -79
@@ -1,270 +1,253 @@
1
- // SPDX-License-Identifier: MPL-2.0
2
- pragma solidity 0.8.33;
3
-
4
- // Contract imports
5
- import "../base/BaseStateMachine.sol";
6
- import "../lib/EngineBlox.sol";
7
- import "../../utils/SharedValidation.sol";
8
- import "./lib/definitions/RuntimeRBACDefinitions.sol";
9
- import "../../interfaces/IDefinition.sol";
10
- import "./interface/IRuntimeRBAC.sol";
11
-
12
- /**
13
- * @title RuntimeRBAC
14
- * @dev Minimal Runtime Role-Based Access Control system based on EngineBlox
15
- *
16
- * This contract provides essential runtime RBAC functionality:
17
- * - Creation of non-protected roles
18
- * - Basic wallet assignment to roles
19
- * - Function permission management per role
20
- * - Integration with EngineBlox for secure operations
21
- *
22
- * Key Features:
23
- * - Only non-protected roles can be created dynamically
24
- * - Protected roles (OWNER, BROADCASTER, RECOVERY) are managed by SecureOwnable
25
- * - Minimal interface for core RBAC operations
26
- * - Essential role management functions only
27
- */
28
- abstract contract RuntimeRBAC is BaseStateMachine {
29
- using EngineBlox for EngineBlox.SecureOperationState;
30
- using SharedValidation for *;
31
-
32
- /**
33
- * @dev Action types for batched RBAC configuration (must match IRuntimeRBAC for encoding)
34
- */
35
- enum RoleConfigActionType {
36
- CREATE_ROLE,
37
- REMOVE_ROLE,
38
- ADD_WALLET,
39
- REVOKE_WALLET,
40
- ADD_FUNCTION_TO_ROLE,
41
- REMOVE_FUNCTION_FROM_ROLE
42
- }
43
-
44
- /**
45
- * @dev Encodes a single RBAC configuration action in a batch (must match IRuntimeRBAC for encoding)
46
- */
47
- struct RoleConfigAction {
48
- RoleConfigActionType actionType;
49
- bytes data;
50
- }
51
-
52
- /**
53
- * @notice Initializer to initialize RuntimeRBAC
54
- * @param initialOwner The initial owner address
55
- * @param broadcaster The broadcaster address
56
- * @param recovery The recovery address
57
- * @param timeLockPeriodSec The timelock period in seconds
58
- * @param eventForwarder The event forwarder address
59
- */
60
- function initialize(
61
- address initialOwner,
62
- address broadcaster,
63
- address recovery,
64
- uint256 timeLockPeriodSec,
65
- address eventForwarder
66
- ) public virtual onlyInitializing {
67
- // Initialize base state machine (only if not already initialized)
68
- if (!_secureState.initialized) {
69
- _initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
70
- }
71
-
72
- // Load RuntimeRBAC-specific definitions
73
- IDefinition.RolePermission memory permissions = RuntimeRBACDefinitions.getRolePermissions();
74
- _loadDefinitions(
75
- RuntimeRBACDefinitions.getFunctionSchemas(),
76
- permissions.roleHashes,
77
- permissions.functionPermissions,
78
- true // Allow protected schemas for factory settings
79
- );
80
- }
81
-
82
- // ============ INTERFACE SUPPORT ============
83
-
84
- /**
85
- * @dev See {IERC165-supportsInterface}.
86
- * @notice Adds IRuntimeRBAC interface ID for component detection
87
- */
88
- function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
89
- return interfaceId == type(IRuntimeRBAC).interfaceId || super.supportsInterface(interfaceId);
90
- }
91
-
92
- // ============ ROLE CONFIGURATION BATCH INTERFACE ============
93
-
94
- /**
95
- * @dev Requests and approves a RBAC configuration batch using a meta-transaction
96
- * @param metaTx The meta-transaction
97
- * @return The transaction record
98
- * @notice OWNER signs, BROADCASTER executes according to RuntimeRBACDefinitions
99
- */
100
- function roleConfigBatchRequestAndApprove(
101
- EngineBlox.MetaTransaction memory metaTx
102
- ) public returns (EngineBlox.TxRecord memory) {
103
- _validateBroadcaster(msg.sender);
104
- return _requestAndApproveTransaction(metaTx);
105
- }
106
-
107
- /**
108
- * @dev External function that can only be called by the contract itself to execute a RBAC configuration batch
109
- * @param actions Encoded role configuration actions
110
- */
111
- function executeRoleConfigBatch(RoleConfigAction[] calldata actions) external {
112
- _validateExecuteBySelf();
113
- _executeRoleConfigBatch(actions);
114
- }
115
-
116
- // Essential Query Functions Only
117
-
118
- /**
119
- * @dev Gets function schema information
120
- * @param functionSelector The function selector to get information for
121
- * @return functionSignature The function signature or name
122
- * @return functionSelectorReturn The function selector
123
- * @return operationType The operation type
124
- * @return operationName The operation name
125
- * @return supportedActions The supported actions
126
- * @return isProtected Whether the function schema is protected
127
- */
128
- function getFunctionSchema(bytes4 functionSelector) external view returns (
129
- string memory functionSignature,
130
- bytes4 functionSelectorReturn,
131
- bytes32 operationType,
132
- string memory operationName,
133
- EngineBlox.TxAction[] memory supportedActions,
134
- bool isProtected
135
- ) {
136
- EngineBlox.FunctionSchema storage schema = _getSecureState().functions[functionSelector];
137
- if (schema.functionSelector != functionSelector) {
138
- revert SharedValidation.ResourceNotFound(bytes32(functionSelector));
139
- }
140
-
141
- // Convert bitmap to array
142
- supportedActions = _convertBitmapToActions(schema.supportedActionsBitmap);
143
-
144
- return (
145
- schema.functionSignature,
146
- schema.functionSelector,
147
- schema.operationType,
148
- schema.operationName,
149
- supportedActions,
150
- schema.isProtected
151
- );
152
- }
153
-
154
-
155
- // ============ HELPER FUNCTIONS ============
156
-
157
- /**
158
- * @dev Reverts if the role is protected (prevents editing OWNER, BROADCASTER, RECOVERY via batch).
159
- * @param roleHash The role hash to check
160
- */
161
- function _requireRoleNotProtected(bytes32 roleHash) internal view {
162
- if (_getSecureState().roles[roleHash].isProtected) {
163
- revert SharedValidation.CannotModifyProtected(roleHash);
164
- }
165
- }
166
-
167
- /**
168
- * @dev Internal helper to execute a RBAC configuration batch
169
- * @param actions Encoded role configuration actions
170
- */
171
- function _executeRoleConfigBatch(RoleConfigAction[] calldata actions) internal {
172
- _validateBatchSize(actions.length);
173
-
174
- for (uint256 i = 0; i < actions.length; i++) {
175
- RoleConfigAction calldata action = actions[i];
176
-
177
- if (action.actionType == RoleConfigActionType.CREATE_ROLE) {
178
- // Decode CREATE_ROLE action data
179
- // Format: (string roleName, uint256 maxWallets, FunctionPermission[] functionPermissions)
180
- // FunctionPermission is struct(bytes4 functionSelector, uint16 grantedActionsBitmap, bytes4 handlerForSelector)
181
- // When encoding from JavaScript, it's encoded as tuple(bytes4,uint16,bytes4)[]
182
- // Solidity can decode tuple[] directly into struct[] if the layout matches
183
- (
184
- string memory roleName,
185
- uint256 maxWallets,
186
- EngineBlox.FunctionPermission[] memory functionPermissions
187
- ) = abi.decode(action.data, (string, uint256, EngineBlox.FunctionPermission[]));
188
-
189
- bytes32 roleHash = _createNewRole(roleName, maxWallets, functionPermissions);
190
-
191
- _logComponentEvent(_encodeRoleConfigEvent(RoleConfigActionType.CREATE_ROLE, roleHash, bytes4(0)));
192
- } else if (action.actionType == RoleConfigActionType.REMOVE_ROLE) {
193
- (bytes32 roleHash) = abi.decode(action.data, (bytes32));
194
- _removeRole(roleHash);
195
-
196
- _logComponentEvent(_encodeRoleConfigEvent(RoleConfigActionType.REMOVE_ROLE, roleHash, bytes4(0)));
197
- } else if (action.actionType == RoleConfigActionType.ADD_WALLET) {
198
- (bytes32 roleHash, address wallet) = abi.decode(action.data, (bytes32, address));
199
- _requireRoleNotProtected(roleHash);
200
- _assignWallet(roleHash, wallet);
201
-
202
- _logComponentEvent(_encodeRoleConfigEvent(RoleConfigActionType.ADD_WALLET, roleHash, bytes4(0)));
203
- } else if (action.actionType == RoleConfigActionType.REVOKE_WALLET) {
204
- (bytes32 roleHash, address wallet) = abi.decode(action.data, (bytes32, address));
205
- _requireRoleNotProtected(roleHash);
206
- _revokeWallet(roleHash, wallet);
207
-
208
- _logComponentEvent(_encodeRoleConfigEvent(RoleConfigActionType.REVOKE_WALLET, roleHash, bytes4(0)));
209
- } else if (action.actionType == RoleConfigActionType.ADD_FUNCTION_TO_ROLE) {
210
- (
211
- bytes32 roleHash,
212
- EngineBlox.FunctionPermission memory functionPermission
213
- ) = abi.decode(action.data, (bytes32, EngineBlox.FunctionPermission));
214
-
215
- _addFunctionToRole(roleHash, functionPermission);
216
-
217
- _logComponentEvent(_encodeRoleConfigEvent(RoleConfigActionType.ADD_FUNCTION_TO_ROLE, roleHash, functionPermission.functionSelector));
218
- } else if (action.actionType == RoleConfigActionType.REMOVE_FUNCTION_FROM_ROLE) {
219
- (bytes32 roleHash, bytes4 functionSelector) = abi.decode(action.data, (bytes32, bytes4));
220
- _removeFunctionFromRole(roleHash, functionSelector);
221
-
222
- _logComponentEvent(_encodeRoleConfigEvent(RoleConfigActionType.REMOVE_FUNCTION_FROM_ROLE, roleHash, functionSelector));
223
- } else {
224
- revert SharedValidation.NotSupported();
225
- }
226
- }
227
- }
228
-
229
- /**
230
- * @dev Encodes RBAC config event payload for ComponentEvent. Decode as (RoleConfigActionType, bytes32 roleHash, bytes4 functionSelector).
231
- */
232
- function _encodeRoleConfigEvent(RoleConfigActionType action, bytes32 roleHash, bytes4 selector) internal pure returns (bytes memory) {
233
- return abi.encode(action, roleHash, selector);
234
- }
235
-
236
- // ============ INTERNAL ROLE / FUNCTION HELPERS ============
237
-
238
- function _createNewRole(
239
- string memory roleName,
240
- uint256 maxWallets,
241
- EngineBlox.FunctionPermission[] memory functionPermissions
242
- ) internal returns (bytes32 roleHash) {
243
- SharedValidation.validateRoleNameNotEmpty(roleName);
244
- SharedValidation.validateMaxWalletsGreaterThanZero(maxWallets);
245
-
246
- roleHash = keccak256(bytes(roleName));
247
-
248
- // Create the role in the secure state with isProtected = false
249
- _createRole(roleName, maxWallets, false);
250
-
251
- // Add all function permissions to the role
252
- // NOTE: Function schemas must be registered BEFORE adding permissions to roles
253
- // This is the same pattern used in _loadDefinitions: schemas first, then permissions
254
- // The function selectors in functionPermissions must exist in supportedFunctionsSet
255
- // (they should be registered during initialize() via RuntimeRBACDefinitions)
256
- //
257
- // CRITICAL: The order matters - _loadDefinitions loads schemas FIRST, then permissions
258
- // In _createNewRole, we assume schemas are already registered (from initialize)
259
- // If schemas aren't registered, addFunctionToRole will revert with ResourceNotFound
260
- for (uint256 i = 0; i < functionPermissions.length; i++) {
261
- // Add function permission to role
262
- // addFunctionToRole will check:
263
- // 1. Role exists in supportedRolesSet (✅ just created)
264
- // 2. Function selector exists in supportedFunctionsSet (must be registered during initialize)
265
- // 3. Actions are supported by function schema (via _validateMetaTxPermissions)
266
- _addFunctionToRole(roleHash, functionPermissions[i]);
267
- }
268
- }
269
-
270
- }
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ pragma solidity 0.8.35;
3
+
4
+ // Contract imports
5
+ import "../base/BaseStateMachine.sol";
6
+ import "../lib/EngineBlox.sol";
7
+ import "../lib/utils/SharedValidation.sol";
8
+ import "./lib/definitions/RuntimeRBACDefinitions.sol";
9
+ import "../lib/interfaces/IDefinition.sol";
10
+ import "./interface/IRuntimeRBAC.sol";
11
+
12
+ /**
13
+ * @title RuntimeRBAC
14
+ * @dev Minimal Runtime Role-Based Access Control system based on EngineBlox
15
+ *
16
+ * This contract provides essential runtime RBAC functionality:
17
+ * - Creation of non-protected roles
18
+ * - Basic wallet assignment to roles
19
+ * - Function permission management per role
20
+ * - Integration with EngineBlox for secure operations
21
+ *
22
+ * Key Features:
23
+ * - Only non-protected roles can be created dynamically
24
+ * - Protected roles (OWNER, BROADCASTER, RECOVERY) are managed by SecureOwnable
25
+ * - Minimal interface for core RBAC operations
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.
40
+ */
41
+ abstract contract RuntimeRBAC is BaseStateMachine, IRuntimeRBAC {
42
+ using EngineBlox for EngineBlox.SecureOperationState;
43
+ using SharedValidation for *;
44
+
45
+ /**
46
+ * @notice Initializer to initialize RuntimeRBAC
47
+ * @param initialOwner The initial owner address
48
+ * @param broadcaster The broadcaster address
49
+ * @param recovery The recovery address
50
+ * @param timeLockPeriodSec The timelock period in seconds
51
+ * @param eventForwarder The event forwarder address
52
+ */
53
+ function initialize(
54
+ address initialOwner,
55
+ address broadcaster,
56
+ address recovery,
57
+ uint256 timeLockPeriodSec,
58
+ address eventForwarder
59
+ ) public virtual onlyInitializing {
60
+ _initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
61
+
62
+ // Load RuntimeRBAC-specific definitions
63
+ IDefinition.RolePermission memory permissions = RuntimeRBACDefinitions.getRolePermissions();
64
+ _loadDefinitions(
65
+ RuntimeRBACDefinitions.getFunctionSchemas(),
66
+ permissions.roleHashes,
67
+ permissions.functionPermissions,
68
+ true // Enforce all function schemas are protected
69
+ );
70
+ }
71
+
72
+ // ============ INTERFACE SUPPORT ============
73
+
74
+ /**
75
+ * @dev See {IERC165-supportsInterface}.
76
+ * @notice Adds IRuntimeRBAC interface ID for component detection
77
+ */
78
+ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
79
+ return interfaceId == type(IRuntimeRBAC).interfaceId || super.supportsInterface(interfaceId);
80
+ }
81
+
82
+ // ============ ROLE CONFIGURATION BATCH INTERFACE ============
83
+
84
+ /**
85
+ * @dev Requests and approves a RBAC configuration batch using a meta-transaction
86
+ * @param metaTx The meta-transaction
87
+ * @return The transaction ID of the applied batch
88
+ * @notice OWNER signs, BROADCASTER executes according to RuntimeRBACDefinitions
89
+ */
90
+ function roleConfigBatchRequestAndApprove(
91
+ EngineBlox.MetaTransaction memory metaTx
92
+ ) public returns (uint256) {
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
+ );
100
+ EngineBlox.TxRecord memory txRecord = _requestAndApproveTransaction(metaTx);
101
+ return txRecord.txId;
102
+ }
103
+
104
+ /**
105
+ * @dev External function that can only be called by the contract itself to execute a RBAC configuration batch
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.
114
+ */
115
+ function executeRoleConfigBatch(IRuntimeRBAC.RoleConfigAction[] calldata actions) external {
116
+ _validateExecuteBySelf();
117
+ _executeRoleConfigBatch(actions);
118
+ }
119
+
120
+ // ============ HELPER FUNCTIONS ============
121
+
122
+ /**
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.
127
+ * @param roleHash The role hash to check
128
+ */
129
+ function _requireRoleNotProtected(bytes32 roleHash) internal view {
130
+ if (_getSecureState().roles[roleHash].isProtected) {
131
+ revert SharedValidation.CannotModifyProtected(roleHash);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * @dev Internal helper to execute a RBAC configuration batch
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.
142
+ */
143
+ function _executeRoleConfigBatch(IRuntimeRBAC.RoleConfigAction[] calldata actions) internal {
144
+ _validateBatchSize(actions.length);
145
+
146
+ for (uint256 i = 0; i < actions.length; ++i) {
147
+ IRuntimeRBAC.RoleConfigAction calldata action = actions[i];
148
+
149
+ if (action.actionType == IRuntimeRBAC.RoleConfigActionType.CREATE_ROLE) {
150
+ _executeCreateRole(action.data);
151
+ } else if (action.actionType == IRuntimeRBAC.RoleConfigActionType.REMOVE_ROLE) {
152
+ _executeRemoveRole(action.data);
153
+ } else if (action.actionType == IRuntimeRBAC.RoleConfigActionType.ADD_WALLET) {
154
+ _executeAddWallet(action.data);
155
+ } else if (action.actionType == IRuntimeRBAC.RoleConfigActionType.REVOKE_WALLET) {
156
+ _executeRevokeWallet(action.data);
157
+ } else if (action.actionType == IRuntimeRBAC.RoleConfigActionType.ADD_FUNCTION_TO_ROLE) {
158
+ _executeAddFunctionToRole(action.data);
159
+ } else if (action.actionType == IRuntimeRBAC.RoleConfigActionType.REMOVE_FUNCTION_FROM_ROLE) {
160
+ _executeRemoveFunctionFromRole(action.data);
161
+ } else {
162
+ revert SharedValidation.NotSupported();
163
+ }
164
+ }
165
+ }
166
+
167
+ /**
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)
249
+ */
250
+ function _logRoleConfigEvent(IRuntimeRBAC.RoleConfigActionType action, bytes32 roleHash, bytes4 selector, address wallet) internal {
251
+ _logComponentEvent(abi.encode(action, roleHash, selector, wallet));
252
+ }
253
+ }