@bloxchain/contracts 1.0.0-alpha.20 → 1.0.0-alpha.22
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.
- package/README.md +5 -6
- package/abi/BaseStateMachine.abi.json +780 -775
- package/abi/EngineBlox.abi.json +545 -538
- package/abi/GuardController.abi.json +1590 -1585
- package/abi/GuardControllerDefinitions.abi.json +231 -226
- package/abi/IDefinition.abi.json +54 -49
- package/abi/RuntimeRBAC.abi.json +832 -827
- package/abi/RuntimeRBACDefinitions.abi.json +212 -207
- package/abi/SecureOwnable.abi.json +1359 -1321
- package/abi/SecureOwnableDefinitions.abi.json +171 -166
- package/core/access/RuntimeRBAC.sol +7 -9
- package/core/access/interface/IRuntimeRBAC.sol +1 -1
- package/core/access/lib/definitions/RuntimeRBACDefinitions.sol +5 -3
- package/core/base/BaseStateMachine.sol +7 -3
- package/core/base/interface/IBaseStateMachine.sol +1 -1
- package/core/execution/GuardController.sol +2 -1
- package/core/execution/interface/IGuardController.sol +1 -1
- package/core/execution/lib/definitions/GuardControllerDefinitions.sol +21 -9
- package/core/lib/EngineBlox.sol +71 -118
- package/core/lib/interfaces/IDefinition.sol +1 -1
- package/core/lib/interfaces/IEventForwarder.sol +35 -33
- package/core/lib/utils/SharedValidation.sol +9 -15
- package/core/pattern/Account.sol +1 -1
- package/core/security/SecureOwnable.sol +446 -456
- package/core/security/interface/ISecureOwnable.sol +5 -5
- package/core/security/lib/definitions/SecureOwnableDefinitions.sol +818 -802
- package/package.json +3 -3
- package/standards/behavior/ICopyable.sol +1 -1
- package/standards/hooks/IOnActionHook.sol +1 -1
package/core/lib/EngineBlox.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.35;
|
|
3
3
|
|
|
4
4
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
5
5
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
@@ -56,11 +56,6 @@ library EngineBlox {
|
|
|
56
56
|
|
|
57
57
|
/// @dev Maximum total number of functions allowed in the system (prevents gas exhaustion in function operations)
|
|
58
58
|
uint256 public constant MAX_FUNCTIONS = 2000;
|
|
59
|
-
|
|
60
|
-
/// @dev Maximum bytes copied from call returndata into memory (and later persisted in `TxRecord.result`).
|
|
61
|
-
/// Full returndata from the callee may be larger; only the first `MAX_RESULT_PREVIEW_BYTES` are retained.
|
|
62
|
-
/// Chosen as 32 KiB to maximize debug/audit surface while bounding memory expansion and storage growth.
|
|
63
|
-
uint256 public constant MAX_RESULT_PREVIEW_BYTES = 32 * 1024;
|
|
64
59
|
|
|
65
60
|
using SharedValidation for *;
|
|
66
61
|
using EnumerableSet for EnumerableSet.UintSet;
|
|
@@ -75,8 +70,7 @@ library EngineBlox {
|
|
|
75
70
|
PROCESSING_PAYMENT,
|
|
76
71
|
CANCELLED,
|
|
77
72
|
COMPLETED,
|
|
78
|
-
FAILED
|
|
79
|
-
REJECTED
|
|
73
|
+
FAILED
|
|
80
74
|
}
|
|
81
75
|
|
|
82
76
|
enum TxAction {
|
|
@@ -118,7 +112,9 @@ library EngineBlox {
|
|
|
118
112
|
TxStatus status;
|
|
119
113
|
TxParams params;
|
|
120
114
|
bytes32 message;
|
|
121
|
-
|
|
115
|
+
/// @dev Commitment to execution returndata: `bytes32(0)` when empty, else `keccak256(returndata)`.
|
|
116
|
+
/// Full returndata is emitted in `TxExecutionResult` on terminal execution (COMPLETED/FAILED).
|
|
117
|
+
bytes32 resultHash;
|
|
122
118
|
PaymentDetails payment;
|
|
123
119
|
}
|
|
124
120
|
|
|
@@ -164,6 +160,9 @@ library EngineBlox {
|
|
|
164
160
|
/// When false (flexible mode): no such check; forward references and unregistered selectors in handlerForSelectors are allowed at registration.
|
|
165
161
|
bool enforceHandlerRelations;
|
|
166
162
|
bool isProtected;
|
|
163
|
+
/// @dev When false, `removeFunctionFromRole` cannot remove this selector from any role (revoke + re-add updates blocked).
|
|
164
|
+
/// When true, grants may be removed from any role, including protected system roles; `isProtected` still blocks `unregisterFunction` for the schema.
|
|
165
|
+
bool isGrantRevocable;
|
|
167
166
|
bytes4[] handlerForSelectors;
|
|
168
167
|
}
|
|
169
168
|
|
|
@@ -254,9 +253,15 @@ library EngineBlox {
|
|
|
254
253
|
TxStatus status,
|
|
255
254
|
address indexed requester,
|
|
256
255
|
address target,
|
|
257
|
-
bytes32 operationType
|
|
256
|
+
bytes32 operationType,
|
|
257
|
+
bytes32 resultHash
|
|
258
258
|
);
|
|
259
259
|
|
|
260
|
+
/// @dev Emitted only on terminal execution (COMPLETED/FAILED). Full execution returndata (`result` may be empty).
|
|
261
|
+
/// Verify against `TxRecord.resultHash` from the same tx: `result.length == 0` implies `resultHash == bytes32(0)`;
|
|
262
|
+
/// otherwise `keccak256(result) == resultHash` (see `_executionResultHash`).
|
|
263
|
+
event TxExecutionResult(uint256 indexed txId, bytes result);
|
|
264
|
+
|
|
260
265
|
// ============ SYSTEM STATE FUNCTIONS ============
|
|
261
266
|
|
|
262
267
|
/**
|
|
@@ -473,14 +478,15 @@ library EngineBlox {
|
|
|
473
478
|
uint256 txId,
|
|
474
479
|
bytes4 handlerSelector
|
|
475
480
|
) public returns (TxRecord memory) {
|
|
476
|
-
// Validate both execution and handler selector permissions
|
|
481
|
+
// CHECK: Validate both execution and handler selector permissions
|
|
477
482
|
_validateExecutionAndHandlerPermissions(self, msg.sender, self.txRecords[txId].params.executionSelector, handlerSelector, TxAction.EXECUTE_TIME_DELAY_APPROVE);
|
|
478
483
|
_validateTxStatus(self, txId, TxStatus.PENDING);
|
|
484
|
+
_validateTargetWhitelist(self, self.txRecords[txId].params.executionSelector, self.txRecords[txId].params.target);
|
|
479
485
|
SharedValidation.validateReleaseTime(self.txRecords[txId].releaseTime);
|
|
480
|
-
|
|
486
|
+
|
|
481
487
|
// EFFECT: Update status to EXECUTING before external call to prevent reentrancy
|
|
482
488
|
self.txRecords[txId].status = TxStatus.EXECUTING;
|
|
483
|
-
|
|
489
|
+
|
|
484
490
|
// INTERACT: External call after state update
|
|
485
491
|
(bool success, bytes memory result) = executeTransaction(self, self.txRecords[txId]);
|
|
486
492
|
|
|
@@ -560,17 +566,19 @@ library EngineBlox {
|
|
|
560
566
|
* (direct path enforces releaseTime) and meta-tx workflows (delegated, time-flexible approval).
|
|
561
567
|
*/
|
|
562
568
|
function _txApprovalWithMetaTx(SecureOperationState storage self, MetaTransaction memory metaTx) private returns (TxRecord memory) {
|
|
569
|
+
// CHECK: Validate transaction parameters
|
|
563
570
|
uint256 txId = metaTx.txRecord.txId;
|
|
564
571
|
_validateTxStatus(self, txId, TxStatus.PENDING);
|
|
572
|
+
_validateTargetWhitelist(self, self.txRecords[txId].params.executionSelector, self.txRecords[txId].params.target);
|
|
565
573
|
_validateMetaTxMatchRecord(self, txId, metaTx.txRecord);
|
|
566
574
|
_validateMetaTxPaymentMatchRecord(self, txId, metaTx.txRecord);
|
|
567
575
|
if (!verifySignature(self, metaTx)) revert SharedValidation.InvalidSignature(metaTx.signature);
|
|
568
|
-
|
|
576
|
+
|
|
569
577
|
incrementSignerNonce(self, metaTx.params.signer);
|
|
570
|
-
|
|
578
|
+
|
|
571
579
|
// EFFECT: Update status to EXECUTING before external call to prevent reentrancy
|
|
572
580
|
self.txRecords[txId].status = TxStatus.EXECUTING;
|
|
573
|
-
|
|
581
|
+
|
|
574
582
|
// INTERACT: External call after state update
|
|
575
583
|
(bool success, bytes memory result) = executeTransaction(self, self.txRecords[txId]);
|
|
576
584
|
|
|
@@ -612,46 +620,6 @@ library EngineBlox {
|
|
|
612
620
|
return _txApprovalWithMetaTx(self, metaTx);
|
|
613
621
|
}
|
|
614
622
|
|
|
615
|
-
/**
|
|
616
|
-
* @dev Performs `address.call` without copying unbounded returndata into memory.
|
|
617
|
-
* @param target Callee address
|
|
618
|
-
* @param value Native value to forward
|
|
619
|
-
* @param callGas Gas to forward to the callee
|
|
620
|
-
* @param data Full calldata for the call
|
|
621
|
-
* @return success Whether the call returned success
|
|
622
|
-
* @return result First `min(returndatasize(), MAX_RESULT_PREVIEW_BYTES)` bytes of returndata
|
|
623
|
-
*/
|
|
624
|
-
function _callWithBoundedReturndata(
|
|
625
|
-
address target,
|
|
626
|
-
uint256 value,
|
|
627
|
-
uint256 callGas,
|
|
628
|
-
bytes memory data
|
|
629
|
-
) private returns (bool success, bytes memory result) {
|
|
630
|
-
uint256 dataLength = data.length;
|
|
631
|
-
assembly ("memory-safe") {
|
|
632
|
-
success := call(
|
|
633
|
-
callGas,
|
|
634
|
-
target,
|
|
635
|
-
value,
|
|
636
|
-
add(data, 0x20),
|
|
637
|
-
dataLength,
|
|
638
|
-
0,
|
|
639
|
-
0
|
|
640
|
-
)
|
|
641
|
-
}
|
|
642
|
-
uint256 returnSize;
|
|
643
|
-
assembly ("memory-safe") {
|
|
644
|
-
returnSize := returndatasize()
|
|
645
|
-
}
|
|
646
|
-
uint256 copyLength = returnSize > MAX_RESULT_PREVIEW_BYTES ? MAX_RESULT_PREVIEW_BYTES : returnSize;
|
|
647
|
-
result = new bytes(copyLength);
|
|
648
|
-
assembly ("memory-safe") {
|
|
649
|
-
if gt(copyLength, 0) {
|
|
650
|
-
returndatacopy(add(result, 0x20), 0, copyLength)
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
623
|
/**
|
|
656
624
|
* @dev Executes a transaction based on its execution type and attached payment.
|
|
657
625
|
* @param self The SecureOperationState storage reference (for validation)
|
|
@@ -666,13 +634,12 @@ library EngineBlox {
|
|
|
666
634
|
* causing _validateTxPending to revert in entry functions
|
|
667
635
|
* 4. Status flow is one-way: PENDING → EXECUTING → (COMPLETED/FAILED)
|
|
668
636
|
* This creates an effective reentrancy guard without additional storage overhead.
|
|
669
|
-
* @notice Returndata from the target is captured up to `MAX_RESULT_PREVIEW_BYTES` only (low-level `call` with
|
|
670
|
-
* zero output area, then bounded `returndatacopy`). This prevents unbounded memory expansion from
|
|
671
|
-
* malicious or buggy callees while preserving a large preview for debugging and audits.
|
|
672
637
|
* @notice **Atomicity:** If the main call succeeds but `executeAttachedPayment` reverts (e.g.
|
|
673
638
|
* insufficient balance, whitelist mismatch), the **entire** approval/execute transaction reverts—main
|
|
674
639
|
* effect included. This is **intentional all-or-nothing** semantics; splitting finalize vs payment
|
|
675
640
|
* would require a separate design with reentrancy and state-machine implications.
|
|
641
|
+
* @notice `record` is a memory copy: final `status` and `resultHash` are written to storage by `_completeTransaction`;
|
|
642
|
+
* full returndata is emitted in `TxExecutionResult`.
|
|
676
643
|
*/
|
|
677
644
|
function executeTransaction(SecureOperationState storage self, TxRecord memory record) private returns (bool, bytes memory) {
|
|
678
645
|
// Validate that transaction is in EXECUTING status (set by caller before this function)
|
|
@@ -690,24 +657,15 @@ library EngineBlox {
|
|
|
690
657
|
// Execute the main transaction
|
|
691
658
|
// REENTRANCY SAFE: Status is EXECUTING, preventing reentry through entry functions
|
|
692
659
|
// that require PENDING status. Any reentry attempt would fail at _validateTxStatus(..., PENDING).
|
|
693
|
-
(bool success, bytes memory result) =
|
|
694
|
-
record.params.target,
|
|
695
|
-
record.params.value,
|
|
696
|
-
gas,
|
|
660
|
+
(bool success, bytes memory result) = record.params.target.call{value: record.params.value, gas: gas}(
|
|
697
661
|
txData
|
|
698
662
|
);
|
|
699
663
|
|
|
700
664
|
if (success) {
|
|
701
|
-
record.status = TxStatus.COMPLETED;
|
|
702
|
-
record.result = result;
|
|
703
|
-
|
|
704
665
|
// Execute attached payment if transaction was successful
|
|
705
666
|
if (record.payment.recipient != address(0)) {
|
|
706
667
|
executeAttachedPayment(self, record);
|
|
707
668
|
}
|
|
708
|
-
} else {
|
|
709
|
-
record.status = TxStatus.FAILED;
|
|
710
|
-
record.result = result;
|
|
711
669
|
}
|
|
712
670
|
|
|
713
671
|
return (success, result);
|
|
@@ -839,7 +797,7 @@ library EngineBlox {
|
|
|
839
797
|
executionParams: executionParams
|
|
840
798
|
}),
|
|
841
799
|
message: 0,
|
|
842
|
-
|
|
800
|
+
resultHash: bytes32(0),
|
|
843
801
|
payment: payment
|
|
844
802
|
});
|
|
845
803
|
}
|
|
@@ -850,8 +808,7 @@ library EngineBlox {
|
|
|
850
808
|
* @param txId The transaction ID to add to the pending set.
|
|
851
809
|
*/
|
|
852
810
|
function addPendingTx(SecureOperationState storage self, uint256 txId) private {
|
|
853
|
-
SharedValidation.validateTransactionExists(txId);
|
|
854
|
-
_validateTxStatus(self, txId, TxStatus.PENDING);
|
|
811
|
+
SharedValidation.validateTransactionExists(txId, self.txCounter);
|
|
855
812
|
|
|
856
813
|
// Try to add transaction ID to the set - add() returns false if already exists
|
|
857
814
|
if (!self.pendingTransactionsSet.add(txId)) {
|
|
@@ -865,7 +822,7 @@ library EngineBlox {
|
|
|
865
822
|
* @param txId The transaction ID to remove from the pending set.
|
|
866
823
|
*/
|
|
867
824
|
function removePendingTx(SecureOperationState storage self, uint256 txId) private {
|
|
868
|
-
SharedValidation.validateTransactionExists(txId);
|
|
825
|
+
SharedValidation.validateTransactionExists(txId, self.txCounter);
|
|
869
826
|
|
|
870
827
|
// Remove the transaction ID from the set (O(1) operation)
|
|
871
828
|
if (!self.pendingTransactionsSet.remove(txId)) {
|
|
@@ -1108,8 +1065,8 @@ library EngineBlox {
|
|
|
1108
1065
|
* @param functionPermission The function permission to add.
|
|
1109
1066
|
* @notice Reverts **`ResourceAlreadyExists`** if the selector is already present on the role. To update
|
|
1110
1067
|
* bitmap or `handlerForSelectors`, **`removeFunctionFromRole`** first then re-add.
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1068
|
+
* **`removeFunctionFromRole`** succeeds only when the schema's **`isGrantRevocable`** is true (see there);
|
|
1069
|
+
* **`isProtected`** on the schema does not, by itself, block removing a grant from a role.
|
|
1113
1070
|
*/
|
|
1114
1071
|
function addFunctionToRole(
|
|
1115
1072
|
SecureOperationState storage self,
|
|
@@ -1142,10 +1099,10 @@ library EngineBlox {
|
|
|
1142
1099
|
* @param self The SecureOperationState to modify.
|
|
1143
1100
|
* @param roleHash The role hash to remove the function permission from.
|
|
1144
1101
|
* @param functionSelector The function selector to remove from the role.
|
|
1145
|
-
* @notice
|
|
1146
|
-
*
|
|
1147
|
-
* role
|
|
1148
|
-
*
|
|
1102
|
+
* @notice When the selector is registered, reverts **`GrantNotRevocable`** if **`isGrantRevocable == false`**
|
|
1103
|
+
* (no role may drop this grant). When **`isGrantRevocable == true`**, the grant may be removed from
|
|
1104
|
+
* any role, including protected system roles; **`isProtected`** on the schema still blocks
|
|
1105
|
+
* **`unregisterFunction`** independently, and **`removeRole`** still blocks protected roles.
|
|
1149
1106
|
*/
|
|
1150
1107
|
function removeFunctionFromRole(
|
|
1151
1108
|
SecureOperationState storage self,
|
|
@@ -1155,12 +1112,10 @@ library EngineBlox {
|
|
|
1155
1112
|
// Check if role exists (checks both roles mapping and supportedRolesSet)
|
|
1156
1113
|
_validateRoleExists(self, roleHash);
|
|
1157
1114
|
|
|
1158
|
-
// Security check: Prevent removing protected functions from roles
|
|
1159
|
-
// Check if function exists and is protected
|
|
1160
1115
|
if (self.supportedFunctionsSet.contains(bytes32(functionSelector))) {
|
|
1161
1116
|
FunctionSchema memory functionSchema = self.functions[functionSelector];
|
|
1162
|
-
if (functionSchema.
|
|
1163
|
-
revert SharedValidation.
|
|
1117
|
+
if (!functionSchema.isGrantRevocable) {
|
|
1118
|
+
revert SharedValidation.GrantNotRevocable(functionSelector);
|
|
1164
1119
|
}
|
|
1165
1120
|
}
|
|
1166
1121
|
|
|
@@ -1259,7 +1214,8 @@ library EngineBlox {
|
|
|
1259
1214
|
* @param operationName The name of the operation type.
|
|
1260
1215
|
* @param supportedActionsBitmap Bitmap of permissions required to execute this function.
|
|
1261
1216
|
* @param enforceHandlerRelations When true (strict mode), handlerForSelectors in role permissions must match this schema's handlerForSelectors at use time. When false (flexible mode), forward references are allowed.
|
|
1262
|
-
* @param isProtected Whether the function schema is protected from
|
|
1217
|
+
* @param isProtected Whether the function schema is protected from **unregister** (`unregisterFunction`).
|
|
1218
|
+
* @param isGrantRevocable When false, `removeFunctionFromRole` cannot remove this selector from any role; when true, grants may be removed from any role, including protected system roles.
|
|
1263
1219
|
* @param handlerForSelectors Non-empty array required - execution selectors must contain self-reference, handler selectors must point to execution selectors.
|
|
1264
1220
|
* @custom:security OPERATIONAL MODES: We do not require handlerForSelectors[i] to be in supportedFunctionsSet at registration.
|
|
1265
1221
|
* - Strict mode (enforceHandlerRelations == true): at use time (_validateHandlerForSelectors) we require role permissions' handlerForSelectors to match this schema's handlerForSelectors; registration order is flexible.
|
|
@@ -1273,6 +1229,7 @@ library EngineBlox {
|
|
|
1273
1229
|
uint16 supportedActionsBitmap,
|
|
1274
1230
|
bool enforceHandlerRelations,
|
|
1275
1231
|
bool isProtected,
|
|
1232
|
+
bool isGrantRevocable,
|
|
1276
1233
|
bytes4[] memory handlerForSelectors
|
|
1277
1234
|
) public {
|
|
1278
1235
|
// Validate that functionSignature matches functionSelector
|
|
@@ -1323,6 +1280,7 @@ library EngineBlox {
|
|
|
1323
1280
|
schema.supportedActionsBitmap = supportedActionsBitmap;
|
|
1324
1281
|
schema.enforceHandlerRelations = enforceHandlerRelations;
|
|
1325
1282
|
schema.isProtected = isProtected;
|
|
1283
|
+
schema.isGrantRevocable = isGrantRevocable;
|
|
1326
1284
|
schema.handlerForSelectors = handlerForSelectors;
|
|
1327
1285
|
|
|
1328
1286
|
// Add to supportedFunctionsSet
|
|
@@ -2058,7 +2016,7 @@ library EngineBlox {
|
|
|
2058
2016
|
SharedValidation.validateChainId(metaTxParams.chainId);
|
|
2059
2017
|
SharedValidation.validateMetaTxHandlerContractBinding(metaTxParams.handlerContract);
|
|
2060
2018
|
SharedValidation.validateHandlerSelector(metaTxParams.handlerSelector);
|
|
2061
|
-
SharedValidation.
|
|
2019
|
+
SharedValidation.validateMetaTxDeadline(metaTxParams.deadline);
|
|
2062
2020
|
SharedValidation.validateNotZeroAddress(metaTxParams.signer);
|
|
2063
2021
|
|
|
2064
2022
|
// Populate the nonce directly from storage for security
|
|
@@ -2128,6 +2086,8 @@ library EngineBlox {
|
|
|
2128
2086
|
* @notice **Gas tradeoff:** There is no explicit `{gas: ...}` stipend; the subcall receives the usual EIP-150
|
|
2129
2087
|
* bounded share of remaining gas (not the entire tx). Primary state updates in callers run **before**
|
|
2130
2088
|
* `logTxEvent` where applicable. Optional hardening: configurable stipend + explicit failure event.
|
|
2089
|
+
* @notice **Execution returndata:** full bytes are emitted only via `TxExecutionResult` from `_completeTransaction`;
|
|
2090
|
+
* this function emits lifecycle `TransactionEvent` with `resultHash` (zero on request/cancel).
|
|
2131
2091
|
* @custom:security REENTRANCY PROTECTION: This function is safe from reentrancy because:
|
|
2132
2092
|
* 1. It is called AFTER all state changes are complete (in _completeTransaction,
|
|
2133
2093
|
* _cancelTransaction, and txRequest)
|
|
@@ -2145,21 +2105,17 @@ library EngineBlox {
|
|
|
2145
2105
|
bytes4 functionSelector
|
|
2146
2106
|
) public {
|
|
2147
2107
|
TxRecord memory txRecord = self.txRecords[txId];
|
|
2148
|
-
|
|
2149
|
-
// Emit only non-sensitive public data
|
|
2108
|
+
|
|
2150
2109
|
emit TransactionEvent(
|
|
2151
2110
|
txId,
|
|
2152
2111
|
functionSelector,
|
|
2153
2112
|
txRecord.status,
|
|
2154
2113
|
txRecord.params.requester,
|
|
2155
2114
|
txRecord.params.target,
|
|
2156
|
-
txRecord.params.operationType
|
|
2115
|
+
txRecord.params.operationType,
|
|
2116
|
+
txRecord.resultHash
|
|
2157
2117
|
);
|
|
2158
|
-
|
|
2159
|
-
// Forward event data to event forwarder
|
|
2160
|
-
// REENTRANCY SAFE: External call is wrapped in try-catch and doesn't modify
|
|
2161
|
-
// critical state. Even if eventForwarder is malicious, reentry attempts fail
|
|
2162
|
-
// because transactions are no longer in PENDING status (they're COMPLETED/CANCELLED).
|
|
2118
|
+
|
|
2163
2119
|
if (self.eventForwarder != address(0)) {
|
|
2164
2120
|
try IEventForwarder(self.eventForwarder).forwardTxEvent(
|
|
2165
2121
|
txId,
|
|
@@ -2167,12 +2123,9 @@ library EngineBlox {
|
|
|
2167
2123
|
txRecord.status,
|
|
2168
2124
|
txRecord.params.requester,
|
|
2169
2125
|
txRecord.params.target,
|
|
2170
|
-
txRecord.params.operationType
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
} catch {
|
|
2174
|
-
// Forwarding failed, continue execution (non-critical operation)
|
|
2175
|
-
}
|
|
2126
|
+
txRecord.params.operationType,
|
|
2127
|
+
txRecord.resultHash
|
|
2128
|
+
) {} catch {}
|
|
2176
2129
|
}
|
|
2177
2130
|
}
|
|
2178
2131
|
|
|
@@ -2258,33 +2211,26 @@ library EngineBlox {
|
|
|
2258
2211
|
* @param self The SecureOperationState to modify
|
|
2259
2212
|
* @param txId The transaction ID to complete
|
|
2260
2213
|
* @param success Whether the transaction execution was successful
|
|
2261
|
-
* @param
|
|
2214
|
+
* @param executionResult Returndata from the main target call (emitted in `TxExecutionResult`).
|
|
2262
2215
|
*/
|
|
2263
2216
|
function _completeTransaction(
|
|
2264
2217
|
SecureOperationState storage self,
|
|
2265
2218
|
uint256 txId,
|
|
2266
2219
|
bool success,
|
|
2267
|
-
bytes memory
|
|
2220
|
+
bytes memory executionResult
|
|
2268
2221
|
) private {
|
|
2269
|
-
|
|
2270
|
-
_validateTargetWhitelist(self, self.txRecords[txId].params.executionSelector, self.txRecords[txId].params.target);
|
|
2271
|
-
|
|
2272
|
-
// Update storage with new status and result
|
|
2222
|
+
bytes32 resultHash = _executionResultHash(executionResult);
|
|
2273
2223
|
if (success) {
|
|
2274
2224
|
self.txRecords[txId].status = TxStatus.COMPLETED;
|
|
2275
|
-
self.txRecords[txId].result = result;
|
|
2276
2225
|
} else {
|
|
2277
2226
|
self.txRecords[txId].status = TxStatus.FAILED;
|
|
2278
|
-
self.txRecords[txId].result = result; // Store failure reason for debugging
|
|
2279
|
-
// Note: FAILED status is intentional - transactions can be valid when requested
|
|
2280
|
-
// but fail when executed (e.g., conditions changed, insufficient balance, etc.)
|
|
2281
|
-
// Users can query status via getTransaction() or listen to TransactionEvent
|
|
2282
2227
|
}
|
|
2283
|
-
|
|
2284
|
-
|
|
2228
|
+
self.txRecords[txId].resultHash = resultHash;
|
|
2229
|
+
|
|
2285
2230
|
removePendingTx(self, txId);
|
|
2286
|
-
|
|
2231
|
+
|
|
2287
2232
|
logTxEvent(self, txId, self.txRecords[txId].params.executionSelector);
|
|
2233
|
+
emit TxExecutionResult(txId, executionResult);
|
|
2288
2234
|
}
|
|
2289
2235
|
|
|
2290
2236
|
/**
|
|
@@ -2296,9 +2242,6 @@ library EngineBlox {
|
|
|
2296
2242
|
SecureOperationState storage self,
|
|
2297
2243
|
uint256 txId
|
|
2298
2244
|
) private {
|
|
2299
|
-
// enforce that the requested target is whitelisted for this selector.
|
|
2300
|
-
_validateTargetWhitelist(self, self.txRecords[txId].params.executionSelector, self.txRecords[txId].params.target);
|
|
2301
|
-
|
|
2302
2245
|
self.txRecords[txId].status = TxStatus.CANCELLED;
|
|
2303
2246
|
|
|
2304
2247
|
// Remove from pending transactions list
|
|
@@ -2307,7 +2250,16 @@ library EngineBlox {
|
|
|
2307
2250
|
logTxEvent(self, txId, self.txRecords[txId].params.executionSelector);
|
|
2308
2251
|
}
|
|
2309
2252
|
|
|
2310
|
-
|
|
2253
|
+
/**
|
|
2254
|
+
* @dev Commitment to execution returndata for storage and `TransactionEvent.resultHash`.
|
|
2255
|
+
* @param executionResult The execution returndata to hash
|
|
2256
|
+
* @return `bytes32(0)` when empty, else `keccak256(executionResult)`
|
|
2257
|
+
*/
|
|
2258
|
+
function _executionResultHash(bytes memory executionResult) private pure returns (bytes32) {
|
|
2259
|
+
return executionResult.length == 0 ? bytes32(0) : keccak256(executionResult);
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
/**
|
|
2311
2263
|
* @dev Validates that the caller has any role permission
|
|
2312
2264
|
* @param self The SecureOperationState to check
|
|
2313
2265
|
* @notice This function consolidates the repeated permission check pattern to reduce contract size
|
|
@@ -2390,7 +2342,8 @@ library EngineBlox {
|
|
|
2390
2342
|
sp.gasLimit != mp.gasLimit ||
|
|
2391
2343
|
sp.operationType != mp.operationType ||
|
|
2392
2344
|
keccak256(sp.executionParams) != keccak256(mp.executionParams) ||
|
|
2393
|
-
stored.releaseTime != metaTxRecord.releaseTime
|
|
2345
|
+
stored.releaseTime != metaTxRecord.releaseTime ||
|
|
2346
|
+
metaTxRecord.resultHash != bytes32(0)
|
|
2394
2347
|
) {
|
|
2395
2348
|
revert SharedValidation.MetaTxRecordMismatchStoredTx(txId);
|
|
2396
2349
|
}
|
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
-
pragma solidity 0.8.
|
|
3
|
-
|
|
4
|
-
// Import TxRecord struct from EngineBlox
|
|
5
|
-
import "../EngineBlox.sol";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @title IEventForwarder
|
|
9
|
-
* @dev Interface for the event forwarder contract
|
|
10
|
-
*
|
|
11
|
-
* This interface defines the contract for forwarding events from deployed instances
|
|
12
|
-
* to a centralized event monitoring system. It uses function selectors for efficient
|
|
13
|
-
* event identification and categorization.
|
|
14
|
-
*/
|
|
15
|
-
interface IEventForwarder {
|
|
16
|
-
/**
|
|
17
|
-
* @dev Forward a transaction event from a deployed instance
|
|
18
|
-
* @param txId The transaction ID
|
|
19
|
-
* @param functionSelector The function selector for the event (bytes4)
|
|
20
|
-
* @param status The transaction status
|
|
21
|
-
* @param requester The address of the requester
|
|
22
|
-
* @param target The target contract address
|
|
23
|
-
* @param operationType The type of operation
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
address
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
pragma solidity 0.8.35;
|
|
3
|
+
|
|
4
|
+
// Import TxRecord struct from EngineBlox
|
|
5
|
+
import "../EngineBlox.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @title IEventForwarder
|
|
9
|
+
* @dev Interface for the event forwarder contract
|
|
10
|
+
*
|
|
11
|
+
* This interface defines the contract for forwarding events from deployed instances
|
|
12
|
+
* to a centralized event monitoring system. It uses function selectors for efficient
|
|
13
|
+
* event identification and categorization.
|
|
14
|
+
*/
|
|
15
|
+
interface IEventForwarder {
|
|
16
|
+
/**
|
|
17
|
+
* @dev Forward a transaction event from a deployed instance
|
|
18
|
+
* @param txId The transaction ID
|
|
19
|
+
* @param functionSelector The function selector for the event (bytes4)
|
|
20
|
+
* @param status The transaction status
|
|
21
|
+
* @param requester The address of the requester
|
|
22
|
+
* @param target The target contract address
|
|
23
|
+
* @param operationType The type of operation
|
|
24
|
+
* @param resultHash Commitment to execution returndata (`bytes32(0)` when none). Full bytes: `TxExecutionResult` log.
|
|
25
|
+
*/
|
|
26
|
+
function forwardTxEvent(
|
|
27
|
+
uint256 txId,
|
|
28
|
+
bytes4 functionSelector,
|
|
29
|
+
EngineBlox.TxStatus status,
|
|
30
|
+
address requester,
|
|
31
|
+
address target,
|
|
32
|
+
bytes32 operationType,
|
|
33
|
+
bytes32 resultHash
|
|
34
|
+
) external;
|
|
35
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.35;
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @title SharedValidation
|
|
@@ -38,7 +38,6 @@ library SharedValidation {
|
|
|
38
38
|
// Time and deadline errors with context
|
|
39
39
|
error InvalidTimeLockPeriod(uint256 provided);
|
|
40
40
|
error TimeLockPeriodZero(uint256 provided);
|
|
41
|
-
error DeadlineInPast(uint256 deadline, uint256 currentTime);
|
|
42
41
|
error MetaTxExpired(uint256 deadline, uint256 currentTime);
|
|
43
42
|
error BeforeReleaseTime(uint256 releaseTime, uint256 currentTime);
|
|
44
43
|
error NewTimelockSame(uint256 newPeriod, uint256 currentPeriod);
|
|
@@ -82,6 +81,7 @@ library SharedValidation {
|
|
|
82
81
|
error ResourceNotFound(bytes32 resourceId);
|
|
83
82
|
error ResourceAlreadyExists(bytes32 resourceId);
|
|
84
83
|
error CannotModifyProtected(bytes32 resourceId);
|
|
84
|
+
error GrantNotRevocable(bytes4 functionSelector);
|
|
85
85
|
|
|
86
86
|
// Consolidated item errors (for addresses: wallets, policies, etc.)
|
|
87
87
|
error ItemAlreadyExists(address item);
|
|
@@ -222,14 +222,6 @@ library SharedValidation {
|
|
|
222
222
|
if (timeLockPeriod == 0) revert TimeLockPeriodZero(timeLockPeriod);
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
/**
|
|
226
|
-
* @dev Validates that a deadline is in the future
|
|
227
|
-
* @param deadline The deadline timestamp to validate
|
|
228
|
-
*/
|
|
229
|
-
function validateDeadline(uint256 deadline) internal view {
|
|
230
|
-
if (deadline <= block.timestamp) revert DeadlineInPast(deadline, block.timestamp);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
225
|
/**
|
|
234
226
|
* @dev Validates that a new time lock period is different from the current one
|
|
235
227
|
* @param newPeriod The new time lock period
|
|
@@ -249,8 +241,9 @@ library SharedValidation {
|
|
|
249
241
|
}
|
|
250
242
|
|
|
251
243
|
/**
|
|
252
|
-
* @dev Validates that a meta-transaction has not
|
|
253
|
-
*
|
|
244
|
+
* @dev Validates that a meta-transaction deadline has not passed (inclusive boundary: `block.timestamp == deadline` is valid).
|
|
245
|
+
* Used both when building unsigned meta-tx payloads (`EngineBlox.generateMetaTransaction`) and when verifying submitted meta-txs.
|
|
246
|
+
* @param deadline The deadline timestamp from `MetaTxParams`
|
|
254
247
|
*/
|
|
255
248
|
function validateMetaTxDeadline(uint256 deadline) internal view {
|
|
256
249
|
if (block.timestamp > deadline) revert MetaTxExpired(deadline, block.timestamp);
|
|
@@ -360,11 +353,12 @@ library SharedValidation {
|
|
|
360
353
|
}
|
|
361
354
|
|
|
362
355
|
/**
|
|
363
|
-
* @dev Validates that a
|
|
356
|
+
* @dev Validates that `txId` refers to a minted record: non-zero and at most `txCounter` (inclusive).
|
|
364
357
|
* @param txId The transaction ID to validate
|
|
358
|
+
* @param txCounter The engine's `txCounter` after the record was allocated (valid ids are `1..txCounter`)
|
|
365
359
|
*/
|
|
366
|
-
function validateTransactionExists(uint256 txId) internal pure {
|
|
367
|
-
if (txId == 0) revert ResourceNotFound(bytes32(uint256(txId)));
|
|
360
|
+
function validateTransactionExists(uint256 txId, uint256 txCounter) internal pure {
|
|
361
|
+
if (txId == 0 || txId > txCounter) revert ResourceNotFound(bytes32(uint256(txId)));
|
|
368
362
|
}
|
|
369
363
|
|
|
370
364
|
/**
|
package/core/pattern/Account.sol
CHANGED