@bloxchain/contracts 1.0.0-alpha.19 → 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.
@@ -1,948 +1,967 @@
1
- // SPDX-License-Identifier: MPL-2.0
2
- pragma solidity 0.8.34;
3
-
4
- // OpenZeppelin imports
5
- import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
6
- import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
7
- import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
8
-
9
- // Contracts imports
10
- import "../lib/EngineBlox.sol";
11
- import "../lib/utils/SharedValidation.sol";
12
- import "./interface/IBaseStateMachine.sol";
13
-
14
- /**
15
- * @title BaseStateMachine
16
- * @dev Core state machine functionality for secure multi-phase operations
17
- *
18
- * This contract provides the foundational state machine capabilities that can be extended
19
- * by security-specific contracts. It handles:
20
- * - State initialization and management
21
- * - Meta-transaction utilities and parameter creation
22
- * - State queries and transaction history
23
- * - Role-based access control queries
24
- * - System state information
25
- *
26
- * The contract is designed to be inherited by security-specific contracts that implement
27
- * their own operation types and business logic while leveraging the core state machine.
28
- * All access to EngineBlox library functions is centralized through BaseStateMachine
29
- * wrapper functions to ensure consistency and maintainability.
30
- *
31
- * Key Features:
32
- * - State initialization with role and permission setup
33
- * - Meta-transaction parameter creation and generation
34
- * - Comprehensive state queries and transaction history
35
- * - Role and permission validation utilities
36
- * - System configuration queries
37
- * - Event forwarding for external monitoring
38
- */
39
- abstract contract BaseStateMachine is Initializable, ERC165Upgradeable, ReentrancyGuardUpgradeable {
40
- using EngineBlox for EngineBlox.SecureOperationState;
41
- using SharedValidation for *;
42
-
43
- EngineBlox.SecureOperationState internal _secureState;
44
-
45
- /**
46
- * @dev Unified component event for SecureOwnable, GuardController, RuntimeRBAC.
47
- * Indexers filter by functionSelector (msg.sig at emit site) and decode data (abi-encoded payload).
48
- * @param functionSelector The function selector (msg.sig) at the emit site; used by indexers to filter
49
- * @param data ABI-encoded payload associated with the event
50
- */
51
- event ComponentEvent(bytes4 indexed functionSelector, bytes data);
52
-
53
- /**
54
- * @notice Initializes the base state machine core
55
- * @param initialOwner The initial owner address
56
- * @param broadcaster The broadcaster address
57
- * @param recovery The recovery address
58
- * @param timeLockPeriodSec The timelock period in seconds
59
- * @param eventForwarder The event forwarder address
60
- */
61
- function _initializeBaseStateMachine(
62
- address initialOwner,
63
- address broadcaster,
64
- address recovery,
65
- uint256 timeLockPeriodSec,
66
- address eventForwarder
67
- ) internal onlyInitializing {
68
- // Skip if already initialized (e.g. when multiple components call from Account.initialize)
69
- if (!_secureState.initialized) {
70
- __ERC165_init();
71
- __ReentrancyGuard_init();
72
-
73
- _secureState.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec);
74
-
75
- _secureState.setEventForwarder(eventForwarder);
76
- }
77
- }
78
-
79
- // ============ SYSTEM ROLE QUERY FUNCTIONS ============
80
-
81
- /**
82
- * @dev Returns the owner of the contract
83
- * @return The owner of the contract
84
- */
85
- function owner() public view returns (address) {
86
- return _getAuthorizedWalletAt(EngineBlox.OWNER_ROLE, 0);
87
- }
88
-
89
- /**
90
- * @dev Returns all broadcaster addresses for the BROADCASTER_ROLE
91
- * @return Array of broadcaster addresses
92
- */
93
- function getBroadcasters() public view returns (address[] memory) {
94
- return _getAuthorizedWallets(EngineBlox.BROADCASTER_ROLE);
95
- }
96
-
97
- /**
98
- * @dev Returns the recovery address
99
- * @return The recovery address
100
- */
101
- function getRecovery() public view returns (address) {
102
- return _getAuthorizedWalletAt(EngineBlox.RECOVERY_ROLE, 0);
103
- }
104
-
105
- // ============ INTERFACE SUPPORT ============
106
-
107
- /**
108
- * @dev See {IERC165-supportsInterface}.
109
- * @notice Base implementation for ERC165 interface detection
110
- * @notice Registers IBaseStateMachine interface ID for proper interface detection
111
- * @notice Component contracts (SecureOwnable, RuntimeRBAC, GuardController) should override
112
- * to add their respective interface IDs for component detection
113
- */
114
- function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
115
- return interfaceId == type(IBaseStateMachine).interfaceId || super.supportsInterface(interfaceId);
116
- }
117
-
118
- // ============ TRANSACTION MANAGEMENT ============
119
-
120
- /**
121
- * @dev Centralized function to request a transaction with common validation
122
- * @param requester The address requesting the transaction
123
- * @param target The target contract address
124
- * @param value The ETH value to send (0 for standard function calls)
125
- * @param gasLimit The gas limit for execution
126
- * @param operationType The type of operation
127
- * @param functionSelector The function selector for execution (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
128
- * @param params The encoded parameters for the function (empty for simple native token transfers)
129
- * @return The created transaction record
130
- * @notice Validates permissions for the calling function (request function), not the execution selector
131
- * @notice Execution functions are internal-only and don't need permission definitions
132
- * @notice This function is virtual to allow extensions to add hook functionality
133
- * @notice For standard function calls: value=0, functionSelector=non-zero, params=encoded data
134
- * @notice For simple native token transfers: value>0, functionSelector=NATIVE_TRANSFER_SELECTOR, params=""
135
- */
136
- function _requestTransaction(
137
- address requester,
138
- address target,
139
- uint256 value,
140
- uint256 gasLimit,
141
- bytes32 operationType,
142
- bytes4 functionSelector,
143
- bytes memory params
144
- ) internal virtual returns (EngineBlox.TxRecord memory) {
145
- EngineBlox.TxRecord memory txRecord = EngineBlox.txRequest(
146
- _getSecureState(),
147
- requester,
148
- target,
149
- value,
150
- gasLimit,
151
- operationType,
152
- bytes4(msg.sig),
153
- functionSelector,
154
- params
155
- );
156
- _postActionHook(txRecord);
157
- return txRecord;
158
- }
159
-
160
- /**
161
- * @dev Centralized function to request a transaction with payment details attached from the start
162
- * @param requester The address requesting the transaction
163
- * @param target The target contract address
164
- * @param value The ETH value to send (0 for standard function calls)
165
- * @param gasLimit The gas limit for execution
166
- * @param operationType The type of operation
167
- * @param functionSelector The function selector for execution (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
168
- * @param params The encoded parameters for the function (empty for simple native token transfers)
169
- * @param paymentDetails The payment details to attach to the transaction
170
- * @return The created transaction record with payment set
171
- * @notice Validates request permissions (same as request without payment)
172
- * @notice This function is virtual to allow extensions to add hook functionality
173
- */
174
- function _requestTransactionWithPayment(
175
- address requester,
176
- address target,
177
- uint256 value,
178
- uint256 gasLimit,
179
- bytes32 operationType,
180
- bytes4 functionSelector,
181
- bytes memory params,
182
- EngineBlox.PaymentDetails memory paymentDetails
183
- ) internal virtual returns (EngineBlox.TxRecord memory) {
184
- EngineBlox.TxRecord memory txRecord = EngineBlox.txRequestWithPayment(
185
- _getSecureState(),
186
- requester,
187
- target,
188
- value,
189
- gasLimit,
190
- operationType,
191
- bytes4(msg.sig),
192
- functionSelector,
193
- params,
194
- paymentDetails
195
- );
196
- _postActionHook(txRecord);
197
- return txRecord;
198
- }
199
-
200
- /**
201
- * @dev Centralized function to approve a pending transaction after release time
202
- * @param txId The transaction ID
203
- * @return The updated transaction record
204
- * @notice Validates permissions for the calling function (approval function selector), not the execution selector
205
- * @notice Execution functions are internal-only and don't need permission definitions
206
- * @notice This function is virtual to allow extensions to add hook functionality
207
- * @notice Protected by ReentrancyGuard to prevent reentrancy attacks
208
- */
209
- function _approveTransaction(
210
- uint256 txId
211
- ) internal virtual nonReentrant returns (EngineBlox.TxRecord memory) {
212
- EngineBlox.TxRecord memory txRecord = EngineBlox.txDelayedApproval(_getSecureState(), txId, bytes4(msg.sig));
213
- _postActionHook(txRecord);
214
- return txRecord;
215
- }
216
-
217
- /**
218
- * @dev Centralized function to approve a transaction using meta-transaction
219
- * @param metaTx The meta-transaction
220
- * @return The updated transaction record
221
- * @notice Validates permissions for the calling function (msg.sig) and handler selector from metaTx
222
- * @notice Uses EXECUTE_META_APPROVE action for permission checking
223
- * @notice This function is virtual to allow extensions to add hook functionality
224
- * @notice Protected by ReentrancyGuard to prevent reentrancy attacks
225
- */
226
- function _approveTransactionWithMetaTx(
227
- EngineBlox.MetaTransaction memory metaTx
228
- ) internal virtual nonReentrant returns (EngineBlox.TxRecord memory) {
229
- EngineBlox.TxRecord memory txRecord = EngineBlox.txApprovalWithMetaTx(_getSecureState(), metaTx);
230
- _postActionHook(txRecord);
231
- return txRecord;
232
- }
233
-
234
- /**
235
- * @dev Centralized function to cancel a pending transaction
236
- * @param txId The transaction ID
237
- * @return The updated transaction record
238
- * @notice Validates permissions for the calling function (cancellation function selector), not the execution selector
239
- * @notice Execution functions are internal-only and don't need permission definitions
240
- * @notice This function is virtual to allow extensions to add hook functionality
241
- */
242
- function _cancelTransaction(
243
- uint256 txId
244
- ) internal virtual returns (EngineBlox.TxRecord memory) {
245
- EngineBlox.TxRecord memory txRecord = EngineBlox.txCancellation(_getSecureState(), txId, bytes4(msg.sig));
246
- _postActionHook(txRecord);
247
- return txRecord;
248
- }
249
-
250
- /**
251
- * @dev Centralized function to cancel a transaction using meta-transaction
252
- * @param metaTx The meta-transaction
253
- * @return The updated transaction record
254
- * @notice Validates permissions for the calling function (msg.sig) and handler selector from metaTx
255
- * @notice Uses EXECUTE_META_CANCEL action for permission checking
256
- * @notice This function is virtual to allow extensions to add hook functionality
257
- */
258
- function _cancelTransactionWithMetaTx(
259
- EngineBlox.MetaTransaction memory metaTx
260
- ) internal virtual returns (EngineBlox.TxRecord memory) {
261
- EngineBlox.TxRecord memory txRecord = EngineBlox.txCancellationWithMetaTx(_getSecureState(), metaTx);
262
- _postActionHook(txRecord);
263
- return txRecord;
264
- }
265
-
266
- /**
267
- * @dev Centralized function to request and approve a transaction using meta-transaction
268
- * @param metaTx The meta-transaction
269
- * @return The transaction record
270
- * @notice Validates permissions for the calling function (msg.sig) and handler selector from metaTx
271
- * @notice Uses EXECUTE_META_REQUEST_AND_APPROVE action for permission checking
272
- * @notice This function is virtual to allow extensions to add hook functionality
273
- * @notice Protected by ReentrancyGuard to prevent reentrancy attacks
274
- */
275
- function _requestAndApproveTransaction(
276
- EngineBlox.MetaTransaction memory metaTx
277
- ) internal virtual nonReentrant returns (EngineBlox.TxRecord memory) {
278
- EngineBlox.TxRecord memory txRecord = EngineBlox.requestAndApprove(_getSecureState(), metaTx);
279
- _postActionHook(txRecord);
280
- return txRecord;
281
- }
282
-
283
- /**
284
- * @dev Post-action hook invoked after any transaction operation that produces a TxRecord.
285
- * Override in derived contracts to add centralized post-tx logic (e.g. notifications, side effects).
286
- * @param txRecord The transaction record produced by the operation
287
- */
288
- function _postActionHook(EngineBlox.TxRecord memory txRecord) internal virtual {}
289
-
290
- // ============ HOOK MANAGEMENT ============
291
-
292
- /**
293
- * @dev Sets the hook contract for a function selector (internal; no access control).
294
- * Extensions (e.g. HookManager) may expose an external setHook with owner check.
295
- * @param functionSelector The function selector
296
- * @param hook The hook contract address (must not be zero)
297
- */
298
- function _setHook(bytes4 functionSelector, address hook) internal {
299
- EngineBlox.setHook(_getSecureState(), functionSelector, hook);
300
- }
301
-
302
- /**
303
- * @dev Clears the hook contract for a function selector (internal; no access control).
304
- * Extensions may expose an external clearHook with owner check.
305
- * @param functionSelector The function selector
306
- * @param hook The hook contract address to remove (must not be zero)
307
- */
308
- function _clearHook(bytes4 functionSelector, address hook) internal {
309
- EngineBlox.clearHook(_getSecureState(), functionSelector, hook);
310
- }
311
-
312
- /**
313
- * @dev Returns all configured hooks for a function selector
314
- * @param functionSelector The function selector
315
- * @return hooks Array of hook contract addresses
316
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
317
- */
318
- function getHooks(bytes4 functionSelector) public view returns (address[] memory hooks) {
319
- _validateAnyRole();
320
- return EngineBlox.getHooks(_getSecureState(), functionSelector);
321
- }
322
-
323
- // ============ META-TRANSACTION UTILITIES ============
324
-
325
- /**
326
- * @dev Creates meta-transaction parameters with specified values
327
- * @param handlerContract The contract that will handle the meta-transaction
328
- * @param handlerSelector The function selector for the handler
329
- * @param action The transaction action type
330
- * @param deadline The timestamp after which the meta-transaction expires
331
- * @param maxGasPrice The maximum gas price allowed for execution
332
- * @param signer The address that will sign the meta-transaction
333
- * @return The formatted meta-transaction parameters
334
- */
335
- function createMetaTxParams(
336
- address handlerContract,
337
- bytes4 handlerSelector,
338
- EngineBlox.TxAction action,
339
- uint256 deadline,
340
- uint256 maxGasPrice,
341
- address signer
342
- ) public view returns (EngineBlox.MetaTxParams memory) {
343
- return EngineBlox.createMetaTxParams(
344
- handlerContract,
345
- handlerSelector,
346
- action,
347
- deadline,
348
- maxGasPrice,
349
- signer
350
- );
351
- }
352
-
353
- /**
354
- * @dev Generates an unsigned meta-transaction for a new operation
355
- * @param requester The address requesting the operation
356
- * @param target The target contract address
357
- * @param value The ETH value to send
358
- * @param gasLimit The gas limit for execution
359
- * @param operationType The type of operation
360
- * @param executionSelector The function selector to execute (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
361
- * @param executionParams The encoded parameters for the function (empty for simple native token transfers)
362
- * @param metaTxParams The meta-transaction parameters
363
- * @return The unsigned meta-transaction
364
- */
365
- function generateUnsignedMetaTransactionForNew(
366
- address requester,
367
- address target,
368
- uint256 value,
369
- uint256 gasLimit,
370
- bytes32 operationType,
371
- bytes4 executionSelector,
372
- bytes memory executionParams,
373
- EngineBlox.MetaTxParams memory metaTxParams
374
- ) public view returns (EngineBlox.MetaTransaction memory) {
375
- EngineBlox.TxParams memory txParams = EngineBlox.TxParams({
376
- requester: requester,
377
- target: target,
378
- value: value,
379
- gasLimit: gasLimit,
380
- operationType: operationType,
381
- executionSelector: executionSelector,
382
- executionParams: executionParams
383
- });
384
-
385
- return _secureState.generateUnsignedForNewMetaTx(txParams, metaTxParams);
386
- }
387
-
388
- /**
389
- * @dev Generates an unsigned meta-transaction for an existing transaction
390
- * @param txId The ID of the existing transaction
391
- * @param metaTxParams The meta-transaction parameters
392
- * @return The unsigned meta-transaction
393
- */
394
- function generateUnsignedMetaTransactionForExisting(
395
- uint256 txId,
396
- EngineBlox.MetaTxParams memory metaTxParams
397
- ) public view returns (EngineBlox.MetaTransaction memory) {
398
- return _secureState.generateUnsignedForExistingMetaTx(txId, metaTxParams);
399
- }
400
-
401
- // ============ STATE QUERIES ============
402
-
403
- /**
404
- * @dev Gets transaction history within a specified range
405
- * @param fromTxId The starting transaction ID (inclusive)
406
- * @param toTxId The ending transaction ID (inclusive)
407
- * @return The transaction history within the specified range
408
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
409
- */
410
- function getTransactionHistory(uint256 fromTxId, uint256 toTxId) public view returns (EngineBlox.TxRecord[] memory) {
411
- _validateAnyRole();
412
-
413
- // Validate the range
414
- fromTxId = fromTxId > 0 ? fromTxId : 1;
415
- toTxId = toTxId > _secureState.txCounter ? _secureState.txCounter : toTxId;
416
-
417
- // Validate that fromTxId is less than toTxId
418
- SharedValidation.validateLessThan(fromTxId, toTxId);
419
-
420
- uint256 rangeSize = toTxId - fromTxId + 1;
421
-
422
- // For larger ranges, use paginated version
423
- SharedValidation.validateRangeSize(rangeSize, 1000);
424
-
425
- EngineBlox.TxRecord[] memory history = new EngineBlox.TxRecord[](rangeSize);
426
-
427
- for (uint256 i = 0; i < rangeSize; i++) {
428
- history[i] = _secureState.getTxRecord(fromTxId + i);
429
- }
430
-
431
- return history;
432
- }
433
-
434
- /**
435
- * @dev Gets a transaction by ID
436
- * @param txId The transaction ID
437
- * @return The transaction record
438
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
439
- */
440
- function getTransaction(uint256 txId) public view returns (EngineBlox.TxRecord memory) {
441
- _validateAnyRole();
442
- return _secureState.getTxRecord(txId);
443
- }
444
-
445
- /**
446
- * @dev Gets all pending transaction IDs
447
- * @return Array of pending transaction IDs
448
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
449
- */
450
- function getPendingTransactions() public view returns (uint256[] memory) {
451
- _validateAnyRole();
452
- return _secureState.getPendingTransactions();
453
- }
454
-
455
- // ============ ROLE AND PERMISSION QUERIES ============
456
-
457
- /**
458
- * @dev Gets the basic role information by its hash
459
- * @param roleHash The hash of the role to get
460
- * @return roleName The name of the role
461
- * @return hash The hash of the role
462
- * @return maxWallets The maximum number of wallets allowed for this role
463
- * @return walletCount The current number of wallets assigned to this role
464
- * @return isProtected Whether the role is protected from removal
465
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
466
- */
467
- function getRole(bytes32 roleHash) public view returns (
468
- string memory roleName,
469
- bytes32 hash,
470
- uint256 maxWallets,
471
- uint256 walletCount,
472
- bool isProtected
473
- ) {
474
- _validateAnyRole();
475
- EngineBlox.Role storage role = _secureState.getRole(roleHash);
476
- return (
477
- role.roleName,
478
- role.roleHash,
479
- role.maxWallets,
480
- role.walletCount,
481
- role.isProtected
482
- );
483
- }
484
-
485
- /**
486
- * @dev Returns if a wallet is authorized for a role
487
- * @param roleHash The hash of the role to check
488
- * @param wallet The wallet address to check
489
- * @return True if the wallet is authorized for the role, false otherwise
490
- */
491
- function hasRole(bytes32 roleHash, address wallet) public view returns (bool) {
492
- return _secureState.hasRole(roleHash, wallet);
493
- }
494
-
495
- /**
496
- * @dev Gets all roles assigned to a wallet
497
- * @param wallet The wallet address to get roles for
498
- * @return Array of role hashes assigned to the wallet
499
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
500
- * @notice This function uses the reverse index for efficient lookup
501
- */
502
- function getWalletRoles(address wallet) public view returns (bytes32[] memory) {
503
- _validateAnyRole();
504
- return EngineBlox.getWalletRoles(_getSecureState(), wallet);
505
- }
506
-
507
- /**
508
- * @dev Gets all authorized wallets for a role
509
- * @param roleHash The role hash to get wallets for
510
- * @return Array of authorized wallet addresses
511
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
512
- */
513
- function getAuthorizedWallets(bytes32 roleHash) public view returns (address[] memory) {
514
- _validateAnyRole();
515
- _validateRoleExists(roleHash);
516
- return _getAuthorizedWallets(roleHash);
517
- }
518
-
519
- /**
520
- * @dev Gets function schema information
521
- * @param functionSelector The function selector to get information for
522
- * @return The full FunctionSchema struct (functionSignature, functionSelector, operationType, operationName, supportedActionsBitmap, enforceHandlerRelations, isProtected, handlerForSelectors)
523
- * @notice Reverts with ResourceNotFound if the schema does not exist
524
- */
525
- function getFunctionSchema(bytes4 functionSelector) external view returns (EngineBlox.FunctionSchema memory) {
526
- _validateAnyRole();
527
- return _secureState.getFunctionSchema(functionSelector);
528
- }
529
-
530
- /**
531
- * @dev Gets the function permissions for a specific role
532
- * @param roleHash The hash of the role to get permissions for
533
- * @return The function permissions array for the role
534
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
535
- */
536
- function getActiveRolePermissions(bytes32 roleHash) public view returns (EngineBlox.FunctionPermission[] memory) {
537
- _validateAnyRole();
538
- return _secureState.getRoleFunctionPermissions(roleHash);
539
- }
540
-
541
- /**
542
- * @dev Gets the current nonce for a specific signer
543
- * @param signer The address of the signer
544
- * @return The current nonce for the signer
545
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
546
- */
547
- function getSignerNonce(address signer) public view returns (uint256) {
548
- _validateAnyRole();
549
- return _secureState.getSignerNonce(signer);
550
- }
551
-
552
- // ============ SYSTEM STATE QUERIES ============
553
-
554
- /**
555
- * @dev Returns the supported operation types
556
- * @return The supported operation types
557
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
558
- */
559
- function getSupportedOperationTypes() public view returns (bytes32[] memory) {
560
- _validateAnyRole();
561
- return _secureState.getSupportedOperationTypes();
562
- }
563
-
564
- /**
565
- * @dev Returns the supported roles list
566
- * @return The supported roles list
567
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
568
- */
569
- function getSupportedRoles() public view returns (bytes32[] memory) {
570
- _validateAnyRole();
571
- return _secureState.getSupportedRoles();
572
- }
573
-
574
- /**
575
- * @dev Returns the supported functions list
576
- * @return The supported functions list
577
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
578
- */
579
- function getSupportedFunctions() public view returns (bytes4[] memory) {
580
- _validateAnyRole();
581
- return _secureState.getSupportedFunctions();
582
- }
583
-
584
- /**
585
- * @dev Returns the time lock period
586
- * @return The time lock period in seconds
587
- */
588
- function getTimeLockPeriodSec() public view returns (uint256) {
589
- return _secureState.timeLockPeriodSec;
590
- }
591
-
592
- /**
593
- * @dev Returns whether the contract is initialized
594
- * @return bool True if the contract is initialized, false otherwise
595
- */
596
- function initialized() public view returns (bool) {
597
- return _getInitializedVersion() != type(uint8).max && _secureState.initialized;
598
- }
599
-
600
- // ============ ROLE MANAGEMENT ============
601
-
602
- /**
603
- * @dev Centralized function to get authorized wallet at specific index
604
- * @param roleHash The role hash
605
- * @param index The wallet index
606
- * @return The authorized wallet address
607
- */
608
- function _getAuthorizedWalletAt(bytes32 roleHash, uint256 index) internal view returns (address) {
609
- return EngineBlox.getAuthorizedWalletAt(_getSecureState(), roleHash, index);
610
- }
611
-
612
- /**
613
- * @dev Centralized function to get all authorized wallets for a role
614
- * @param roleHash The role hash
615
- * @return Array of authorized wallet addresses
616
- */
617
- function _getAuthorizedWallets(bytes32 roleHash) internal view returns (address[] memory) {
618
- return EngineBlox.getAuthorizedWallets(_getSecureState(), roleHash);
619
- }
620
-
621
- /**
622
- * @dev Centralized function to create a new role
623
- * @param roleName The name of the role
624
- * @param maxWallets The maximum number of wallets allowed for this role
625
- * @param isProtected Whether the role is protected from removal
626
- * @return roleHash The hash of the created role
627
- * @notice This function is virtual to allow extensions to add hook functionality
628
- */
629
- function _createRole(
630
- string memory roleName,
631
- uint256 maxWallets,
632
- bool isProtected
633
- ) internal virtual returns (bytes32) {
634
- bytes32 roleHash = keccak256(bytes(roleName));
635
- EngineBlox.createRole(_getSecureState(), roleName, maxWallets, isProtected);
636
- return roleHash;
637
- }
638
-
639
- /**
640
- * @dev Centralized function to remove a role
641
- * @param roleHash The hash of the role to remove
642
- * @notice This function is virtual to allow extensions to add hook functionality
643
- */
644
- function _removeRole(bytes32 roleHash) internal virtual {
645
- EngineBlox.removeRole(_getSecureState(), roleHash);
646
- }
647
-
648
- /**
649
- * @dev Centralized function to assign a wallet to a role
650
- * @param roleHash The role hash
651
- * @param wallet The wallet address to assign
652
- * @notice This function is virtual to allow extensions to add hook functionality
653
- */
654
- function _assignWallet(bytes32 roleHash, address wallet) internal virtual {
655
- EngineBlox.assignWallet(_getSecureState(), roleHash, wallet);
656
- }
657
-
658
- /**
659
- * @dev Centralized function to revoke a wallet from a role
660
- * @param roleHash The role hash
661
- * @param wallet The wallet address to revoke
662
- * @notice This function is virtual to allow extensions to add hook functionality
663
- */
664
- function _revokeWallet(bytes32 roleHash, address wallet) internal virtual {
665
- EngineBlox.revokeWallet(_getSecureState(), roleHash, wallet);
666
- }
667
-
668
- /**
669
- * @dev Centralized function to update wallet for a role (replaces oldWallet with newWallet).
670
- * @param roleHash The role hash
671
- * @param newWallet The new wallet address
672
- * @param oldWallet The old wallet address
673
- * @notice This function is virtual to allow extensions to add hook functionality or additional validation
674
- */
675
- function _updateWallet(bytes32 roleHash, address newWallet, address oldWallet) internal virtual {
676
- EngineBlox.updateWallet(_getSecureState(), roleHash, newWallet, oldWallet);
677
- }
678
-
679
- /**
680
- * @dev Centralized function to update the time lock period
681
- * @param newTimeLockPeriodSec The new time lock period in seconds
682
- * @notice This function is virtual to allow extensions to add hook functionality
683
- */
684
- function _updateTimeLockPeriod(uint256 newTimeLockPeriodSec) internal virtual {
685
- uint256 oldPeriod = getTimeLockPeriodSec();
686
- EngineBlox.updateTimeLockPeriod(_getSecureState(), newTimeLockPeriodSec);
687
- _logComponentEvent(abi.encode(oldPeriod, newTimeLockPeriodSec));
688
- }
689
-
690
- // ============ FUNCTION SCHEMA MANAGEMENT ============
691
-
692
- /**
693
- * @dev Centralized function to register a function schema
694
- * @param functionSignature The function signature
695
- * @param functionSelector The function selector
696
- * @param operationName The operation name
697
- * @param supportedActionsBitmap The bitmap of supported actions
698
- * @param enforceHandlerRelations Whether to enforce strict handler/schema alignment
699
- * @param isProtected Whether the function schema is protected
700
- * @param handlerForSelectors Array of handler selectors
701
- * @notice This function is virtual to allow extensions to add hook functionality
702
- */
703
- function _registerFunction(
704
- string memory functionSignature,
705
- bytes4 functionSelector,
706
- string memory operationName,
707
- uint16 supportedActionsBitmap,
708
- bool enforceHandlerRelations,
709
- bool isProtected,
710
- bytes4[] memory handlerForSelectors
711
- ) internal virtual {
712
- EngineBlox.registerFunction(
713
- _getSecureState(),
714
- functionSignature,
715
- functionSelector,
716
- operationName,
717
- supportedActionsBitmap,
718
- enforceHandlerRelations,
719
- isProtected,
720
- handlerForSelectors
721
- );
722
- }
723
-
724
- /**
725
- * @dev Centralized function to unregister a function schema
726
- * @param functionSelector The function selector to unregister
727
- * @param safeRemoval Whether to perform safe removal (check for role references)
728
- * @notice This function is virtual to allow extensions to add hook functionality
729
- */
730
- function _unregisterFunction(bytes4 functionSelector, bool safeRemoval) internal virtual {
731
- EngineBlox.unregisterFunction(_getSecureState(), functionSelector, safeRemoval);
732
- }
733
-
734
- /**
735
- * @dev Centralized function to add a function permission to a role
736
- * @param roleHash The role hash
737
- * @param functionPermission The function permission to add
738
- * @notice This function is virtual to allow extensions to add hook functionality
739
- */
740
- function _addFunctionToRole(
741
- bytes32 roleHash,
742
- EngineBlox.FunctionPermission memory functionPermission
743
- ) internal virtual {
744
- EngineBlox.addFunctionToRole(_getSecureState(), roleHash, functionPermission);
745
- }
746
-
747
- /**
748
- * @dev Centralized function to remove a function permission from a role
749
- * @param roleHash The role hash
750
- * @param functionSelector The function selector to remove
751
- * @notice This function is virtual to allow extensions to add hook functionality
752
- */
753
- function _removeFunctionFromRole(bytes32 roleHash, bytes4 functionSelector) internal virtual {
754
- EngineBlox.removeFunctionFromRole(_getSecureState(), roleHash, functionSelector);
755
- }
756
-
757
- // ============ PERMISSION VALIDATION ============
758
-
759
- /**
760
- * @dev Centralized function to validate that the caller has any role
761
- */
762
- function _validateAnyRole() internal view {
763
- EngineBlox._validateAnyRole(_getSecureState());
764
- }
765
-
766
- /**
767
- * @dev Centralized function to validate that a role exists
768
- * @param roleHash The role hash to validate
769
- */
770
- function _validateRoleExists(bytes32 roleHash) internal view {
771
- EngineBlox._validateRoleExists(_getSecureState(), roleHash);
772
- }
773
-
774
- /**
775
- * @dev Centralized function to validate that the caller is the contract itself (for execution-only entry points).
776
- */
777
- function _validateExecuteBySelf() internal view {
778
- SharedValidation.validateInternalCall(address(this));
779
- }
780
-
781
- /**
782
- * @dev Centralized function to validate batch size against EngineBlox.MAX_BATCH_SIZE.
783
- * @param length The batch length to validate
784
- */
785
- function _validateBatchSize(uint256 length) internal pure {
786
- SharedValidation.validateBatchSize(length, EngineBlox.MAX_BATCH_SIZE);
787
- }
788
-
789
- // ============ MACRO SELECTORS ============
790
-
791
- /**
792
- * @dev Adds a function selector to the system macro selectors set.
793
- * Macro selectors are allowed to target address(this) for system-level operations.
794
- * @param functionSelector The function selector to add (e.g. NATIVE_TRANSFER_SELECTOR).
795
- */
796
- function _addMacroSelector(bytes4 functionSelector) internal {
797
- EngineBlox.addMacroSelector(_getSecureState(), functionSelector);
798
- }
799
-
800
- /**
801
- * @dev Returns true if the given function selector is in the system macro selectors set.
802
- * @param functionSelector The function selector to check.
803
- */
804
- function _isMacroSelector(bytes4 functionSelector) internal view returns (bool) {
805
- return EngineBlox.isMacroSelector(_getSecureState(), functionSelector);
806
- }
807
-
808
- // ============ UTILITY FUNCTIONS ============
809
-
810
- /**
811
- * @dev Centralized function to convert a bitmap to an array of actions
812
- * @param bitmap The bitmap to convert
813
- * @return Array of TxAction values
814
- */
815
- function _convertBitmapToActions(uint16 bitmap) internal pure returns (EngineBlox.TxAction[] memory) {
816
- return EngineBlox.convertBitmapToActions(bitmap);
817
- }
818
-
819
- /**
820
- * @dev Centralized function to create a bitmap from an array of actions
821
- * @param actions Array of TxAction values
822
- * @return The bitmap representation
823
- */
824
- function _createBitmapFromActions(EngineBlox.TxAction[] memory actions) internal pure returns (uint16) {
825
- return EngineBlox.createBitmapFromActions(actions);
826
- }
827
-
828
- // ============ TARGET WHITELIST MANAGEMENT ============
829
-
830
- /**
831
- * @dev Centralized function to add a target address to the whitelist for a function selector
832
- * @param functionSelector The function selector
833
- * @param target The target address to whitelist
834
- * @notice This function is virtual to allow extensions to add hook functionality
835
- */
836
- function _addTargetToWhitelist(bytes4 functionSelector, address target) internal virtual {
837
- _getSecureState().addTargetToWhitelist(functionSelector, target);
838
- }
839
-
840
- /**
841
- * @dev Centralized function to remove a target address from the whitelist for a function selector
842
- * @param functionSelector The function selector
843
- * @param target The target address to remove
844
- * @notice This function is virtual to allow extensions to add hook functionality
845
- */
846
- function _removeTargetFromWhitelist(bytes4 functionSelector, address target) internal virtual {
847
- _getSecureState().removeTargetFromWhitelist(functionSelector, target);
848
- }
849
-
850
- /**
851
- * @dev Gets all whitelisted targets for a function selector
852
- * @param functionSelector The function selector
853
- * @return Array of whitelisted target addresses
854
- * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
855
- */
856
- function getFunctionWhitelistTargets(bytes4 functionSelector) public view returns (address[] memory) {
857
- _validateAnyRole();
858
- return _getSecureState().getFunctionWhitelistTargets(functionSelector);
859
- }
860
-
861
- // ============ DEFINITION LOADING ============
862
-
863
- /**
864
- * @dev Loads definitions directly into the secure state
865
- * This function initializes the secure state with all predefined definitions
866
- * @param functionSchemas Array of function schema definitions
867
- * @param roleHashes Array of role hashes
868
- * @param functionPermissions Array of function permissions (parallel to roleHashes)
869
- * @param requireProtected When true, all function schemas must be protected; reverts if any is not
870
- * @notice When requireProtected is true, every function schema must have isProtected == true
871
- */
872
- function _loadDefinitions(
873
- EngineBlox.FunctionSchema[] memory functionSchemas,
874
- bytes32[] memory roleHashes,
875
- EngineBlox.FunctionPermission[] memory functionPermissions,
876
- bool requireProtected
877
- ) internal {
878
- // Load function schemas
879
- for (uint256 i = 0; i < functionSchemas.length; i++) {
880
- // When enforcing, require every schema to be protected
881
- if (requireProtected && !functionSchemas[i].isProtected) {
882
- revert SharedValidation.ContractFunctionMustBeProtected(
883
- functionSchemas[i].functionSelector
884
- );
885
- }
886
- _registerFunction(
887
- functionSchemas[i].functionSignature,
888
- functionSchemas[i].functionSelector,
889
- functionSchemas[i].operationName,
890
- functionSchemas[i].supportedActionsBitmap,
891
- functionSchemas[i].enforceHandlerRelations,
892
- functionSchemas[i].isProtected,
893
- functionSchemas[i].handlerForSelectors
894
- );
895
- }
896
-
897
- // Load role permissions using parallel arrays
898
- SharedValidation.validateArrayLengthMatch(roleHashes.length, functionPermissions.length);
899
- for (uint256 i = 0; i < roleHashes.length; i++) {
900
- _addFunctionToRole(roleHashes[i], functionPermissions[i]);
901
- }
902
- }
903
-
904
- // ============ INTERNAL UTILITIES ============
905
-
906
- /**
907
- * @dev Internal function to get the secure state
908
- * @return secureState The secure state
909
- */
910
- function _getSecureState() internal view returns (EngineBlox.SecureOperationState storage) {
911
- return _secureState;
912
- }
913
-
914
- /**
915
- * @dev Internal function to check if an address has action permission
916
- * @param caller The address to check
917
- * @param functionSelector The function selector
918
- * @param action The action to check
919
- * @return True if the caller has permission, false otherwise
920
- */
921
- function _hasActionPermission(
922
- address caller,
923
- bytes4 functionSelector,
924
- EngineBlox.TxAction action
925
- ) internal view returns (bool) {
926
- return _secureState.hasActionPermission(caller, functionSelector, action);
927
- }
928
-
929
- /**
930
- * @dev Internal helper to validate that a caller has the BROADCASTER_ROLE
931
- * @param caller The address to validate
932
- */
933
- function _validateBroadcaster(address caller) internal view {
934
- if (!hasRole(EngineBlox.BROADCASTER_ROLE, caller)) {
935
- revert SharedValidation.NoPermission(caller);
936
- }
937
- }
938
-
939
- /**
940
- * @dev Centralized component event logging for SecureOwnable, GuardController, RuntimeRBAC.
941
- * Uses msg.sig as the event index so callers only pass encoded data.
942
- * @param data abi.encode of event parameters
943
- */
944
- function _logComponentEvent(bytes memory data) internal {
945
- emit ComponentEvent(msg.sig, data);
946
- }
947
-
948
- }
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ pragma solidity 0.8.34;
3
+
4
+ // OpenZeppelin imports
5
+ import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
6
+ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
7
+ import "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
8
+
9
+ // Contracts imports
10
+ import "../lib/EngineBlox.sol";
11
+ import "../lib/utils/SharedValidation.sol";
12
+ import "./interface/IBaseStateMachine.sol";
13
+
14
+ /**
15
+ * @title BaseStateMachine
16
+ * @dev Core state machine functionality for secure multi-phase operations
17
+ *
18
+ * This contract provides the foundational state machine capabilities that can be extended
19
+ * by security-specific contracts. It handles:
20
+ * - State initialization and management
21
+ * - Meta-transaction utilities and parameter creation
22
+ * - State queries and transaction history
23
+ * - Role-based access control queries
24
+ * - System state information
25
+ *
26
+ * The contract is designed to be inherited by security-specific contracts that implement
27
+ * their own operation types and business logic while leveraging the core state machine.
28
+ * All access to EngineBlox library functions is centralized through BaseStateMachine
29
+ * wrapper functions to ensure consistency and maintainability.
30
+ *
31
+ * Key Features:
32
+ * - State initialization with role and permission setup
33
+ * - Meta-transaction parameter creation and generation
34
+ * - Comprehensive state queries and transaction history
35
+ * - Role and permission validation utilities
36
+ * - System configuration queries
37
+ * - Event forwarding for external monitoring
38
+ */
39
+ abstract contract BaseStateMachine is Initializable, ERC165Upgradeable, ReentrancyGuardTransient {
40
+ using EngineBlox for EngineBlox.SecureOperationState;
41
+ using SharedValidation for *;
42
+
43
+ EngineBlox.SecureOperationState internal _secureState;
44
+
45
+ /**
46
+ * @dev Unified component event for SecureOwnable, GuardController, RuntimeRBAC.
47
+ * Indexers filter by functionSelector (msg.sig at emit site) and decode data (abi-encoded payload).
48
+ * @param functionSelector The function selector (msg.sig) at the emit site; used by indexers to filter
49
+ * @param data ABI-encoded payload associated with the event
50
+ */
51
+ event ComponentEvent(bytes4 indexed functionSelector, bytes data);
52
+
53
+ /**
54
+ * @notice Initializes the base state machine core
55
+ * @param initialOwner The initial owner address
56
+ * @param broadcaster The broadcaster address
57
+ * @param recovery The recovery address
58
+ * @param timeLockPeriodSec The timelock period in seconds
59
+ * @param eventForwarder The event forwarder address
60
+ * @dev Reentrancy protection inherits OpenZeppelin {ReentrancyGuardTransient} (EIP-1153 transient storage; requires Cancun+ / configured EVM such as Osaka). No initializer is required.
61
+ */
62
+ function _initializeBaseStateMachine(
63
+ address initialOwner,
64
+ address broadcaster,
65
+ address recovery,
66
+ uint256 timeLockPeriodSec,
67
+ address eventForwarder
68
+ ) internal onlyInitializing {
69
+ // Skip if already initialized (e.g. when multiple components call from Account.initialize)
70
+ if (!_secureState.initialized) {
71
+ __ERC165_init();
72
+
73
+ _secureState.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec);
74
+
75
+ _secureState.setEventForwarder(eventForwarder);
76
+ }
77
+ }
78
+
79
+ // ============ SYSTEM ROLE QUERY FUNCTIONS ============
80
+
81
+ /**
82
+ * @dev Returns the owner of the contract
83
+ * @return The owner of the contract
84
+ */
85
+ function owner() public view returns (address) {
86
+ return _getAuthorizedWalletAt(EngineBlox.OWNER_ROLE, 0);
87
+ }
88
+
89
+ /**
90
+ * @dev Returns all broadcaster addresses for the BROADCASTER_ROLE
91
+ * @return Array of broadcaster addresses
92
+ */
93
+ function getBroadcasters() public view returns (address[] memory) {
94
+ return _getAuthorizedWallets(EngineBlox.BROADCASTER_ROLE);
95
+ }
96
+
97
+ /**
98
+ * @dev Returns the recovery address
99
+ * @return The recovery address
100
+ */
101
+ function getRecovery() public view returns (address) {
102
+ return _getAuthorizedWalletAt(EngineBlox.RECOVERY_ROLE, 0);
103
+ }
104
+
105
+ // ============ INTERFACE SUPPORT ============
106
+
107
+ /**
108
+ * @dev See {IERC165-supportsInterface}.
109
+ * @notice Base implementation for ERC165 interface detection
110
+ * @notice Registers IBaseStateMachine interface ID for proper interface detection
111
+ * @notice Component contracts (SecureOwnable, RuntimeRBAC, GuardController) should override
112
+ * to add their respective interface IDs for component detection
113
+ */
114
+ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
115
+ return interfaceId == type(IBaseStateMachine).interfaceId || super.supportsInterface(interfaceId);
116
+ }
117
+
118
+ // ============ TRANSACTION MANAGEMENT ============
119
+
120
+ /**
121
+ * @dev Centralized function to request a transaction with common validation
122
+ * @param requester The address requesting the transaction
123
+ * @param target The target contract address
124
+ * @param value The ETH value to send (typically 0 for standard function calls; non-zero is supported for payable edge-case workflows)
125
+ * @param gasLimit The gas limit for execution
126
+ * @param operationType The type of operation
127
+ * @param functionSelector The function selector for execution (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
128
+ * @param params The encoded parameters for the function (empty for simple native token transfers)
129
+ * @return The created transaction record
130
+ * @notice Validates permissions for the calling function (request function), not the execution selector
131
+ * @notice Execution functions are internal-only and don't need permission definitions
132
+ * @notice This function is virtual to allow extensions to add hook functionality
133
+ * @notice Recommended standard calls: value=0, functionSelector=non-zero, params=encoded data
134
+ * @notice Flexible edge case: non-native selectors may intentionally forward ETH to payable targets
135
+ * @notice Native-only convenience flow: value>0, functionSelector=NATIVE_TRANSFER_SELECTOR, params=""
136
+ */
137
+ function _requestTransaction(
138
+ address requester,
139
+ address target,
140
+ uint256 value,
141
+ uint256 gasLimit,
142
+ bytes32 operationType,
143
+ bytes4 functionSelector,
144
+ bytes memory params
145
+ ) internal virtual returns (EngineBlox.TxRecord memory) {
146
+ EngineBlox.TxRecord memory txRecord = EngineBlox.txRequest(
147
+ _getSecureState(),
148
+ requester,
149
+ target,
150
+ value,
151
+ gasLimit,
152
+ operationType,
153
+ bytes4(msg.sig),
154
+ functionSelector,
155
+ params
156
+ );
157
+ _postActionHook(txRecord);
158
+ return txRecord;
159
+ }
160
+
161
+ /**
162
+ * @dev Centralized function to request a transaction with payment details attached from the start
163
+ * @param requester The address requesting the transaction
164
+ * @param target The target contract address
165
+ * @param value The ETH value to send (typically 0 for standard function calls; non-zero is supported for payable edge-case workflows)
166
+ * @param gasLimit The gas limit for execution
167
+ * @param operationType The type of operation
168
+ * @param functionSelector The function selector for execution (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
169
+ * @param params The encoded parameters for the function (empty for simple native token transfers)
170
+ * @param paymentDetails The payment details to attach to the transaction
171
+ * @return The created transaction record with payment set
172
+ * @notice Validates request permissions (same as request without payment)
173
+ * @notice This function is virtual to allow extensions to add hook functionality
174
+ */
175
+ function _requestTransactionWithPayment(
176
+ address requester,
177
+ address target,
178
+ uint256 value,
179
+ uint256 gasLimit,
180
+ bytes32 operationType,
181
+ bytes4 functionSelector,
182
+ bytes memory params,
183
+ EngineBlox.PaymentDetails memory paymentDetails
184
+ ) internal virtual returns (EngineBlox.TxRecord memory) {
185
+ EngineBlox.TxRecord memory txRecord = EngineBlox.txRequestWithPayment(
186
+ _getSecureState(),
187
+ requester,
188
+ target,
189
+ value,
190
+ gasLimit,
191
+ operationType,
192
+ bytes4(msg.sig),
193
+ functionSelector,
194
+ params,
195
+ paymentDetails
196
+ );
197
+ _postActionHook(txRecord);
198
+ return txRecord;
199
+ }
200
+
201
+ /**
202
+ * @dev Centralized function to approve a pending transaction after release time
203
+ * @param txId The transaction ID
204
+ * @return The updated transaction record
205
+ * @notice Validates permissions for the calling function (approval function selector), not the execution selector
206
+ * @notice Execution functions are internal-only and don't need permission definitions
207
+ * @notice This function is virtual to allow extensions to add hook functionality
208
+ * @notice Protected by ReentrancyGuardTransient to prevent reentrancy attacks
209
+ */
210
+ function _approveTransaction(
211
+ uint256 txId
212
+ ) internal virtual nonReentrant returns (EngineBlox.TxRecord memory) {
213
+ EngineBlox.TxRecord memory txRecord = EngineBlox.txDelayedApproval(_getSecureState(), txId, bytes4(msg.sig));
214
+ _postActionHook(txRecord);
215
+ return txRecord;
216
+ }
217
+
218
+ /**
219
+ * @dev Centralized function to approve a transaction using meta-transaction
220
+ * @param metaTx The meta-transaction
221
+ * @return The updated transaction record
222
+ * @notice Validates permissions for the calling function (msg.sig) and handler selector from metaTx
223
+ * @notice Uses EXECUTE_META_APPROVE action for permission checking
224
+ * @notice This function is virtual to allow extensions to add hook functionality
225
+ * @notice Protected by ReentrancyGuardTransient to prevent reentrancy attacks
226
+ */
227
+ function _approveTransactionWithMetaTx(
228
+ EngineBlox.MetaTransaction memory metaTx
229
+ ) internal virtual nonReentrant returns (EngineBlox.TxRecord memory) {
230
+ _validateMetaTxHandlerBinding(metaTx);
231
+ EngineBlox.TxRecord memory txRecord = EngineBlox.txApprovalWithMetaTx(_getSecureState(), metaTx);
232
+ _postActionHook(txRecord);
233
+ return txRecord;
234
+ }
235
+
236
+ /**
237
+ * @dev Centralized function to cancel a pending transaction
238
+ * @param txId The transaction ID
239
+ * @return The updated transaction record
240
+ * @notice Validates permissions for the calling function (cancellation function selector), not the execution selector
241
+ * @notice Execution functions are internal-only and don't need permission definitions
242
+ * @notice This function is virtual to allow extensions to add hook functionality
243
+ */
244
+ function _cancelTransaction(
245
+ uint256 txId
246
+ ) internal virtual returns (EngineBlox.TxRecord memory) {
247
+ EngineBlox.TxRecord memory txRecord = EngineBlox.txCancellation(_getSecureState(), txId, bytes4(msg.sig));
248
+ _postActionHook(txRecord);
249
+ return txRecord;
250
+ }
251
+
252
+ /**
253
+ * @dev Centralized function to cancel a transaction using meta-transaction
254
+ * @param metaTx The meta-transaction
255
+ * @return The updated transaction record
256
+ * @notice Validates permissions for the calling function (msg.sig) and handler selector from metaTx
257
+ * @notice Uses EXECUTE_META_CANCEL action for permission checking
258
+ * @notice This function is virtual to allow extensions to add hook functionality
259
+ */
260
+ function _cancelTransactionWithMetaTx(
261
+ EngineBlox.MetaTransaction memory metaTx
262
+ ) internal virtual returns (EngineBlox.TxRecord memory) {
263
+ _validateMetaTxHandlerBinding(metaTx);
264
+ EngineBlox.TxRecord memory txRecord = EngineBlox.txCancellationWithMetaTx(_getSecureState(), metaTx);
265
+ _postActionHook(txRecord);
266
+ return txRecord;
267
+ }
268
+
269
+ /**
270
+ * @dev Centralized function to request and approve a transaction using meta-transaction
271
+ * @param metaTx The meta-transaction
272
+ * @return The transaction record
273
+ * @notice Validates permissions for the calling function (msg.sig) and handler selector from metaTx
274
+ * @notice Uses EXECUTE_META_REQUEST_AND_APPROVE action for permission checking
275
+ * @notice This function is virtual to allow extensions to add hook functionality
276
+ * @notice Protected by ReentrancyGuardTransient to prevent reentrancy attacks
277
+ */
278
+ function _requestAndApproveTransaction(
279
+ EngineBlox.MetaTransaction memory metaTx
280
+ ) internal virtual nonReentrant returns (EngineBlox.TxRecord memory) {
281
+ _validateMetaTxHandlerBinding(metaTx);
282
+ EngineBlox.TxRecord memory txRecord = EngineBlox.requestAndApprove(_getSecureState(), metaTx);
283
+ _postActionHook(txRecord);
284
+ return txRecord;
285
+ }
286
+
287
+ /**
288
+ * @dev Post-action hook invoked after any transaction operation that produces a TxRecord.
289
+ * Override in derived contracts to add centralized post-tx logic (e.g. notifications, side effects).
290
+ * @param txRecord The transaction record produced by the operation
291
+ */
292
+ function _postActionHook(EngineBlox.TxRecord memory txRecord) internal virtual {}
293
+
294
+ // ============ HOOK MANAGEMENT ============
295
+
296
+ /**
297
+ * @dev Sets the hook contract for a function selector (internal; no access control).
298
+ * Extensions (e.g. HookManager) may expose an external setHook with owner check.
299
+ * @param functionSelector The function selector
300
+ * @param hook The hook contract address (must not be zero)
301
+ */
302
+ function _setHook(bytes4 functionSelector, address hook) internal {
303
+ EngineBlox.setHook(_getSecureState(), functionSelector, hook);
304
+ }
305
+
306
+ /**
307
+ * @dev Clears the hook contract for a function selector (internal; no access control).
308
+ * Extensions may expose an external clearHook with owner check.
309
+ * @param functionSelector The function selector
310
+ * @param hook The hook contract address to remove (must not be zero)
311
+ */
312
+ function _clearHook(bytes4 functionSelector, address hook) internal {
313
+ EngineBlox.clearHook(_getSecureState(), functionSelector, hook);
314
+ }
315
+
316
+ /**
317
+ * @dev Returns all configured hooks for a function selector
318
+ * @param functionSelector The function selector
319
+ * @return hooks Array of hook contract addresses
320
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
321
+ */
322
+ function getHooks(bytes4 functionSelector) public view returns (address[] memory hooks) {
323
+ _validateAnyRole();
324
+ return EngineBlox.getHooks(_getSecureState(), functionSelector);
325
+ }
326
+
327
+ // ============ META-TRANSACTION UTILITIES ============
328
+
329
+ /**
330
+ * @dev Creates meta-transaction parameters with specified values
331
+ * @param handlerContract The contract that will handle the meta-transaction
332
+ * @param handlerSelector The function selector for the handler
333
+ * @param action The transaction action type
334
+ * @param deadline The timestamp after which the meta-transaction expires
335
+ * @param maxGasPrice The maximum gas price allowed for execution
336
+ * @param signer The address that will sign the meta-transaction
337
+ * @return The formatted meta-transaction parameters
338
+ */
339
+ function createMetaTxParams(
340
+ address handlerContract,
341
+ bytes4 handlerSelector,
342
+ EngineBlox.TxAction action,
343
+ uint256 deadline,
344
+ uint256 maxGasPrice,
345
+ address signer
346
+ ) public view returns (EngineBlox.MetaTxParams memory) {
347
+ return EngineBlox.createMetaTxParams(
348
+ handlerContract,
349
+ handlerSelector,
350
+ action,
351
+ deadline,
352
+ maxGasPrice,
353
+ signer
354
+ );
355
+ }
356
+
357
+ /**
358
+ * @dev Generates an unsigned meta-transaction for a new operation
359
+ * @param requester The address requesting the operation
360
+ * @param target The target contract address
361
+ * @param value The ETH value to send
362
+ * @param gasLimit The gas limit for execution
363
+ * @param operationType The type of operation
364
+ * @param executionSelector The function selector to execute (NATIVE_TRANSFER_SELECTOR for simple native token transfers)
365
+ * @param executionParams The encoded parameters for the function (empty for simple native token transfers)
366
+ * @param metaTxParams The meta-transaction parameters
367
+ * @return The unsigned meta-transaction
368
+ */
369
+ function generateUnsignedMetaTransactionForNew(
370
+ address requester,
371
+ address target,
372
+ uint256 value,
373
+ uint256 gasLimit,
374
+ bytes32 operationType,
375
+ bytes4 executionSelector,
376
+ bytes memory executionParams,
377
+ EngineBlox.MetaTxParams memory metaTxParams
378
+ ) public view returns (EngineBlox.MetaTransaction memory) {
379
+ EngineBlox.TxParams memory txParams = EngineBlox.TxParams({
380
+ requester: requester,
381
+ target: target,
382
+ value: value,
383
+ gasLimit: gasLimit,
384
+ operationType: operationType,
385
+ executionSelector: executionSelector,
386
+ executionParams: executionParams
387
+ });
388
+
389
+ return _secureState.generateUnsignedForNewMetaTx(txParams, metaTxParams);
390
+ }
391
+
392
+ /**
393
+ * @dev Generates an unsigned meta-transaction for an existing transaction
394
+ * @param txId The ID of the existing transaction
395
+ * @param metaTxParams The meta-transaction parameters
396
+ * @return The unsigned meta-transaction
397
+ */
398
+ function generateUnsignedMetaTransactionForExisting(
399
+ uint256 txId,
400
+ EngineBlox.MetaTxParams memory metaTxParams
401
+ ) public view returns (EngineBlox.MetaTransaction memory) {
402
+ return _secureState.generateUnsignedForExistingMetaTx(txId, metaTxParams);
403
+ }
404
+
405
+ // ============ STATE QUERIES ============
406
+
407
+ /**
408
+ * @dev Gets transaction history within a specified range
409
+ * @param fromTxId The starting transaction ID (inclusive)
410
+ * @param toTxId The ending transaction ID (inclusive)
411
+ * @return The transaction history within the specified range
412
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
413
+ * @notice Returns an empty array when **`txCounter == 0`** or when the range, after clamping to **1..txCounter**,
414
+ * does not overlap any transaction ids (e.g. `fromTxId` entirely above the current counter).
415
+ */
416
+ function getTransactionHistory(uint256 fromTxId, uint256 toTxId) public view returns (EngineBlox.TxRecord[] memory) {
417
+ _validateAnyRole();
418
+
419
+ uint256 counter = _secureState.txCounter;
420
+
421
+ // Normalize bounds to valid transaction id range [1, counter]
422
+ fromTxId = fromTxId > 0 ? fromTxId : 1;
423
+ toTxId = toTxId > counter ? counter : toTxId;
424
+
425
+ // Empty history: invalid / non-overlapping clamped range (includes txCounter == 0 → toTxId == 0).
426
+ uint256 rangeSize = fromTxId > toTxId ? 0 : toTxId - fromTxId + 1;
427
+
428
+ if (rangeSize > 0) {
429
+ SharedValidation.validateRangeSize(rangeSize, 1000);
430
+ }
431
+
432
+ EngineBlox.TxRecord[] memory history = new EngineBlox.TxRecord[](rangeSize);
433
+ for (uint256 i = 0; i < rangeSize; i++) {
434
+ history[i] = _secureState.getTxRecord(fromTxId + i);
435
+ }
436
+ return history;
437
+ }
438
+
439
+ /**
440
+ * @dev Gets a transaction by ID
441
+ * @param txId The transaction ID
442
+ * @return The transaction record
443
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
444
+ */
445
+ function getTransaction(uint256 txId) public view returns (EngineBlox.TxRecord memory) {
446
+ _validateAnyRole();
447
+ return _secureState.getTxRecord(txId);
448
+ }
449
+
450
+ /**
451
+ * @dev Gets all pending transaction IDs
452
+ * @return Array of pending transaction IDs
453
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
454
+ */
455
+ function getPendingTransactions() public view returns (uint256[] memory) {
456
+ _validateAnyRole();
457
+ return _secureState.getPendingTransactions();
458
+ }
459
+
460
+ // ============ ROLE AND PERMISSION QUERIES ============
461
+
462
+ /**
463
+ * @dev Gets the basic role information by its hash
464
+ * @param roleHash The hash of the role to get
465
+ * @return roleName The name of the role
466
+ * @return hash The hash of the role
467
+ * @return maxWallets The maximum number of wallets allowed for this role
468
+ * @return walletCount The current number of wallets assigned to this role
469
+ * @return isProtected Whether the role is protected from removal
470
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
471
+ */
472
+ function getRole(bytes32 roleHash) public view returns (
473
+ string memory roleName,
474
+ bytes32 hash,
475
+ uint256 maxWallets,
476
+ uint256 walletCount,
477
+ bool isProtected
478
+ ) {
479
+ _validateAnyRole();
480
+ EngineBlox.Role storage role = _secureState.getRole(roleHash);
481
+ return (
482
+ role.roleName,
483
+ role.roleHash,
484
+ role.maxWallets,
485
+ role.walletCount,
486
+ role.isProtected
487
+ );
488
+ }
489
+
490
+ /**
491
+ * @dev Returns if a wallet is authorized for a role
492
+ * @param roleHash The hash of the role to check
493
+ * @param wallet The wallet address to check
494
+ * @return True if the wallet is authorized for the role, false otherwise
495
+ */
496
+ function hasRole(bytes32 roleHash, address wallet) public view returns (bool) {
497
+ return _secureState.hasRole(roleHash, wallet);
498
+ }
499
+
500
+ /**
501
+ * @dev Gets all roles assigned to a wallet
502
+ * @param wallet The wallet address to get roles for
503
+ * @return Array of role hashes assigned to the wallet
504
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
505
+ * @notice This function uses the reverse index for efficient lookup
506
+ */
507
+ function getWalletRoles(address wallet) public view returns (bytes32[] memory) {
508
+ _validateAnyRole();
509
+ return EngineBlox.getWalletRoles(_getSecureState(), wallet);
510
+ }
511
+
512
+ /**
513
+ * @dev Gets all authorized wallets for a role
514
+ * @param roleHash The role hash to get wallets for
515
+ * @return Array of authorized wallet addresses
516
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
517
+ */
518
+ function getAuthorizedWallets(bytes32 roleHash) public view returns (address[] memory) {
519
+ _validateAnyRole();
520
+ _validateRoleExists(roleHash);
521
+ return _getAuthorizedWallets(roleHash);
522
+ }
523
+
524
+ /**
525
+ * @dev Gets function schema information
526
+ * @param functionSelector The function selector to get information for
527
+ * @return The full FunctionSchema struct (functionSignature, functionSelector, operationType, operationName, supportedActionsBitmap, enforceHandlerRelations, isProtected, handlerForSelectors)
528
+ * @notice Reverts with ResourceNotFound if the schema does not exist
529
+ */
530
+ function getFunctionSchema(bytes4 functionSelector) external view returns (EngineBlox.FunctionSchema memory) {
531
+ _validateAnyRole();
532
+ return _secureState.getFunctionSchema(functionSelector);
533
+ }
534
+
535
+ /**
536
+ * @dev Gets the function permissions for a specific role
537
+ * @param roleHash The hash of the role to get permissions for
538
+ * @return The function permissions array for the role
539
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
540
+ */
541
+ function getActiveRolePermissions(bytes32 roleHash) public view returns (EngineBlox.FunctionPermission[] memory) {
542
+ _validateAnyRole();
543
+ return _secureState.getRoleFunctionPermissions(roleHash);
544
+ }
545
+
546
+ /**
547
+ * @dev Gets the current nonce for a specific signer
548
+ * @param signer The address of the signer
549
+ * @return The current nonce for the signer
550
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
551
+ */
552
+ function getSignerNonce(address signer) public view returns (uint256) {
553
+ _validateAnyRole();
554
+ return _secureState.getSignerNonce(signer);
555
+ }
556
+
557
+ // ============ SYSTEM STATE QUERIES ============
558
+
559
+ /**
560
+ * @dev Returns the supported operation types
561
+ * @return The supported operation types
562
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
563
+ */
564
+ function getSupportedOperationTypes() public view returns (bytes32[] memory) {
565
+ _validateAnyRole();
566
+ return _secureState.getSupportedOperationTypes();
567
+ }
568
+
569
+ /**
570
+ * @dev Returns the supported roles list
571
+ * @return The supported roles list
572
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
573
+ */
574
+ function getSupportedRoles() public view returns (bytes32[] memory) {
575
+ _validateAnyRole();
576
+ return _secureState.getSupportedRoles();
577
+ }
578
+
579
+ /**
580
+ * @dev Returns the supported functions list
581
+ * @return The supported functions list
582
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
583
+ */
584
+ function getSupportedFunctions() public view returns (bytes4[] memory) {
585
+ _validateAnyRole();
586
+ return _secureState.getSupportedFunctions();
587
+ }
588
+
589
+ /**
590
+ * @dev Returns the time lock period
591
+ * @return The time lock period in seconds
592
+ */
593
+ function getTimeLockPeriodSec() public view returns (uint256) {
594
+ return _secureState.timeLockPeriodSec;
595
+ }
596
+
597
+ /**
598
+ * @dev Returns whether the contract is initialized
599
+ * @return bool True if the contract is initialized, false otherwise
600
+ */
601
+ function initialized() public view returns (bool) {
602
+ return _getInitializedVersion() != type(uint8).max && _secureState.initialized;
603
+ }
604
+
605
+ // ============ ROLE MANAGEMENT ============
606
+
607
+ /**
608
+ * @dev Centralized function to get authorized wallet at specific index
609
+ * @param roleHash The role hash
610
+ * @param index The wallet index
611
+ * @return The authorized wallet address
612
+ */
613
+ function _getAuthorizedWalletAt(bytes32 roleHash, uint256 index) internal view returns (address) {
614
+ return EngineBlox.getAuthorizedWalletAt(_getSecureState(), roleHash, index);
615
+ }
616
+
617
+ /**
618
+ * @dev Centralized function to get all authorized wallets for a role
619
+ * @param roleHash The role hash
620
+ * @return Array of authorized wallet addresses
621
+ */
622
+ function _getAuthorizedWallets(bytes32 roleHash) internal view returns (address[] memory) {
623
+ return EngineBlox.getAuthorizedWallets(_getSecureState(), roleHash);
624
+ }
625
+
626
+ /**
627
+ * @dev Centralized function to create a new role
628
+ * @param roleName The name of the role
629
+ * @param maxWallets The maximum number of wallets allowed for this role
630
+ * @param isProtected Whether the role is protected from removal
631
+ * @return roleHash The hash of the created role
632
+ * @notice This function is virtual to allow extensions to add hook functionality
633
+ */
634
+ function _createRole(
635
+ string memory roleName,
636
+ uint256 maxWallets,
637
+ bool isProtected
638
+ ) internal virtual returns (bytes32) {
639
+ bytes32 roleHash = keccak256(bytes(roleName));
640
+ EngineBlox.createRole(_getSecureState(), roleName, maxWallets, isProtected);
641
+ return roleHash;
642
+ }
643
+
644
+ /**
645
+ * @dev Centralized function to remove a role
646
+ * @param roleHash The hash of the role to remove
647
+ * @notice This function is virtual to allow extensions to add hook functionality
648
+ */
649
+ function _removeRole(bytes32 roleHash) internal virtual {
650
+ EngineBlox.removeRole(_getSecureState(), roleHash);
651
+ }
652
+
653
+ /**
654
+ * @dev Centralized function to assign a wallet to a role
655
+ * @param roleHash The role hash
656
+ * @param wallet The wallet address to assign
657
+ * @notice This function is virtual to allow extensions to add hook functionality
658
+ */
659
+ function _assignWallet(bytes32 roleHash, address wallet) internal virtual {
660
+ EngineBlox.assignWallet(_getSecureState(), roleHash, wallet);
661
+ }
662
+
663
+ /**
664
+ * @dev Centralized function to revoke a wallet from a role
665
+ * @param roleHash The role hash
666
+ * @param wallet The wallet address to revoke
667
+ * @notice This function is virtual to allow extensions to add hook functionality
668
+ */
669
+ function _revokeWallet(bytes32 roleHash, address wallet) internal virtual {
670
+ EngineBlox.revokeWallet(_getSecureState(), roleHash, wallet);
671
+ }
672
+
673
+ /**
674
+ * @dev Centralized function to update wallet for a role (replaces oldWallet with newWallet).
675
+ * @param roleHash The role hash
676
+ * @param newWallet The new wallet address
677
+ * @param oldWallet The old wallet address
678
+ * @notice This function is virtual to allow extensions to add hook functionality or additional validation
679
+ */
680
+ function _updateWallet(bytes32 roleHash, address newWallet, address oldWallet) internal virtual {
681
+ EngineBlox.updateWallet(_getSecureState(), roleHash, newWallet, oldWallet);
682
+ }
683
+
684
+ /**
685
+ * @dev Centralized function to update the time lock period
686
+ * @param newTimeLockPeriodSec The new time lock period in seconds
687
+ * @notice This function is virtual to allow extensions to add hook functionality
688
+ */
689
+ function _updateTimeLockPeriod(uint256 newTimeLockPeriodSec) internal virtual {
690
+ uint256 oldPeriod = getTimeLockPeriodSec();
691
+ EngineBlox.updateTimeLockPeriod(_getSecureState(), newTimeLockPeriodSec);
692
+ _logComponentEvent(abi.encode(oldPeriod, newTimeLockPeriodSec));
693
+ }
694
+
695
+ // ============ FUNCTION SCHEMA MANAGEMENT ============
696
+
697
+ /**
698
+ * @dev Centralized function to register a function schema
699
+ * @param functionSignature The function signature
700
+ * @param functionSelector The function selector
701
+ * @param operationName The operation name
702
+ * @param supportedActionsBitmap The bitmap of supported actions
703
+ * @param enforceHandlerRelations Whether to enforce strict handler/schema alignment
704
+ * @param isProtected Whether the function schema is protected
705
+ * @param handlerForSelectors Array of handler selectors
706
+ * @notice This function is virtual to allow extensions to add hook functionality
707
+ */
708
+ function _registerFunction(
709
+ string memory functionSignature,
710
+ bytes4 functionSelector,
711
+ string memory operationName,
712
+ uint16 supportedActionsBitmap,
713
+ bool enforceHandlerRelations,
714
+ bool isProtected,
715
+ bytes4[] memory handlerForSelectors
716
+ ) internal virtual {
717
+ EngineBlox.registerFunction(
718
+ _getSecureState(),
719
+ functionSignature,
720
+ functionSelector,
721
+ operationName,
722
+ supportedActionsBitmap,
723
+ enforceHandlerRelations,
724
+ isProtected,
725
+ handlerForSelectors
726
+ );
727
+ }
728
+
729
+ /**
730
+ * @dev Centralized function to unregister a function schema
731
+ * @param functionSelector The function selector to unregister
732
+ * @param safeRemoval Whether to perform safe removal (check for role references)
733
+ * @notice This function is virtual to allow extensions to add hook functionality
734
+ */
735
+ function _unregisterFunction(bytes4 functionSelector, bool safeRemoval) internal virtual {
736
+ EngineBlox.unregisterFunction(_getSecureState(), functionSelector, safeRemoval);
737
+ }
738
+
739
+ /**
740
+ * @dev Centralized function to add a function permission to a role
741
+ * @param roleHash The role hash
742
+ * @param functionPermission The function permission to add
743
+ * @notice This function is virtual to allow extensions to add hook functionality
744
+ */
745
+ function _addFunctionToRole(
746
+ bytes32 roleHash,
747
+ EngineBlox.FunctionPermission memory functionPermission
748
+ ) internal virtual {
749
+ EngineBlox.addFunctionToRole(_getSecureState(), roleHash, functionPermission);
750
+ }
751
+
752
+ /**
753
+ * @dev Centralized function to remove a function permission from a role
754
+ * @param roleHash The role hash
755
+ * @param functionSelector The function selector to remove
756
+ * @notice This function is virtual to allow extensions to add hook functionality
757
+ */
758
+ function _removeFunctionFromRole(bytes32 roleHash, bytes4 functionSelector) internal virtual {
759
+ EngineBlox.removeFunctionFromRole(_getSecureState(), roleHash, functionSelector);
760
+ }
761
+
762
+ // ============ PERMISSION VALIDATION ============
763
+
764
+ /**
765
+ * @dev Binds signed `MetaTxParams` to this wrapper's entrypoint (`msg.sig`) and verifying contract.
766
+ * Must run in `BaseStateMachine` context, not inside linked `EngineBlox` library code (delegatecall
767
+ * would make `msg.sig` refer to the library function, not the outer wrapper).
768
+ * @param metaTx The meta-transaction whose `params` are validated against `msg.sig` and `address(this)`.
769
+ */
770
+ function _validateMetaTxHandlerBinding(EngineBlox.MetaTransaction memory metaTx) internal view {
771
+ SharedValidation.validateMetaTxHandlerSelectorBinding(
772
+ metaTx.params.handlerSelector,
773
+ bytes4(msg.sig)
774
+ );
775
+ SharedValidation.validateMetaTxHandlerContractBinding(metaTx.params.handlerContract);
776
+ }
777
+
778
+ /**
779
+ * @dev Centralized function to validate that the caller has any role
780
+ */
781
+ function _validateAnyRole() internal view {
782
+ EngineBlox._validateAnyRole(_getSecureState());
783
+ }
784
+
785
+ /**
786
+ * @dev Centralized function to validate that a role exists
787
+ * @param roleHash The role hash to validate
788
+ */
789
+ function _validateRoleExists(bytes32 roleHash) internal view {
790
+ EngineBlox._validateRoleExists(_getSecureState(), roleHash);
791
+ }
792
+
793
+ /**
794
+ * @dev Centralized function to validate that the caller is the contract itself (for execution-only entry points).
795
+ */
796
+ function _validateExecuteBySelf() internal view {
797
+ SharedValidation.validateInternalCall(address(this));
798
+ }
799
+
800
+ /**
801
+ * @dev Centralized function to validate batch size against EngineBlox.MAX_BATCH_SIZE.
802
+ * @param length The batch length to validate
803
+ */
804
+ function _validateBatchSize(uint256 length) internal pure {
805
+ SharedValidation.validateBatchSize(length, EngineBlox.MAX_BATCH_SIZE);
806
+ }
807
+
808
+ // ============ MACRO SELECTORS ============
809
+
810
+ /**
811
+ * @dev Adds a function selector to the system macro selectors set.
812
+ * Macro selectors are allowed to target address(this) for system-level operations.
813
+ * @param functionSelector The function selector to add (e.g. NATIVE_TRANSFER_SELECTOR).
814
+ */
815
+ function _addMacroSelector(bytes4 functionSelector) internal {
816
+ EngineBlox.addMacroSelector(_getSecureState(), functionSelector);
817
+ }
818
+
819
+ /**
820
+ * @dev Returns true if the given function selector is in the system macro selectors set.
821
+ * @param functionSelector The function selector to check.
822
+ */
823
+ function _isMacroSelector(bytes4 functionSelector) internal view returns (bool) {
824
+ return EngineBlox.isMacroSelector(_getSecureState(), functionSelector);
825
+ }
826
+
827
+ // ============ UTILITY FUNCTIONS ============
828
+
829
+ /**
830
+ * @dev Centralized function to convert a bitmap to an array of actions
831
+ * @param bitmap The bitmap to convert
832
+ * @return Array of TxAction values
833
+ */
834
+ function _convertBitmapToActions(uint16 bitmap) internal pure returns (EngineBlox.TxAction[] memory) {
835
+ return EngineBlox.convertBitmapToActions(bitmap);
836
+ }
837
+
838
+ /**
839
+ * @dev Centralized function to create a bitmap from an array of actions
840
+ * @param actions Array of TxAction values
841
+ * @return The bitmap representation
842
+ */
843
+ function _createBitmapFromActions(EngineBlox.TxAction[] memory actions) internal pure returns (uint16) {
844
+ return EngineBlox.createBitmapFromActions(actions);
845
+ }
846
+
847
+ // ============ TARGET WHITELIST MANAGEMENT ============
848
+
849
+ /**
850
+ * @dev Centralized function to add a target address to the whitelist for a function selector
851
+ * @param functionSelector The function selector
852
+ * @param target The target address to whitelist
853
+ * @notice This function is virtual to allow extensions to add hook functionality
854
+ */
855
+ function _addTargetToWhitelist(bytes4 functionSelector, address target) internal virtual {
856
+ _getSecureState().addTargetToWhitelist(functionSelector, target);
857
+ }
858
+
859
+ /**
860
+ * @dev Centralized function to remove a target address from the whitelist for a function selector
861
+ * @param functionSelector The function selector
862
+ * @param target The target address to remove
863
+ * @notice This function is virtual to allow extensions to add hook functionality
864
+ */
865
+ function _removeTargetFromWhitelist(bytes4 functionSelector, address target) internal virtual {
866
+ _getSecureState().removeTargetFromWhitelist(functionSelector, target);
867
+ }
868
+
869
+ /**
870
+ * @dev Gets all whitelisted targets for a function selector
871
+ * @param functionSelector The function selector
872
+ * @return Array of whitelisted target addresses
873
+ * @notice Requires caller to have any role (via _validateAnyRole) to limit information visibility
874
+ */
875
+ function getFunctionWhitelistTargets(bytes4 functionSelector) public view returns (address[] memory) {
876
+ _validateAnyRole();
877
+ return _getSecureState().getFunctionWhitelistTargets(functionSelector);
878
+ }
879
+
880
+ // ============ DEFINITION LOADING ============
881
+
882
+ /**
883
+ * @dev Loads definitions directly into the secure state
884
+ * This function initializes the secure state with all predefined definitions
885
+ * @param functionSchemas Array of function schema definitions
886
+ * @param roleHashes Array of role hashes
887
+ * @param functionPermissions Array of function permissions (parallel to roleHashes)
888
+ * @param requireProtected When true, all function schemas must be protected; reverts if any is not
889
+ * @notice When requireProtected is true, every function schema must have isProtected == true
890
+ */
891
+ function _loadDefinitions(
892
+ EngineBlox.FunctionSchema[] memory functionSchemas,
893
+ bytes32[] memory roleHashes,
894
+ EngineBlox.FunctionPermission[] memory functionPermissions,
895
+ bool requireProtected
896
+ ) internal {
897
+ // Load function schemas
898
+ for (uint256 i = 0; i < functionSchemas.length; i++) {
899
+ // When enforcing, require every schema to be protected
900
+ if (requireProtected && !functionSchemas[i].isProtected) {
901
+ revert SharedValidation.ContractFunctionMustBeProtected(
902
+ functionSchemas[i].functionSelector
903
+ );
904
+ }
905
+ _registerFunction(
906
+ functionSchemas[i].functionSignature,
907
+ functionSchemas[i].functionSelector,
908
+ functionSchemas[i].operationName,
909
+ functionSchemas[i].supportedActionsBitmap,
910
+ functionSchemas[i].enforceHandlerRelations,
911
+ functionSchemas[i].isProtected,
912
+ functionSchemas[i].handlerForSelectors
913
+ );
914
+ }
915
+
916
+ // Load role permissions using parallel arrays
917
+ SharedValidation.validateArrayLengthMatch(roleHashes.length, functionPermissions.length);
918
+ for (uint256 i = 0; i < roleHashes.length; i++) {
919
+ _addFunctionToRole(roleHashes[i], functionPermissions[i]);
920
+ }
921
+ }
922
+
923
+ // ============ INTERNAL UTILITIES ============
924
+
925
+ /**
926
+ * @dev Internal function to get the secure state
927
+ * @return secureState The secure state
928
+ */
929
+ function _getSecureState() internal view returns (EngineBlox.SecureOperationState storage) {
930
+ return _secureState;
931
+ }
932
+
933
+ /**
934
+ * @dev Internal function to check if an address has action permission
935
+ * @param caller The address to check
936
+ * @param functionSelector The function selector
937
+ * @param action The action to check
938
+ * @return True if the caller has permission, false otherwise
939
+ */
940
+ function _hasActionPermission(
941
+ address caller,
942
+ bytes4 functionSelector,
943
+ EngineBlox.TxAction action
944
+ ) internal view returns (bool) {
945
+ return _secureState.hasActionPermission(caller, functionSelector, action);
946
+ }
947
+
948
+ /**
949
+ * @dev Internal helper to validate that a caller has the BROADCASTER_ROLE
950
+ * @param caller The address to validate
951
+ */
952
+ function _validateBroadcaster(address caller) internal view {
953
+ if (!hasRole(EngineBlox.BROADCASTER_ROLE, caller)) {
954
+ revert SharedValidation.NoPermission(caller);
955
+ }
956
+ }
957
+
958
+ /**
959
+ * @dev Centralized component event logging for SecureOwnable, GuardController, RuntimeRBAC.
960
+ * Uses msg.sig as the event index so callers only pass encoded data.
961
+ * @param data abi.encode of event parameters
962
+ */
963
+ function _logComponentEvent(bytes memory data) internal {
964
+ emit ComponentEvent(msg.sig, data);
965
+ }
966
+
967
+ }