@bloxchain/contracts 1.0.0-alpha.2 → 1.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/abi/BaseStateMachine.abi.json +798 -753
- package/abi/EngineBlox.abi.json +566 -576
- package/abi/GuardController.abi.json +1546 -2095
- package/abi/GuardControllerDefinitions.abi.json +416 -0
- package/abi/IDefinition.abi.json +57 -47
- package/abi/RuntimeRBAC.abi.json +901 -959
- package/abi/RuntimeRBACDefinitions.abi.json +265 -81
- package/abi/SecureOwnable.abi.json +1522 -2581
- package/abi/SecureOwnableDefinitions.abi.json +174 -164
- package/components/README.md +8 -0
- package/core/access/RuntimeRBAC.sol +253 -270
- package/core/access/interface/IRuntimeRBAC.sol +55 -84
- package/core/access/lib/definitions/RuntimeRBACDefinitions.sol +97 -4
- package/core/base/BaseStateMachine.sol +198 -108
- package/core/base/interface/IBaseStateMachine.sol +153 -153
- package/core/execution/GuardController.sol +156 -131
- package/core/execution/interface/IGuardController.sol +146 -120
- package/core/execution/lib/definitions/GuardControllerDefinitions.sol +207 -45
- package/core/lib/EngineBlox.sol +2636 -2322
- package/{interfaces → core/lib/interfaces}/IDefinition.sol +49 -49
- package/{interfaces → core/lib/interfaces}/IEventForwarder.sol +5 -3
- package/{utils → core/lib/utils}/SharedValidation.sol +69 -22
- package/core/pattern/Account.sol +84 -0
- package/core/security/SecureOwnable.sol +180 -146
- package/core/security/interface/ISecureOwnable.sol +105 -104
- package/core/security/lib/definitions/SecureOwnableDefinitions.sol +818 -786
- package/package.json +5 -5
- package/standards/README.md +12 -0
- package/standards/behavior/ICopyable.sol +34 -0
- package/standards/hooks/IOnActionHook.sol +21 -0
- package/abi/AccountBlox.abi.json +0 -5799
- package/abi/BareBlox.abi.json +0 -1284
- package/abi/RoleBlox.abi.json +0 -4209
- package/abi/SecureBlox.abi.json +0 -3828
- package/abi/SimpleRWA20.abi.json +0 -5288
- package/abi/SimpleRWA20Definitions.abi.json +0 -191
- package/abi/SimpleVault.abi.json +0 -4951
- package/abi/SimpleVaultDefinitions.abi.json +0 -269
- package/core/research/BloxchainWallet.sol +0 -306
- package/core/research/erc20-blox/ERC20Blox.sol +0 -140
- package/core/research/erc20-blox/lib/definitions/ERC20BloxDefinitions.sol +0 -185
- package/interfaces/IOnActionHook.sol +0 -79
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.35;
|
|
3
3
|
|
|
4
4
|
// Contracts imports
|
|
5
5
|
import "../base/BaseStateMachine.sol";
|
|
6
6
|
import "./lib/definitions/SecureOwnableDefinitions.sol";
|
|
7
|
-
import "
|
|
8
|
-
import "
|
|
7
|
+
import "../lib/interfaces/IDefinition.sol";
|
|
8
|
+
import "../lib/utils/SharedValidation.sol";
|
|
9
9
|
import "./interface/ISecureOwnable.sol";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -27,8 +27,28 @@ import "./interface/ISecureOwnable.sol";
|
|
|
27
27
|
* Each operation follows a request -> approval workflow with appropriate time locks
|
|
28
28
|
* and authorization checks. Operations can be cancelled within specific time windows.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
30
|
+
* Pending secure requests use separate flags for ownership transfer and broadcaster update.
|
|
31
|
+
* A new ownership-transfer request is allowed if no ownership transfer is already pending
|
|
32
|
+
* (a broadcaster update may still be pending). A new broadcaster-update request is allowed only
|
|
33
|
+
* when neither type has a pending request.
|
|
34
|
+
*
|
|
35
|
+
* **Ownership transfer vs recovery (threat model):**
|
|
36
|
+
* - `transferOwnershipRequest` snapshots `getRecovery()` into the pending tx `executionParams`. On execution,
|
|
37
|
+
* `executeTransferOwnership` receives that snapshotted address as the new owner. Rotating recovery after
|
|
38
|
+
* the request does **not** rewrite the pending payload; the beneficiary remains the recovery address
|
|
39
|
+
* at request time.
|
|
40
|
+
* - `transferOwnershipDelayedApproval` authorizes the **current** owner or **current** recovery (`getRecovery()`
|
|
41
|
+
* at approval time). It does **not** require the approver to match the snapshotted beneficiary. Integrators
|
|
42
|
+
* must treat approval as consent to execute the **stored** transfer, not “transfer to whoever is recovery now.”
|
|
43
|
+
* - `transferOwnershipCancellation` allows only the **current** recovery to cancel. If owner and broadcaster
|
|
44
|
+
* rotate recovery via `updateRecoveryRequestAndApprove` while a transfer is pending, the **previous**
|
|
45
|
+
* recovery loses cancel rights immediately; the pending tx still targets the old address until approved,
|
|
46
|
+
* cancelled by the new recovery, or superseded operationally.
|
|
47
|
+
* - Recovery and timelock updates use a request-and-approve meta-tx path without an additional timelock and
|
|
48
|
+
* are **not** blocked when an ownership transfer is pending (unlike broadcaster update requests). This is
|
|
49
|
+
* intentional: fast recovery rotation when owner and broadcaster still cooperate; operators who need a
|
|
50
|
+
* strict “recovery cannot change during pending ownership transfer” invariant must enforce it off-chain or
|
|
51
|
+
* extend this contract.
|
|
32
52
|
*
|
|
33
53
|
* This contract focuses purely on security logic while leveraging the BaseStateMachine
|
|
34
54
|
* for transaction management, meta-transactions, and state machine operations.
|
|
@@ -36,8 +56,14 @@ import "./interface/ISecureOwnable.sol";
|
|
|
36
56
|
abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
37
57
|
using SharedValidation for *;
|
|
38
58
|
|
|
39
|
-
/// @dev
|
|
40
|
-
|
|
59
|
+
/// @dev Lane flags for **delayed** ownership-transfer and broadcaster-update requests only (`transferOwnershipRequest`,
|
|
60
|
+
/// `updateBroadcasterRequest`). Recovery and timelock updates use `_requestAndApproveTransaction` and do **not**
|
|
61
|
+
/// read or write these booleans. Each flag is set only after a successful `_requestTransaction` in that same tx;
|
|
62
|
+
/// clearing happens only in `_completeApprove` / `_completeCancel` in the **same** transaction as a successful
|
|
63
|
+
/// `_approveTransaction` / `_cancelTransaction`, so a revert unwinds engine state and flag writes together.
|
|
64
|
+
/// @dev Upgrading from legacy `_hasOpenRequest` / `_pendingBits` requires no pending requests.
|
|
65
|
+
bool private _hasOpenOwnershipRequest;
|
|
66
|
+
bool private _hasOpenBroadcasterRequest;
|
|
41
67
|
|
|
42
68
|
/**
|
|
43
69
|
* @notice Initializer to initialize SecureOwnable state
|
|
@@ -54,18 +80,15 @@ abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
|
54
80
|
uint256 timeLockPeriodSec,
|
|
55
81
|
address eventForwarder
|
|
56
82
|
) public virtual onlyInitializing {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
_initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
|
|
60
|
-
}
|
|
61
|
-
|
|
83
|
+
_initializeBaseStateMachine(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
|
|
84
|
+
|
|
62
85
|
// Load SecureOwnable-specific definitions
|
|
63
86
|
IDefinition.RolePermission memory secureOwnablePermissions = SecureOwnableDefinitions.getRolePermissions();
|
|
64
87
|
_loadDefinitions(
|
|
65
88
|
SecureOwnableDefinitions.getFunctionSchemas(),
|
|
66
89
|
secureOwnablePermissions.roleHashes,
|
|
67
90
|
secureOwnablePermissions.functionPermissions,
|
|
68
|
-
true //
|
|
91
|
+
true // Enforce all function schemas are protected
|
|
69
92
|
);
|
|
70
93
|
}
|
|
71
94
|
|
|
@@ -81,12 +104,14 @@ abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
|
81
104
|
|
|
82
105
|
// Ownership Management
|
|
83
106
|
/**
|
|
84
|
-
* @dev Requests a transfer of
|
|
85
|
-
* @
|
|
107
|
+
* @dev Requests a time-delayed transfer of the OWNER role to the **recovery address at request time**.
|
|
108
|
+
* @notice Encodes `getRecovery()` into `executionParams`; that address becomes the new owner on successful
|
|
109
|
+
* execution. Changing recovery later does not update this pending record.
|
|
110
|
+
* @return txId The transaction ID (use getTransaction(txId) for full record)
|
|
86
111
|
*/
|
|
87
|
-
function transferOwnershipRequest() public returns (
|
|
112
|
+
function transferOwnershipRequest() public returns (uint256 txId) {
|
|
88
113
|
SharedValidation.validateRecovery(getRecovery());
|
|
89
|
-
_requireNoPendingRequest();
|
|
114
|
+
_requireNoPendingRequest(SecureOwnableDefinitions.OWNERSHIP_TRANSFER);
|
|
90
115
|
|
|
91
116
|
EngineBlox.TxRecord memory txRecord = _requestTransaction(
|
|
92
117
|
msg.sender,
|
|
@@ -98,66 +123,70 @@ abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
|
98
123
|
abi.encode(getRecovery())
|
|
99
124
|
);
|
|
100
125
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return txRecord;
|
|
126
|
+
_hasOpenOwnershipRequest = true;
|
|
127
|
+
_logAddressPairEvent(owner(), getRecovery());
|
|
128
|
+
return txRecord.txId;
|
|
104
129
|
}
|
|
105
130
|
|
|
106
131
|
/**
|
|
107
|
-
* @dev Approves a pending ownership transfer
|
|
132
|
+
* @dev Approves a pending ownership transfer after `releaseTime` (timelock on the direct path).
|
|
133
|
+
* @notice Callable by **current** owner or **current** recovery. Execution still transfers ownership to
|
|
134
|
+
* the address snapshotted at request time, which may differ from `getRecovery()` at approval time.
|
|
108
135
|
* @param txId The transaction ID
|
|
109
|
-
* @return The
|
|
136
|
+
* @return The transaction ID
|
|
110
137
|
*/
|
|
111
|
-
function transferOwnershipDelayedApproval(uint256 txId) public returns (
|
|
138
|
+
function transferOwnershipDelayedApproval(uint256 txId) public returns (uint256) {
|
|
112
139
|
SharedValidation.validateOwnerOrRecovery(owner(), getRecovery());
|
|
113
|
-
|
|
114
|
-
return _completeOwnershipApprove(_approveTransaction(txId));
|
|
140
|
+
return _completeApprove(_approveTransaction(txId));
|
|
115
141
|
}
|
|
116
142
|
|
|
117
143
|
/**
|
|
118
144
|
* @dev Approves a pending ownership transfer transaction using a meta-transaction
|
|
119
145
|
* @param metaTx The meta-transaction
|
|
120
|
-
* @return The
|
|
146
|
+
* @return The transaction ID
|
|
121
147
|
*/
|
|
122
|
-
function transferOwnershipApprovalWithMetaTx(EngineBlox.MetaTransaction memory metaTx) public returns (
|
|
148
|
+
function transferOwnershipApprovalWithMetaTx(EngineBlox.MetaTransaction memory metaTx) public returns (uint256) {
|
|
123
149
|
_validateBroadcasterAndOwnerSigner(metaTx);
|
|
124
|
-
|
|
125
|
-
return _completeOwnershipApprove(_approveTransactionWithMetaTx(metaTx));
|
|
150
|
+
return _completeApprove(_approveTransactionWithMetaTx(metaTx));
|
|
126
151
|
}
|
|
127
152
|
|
|
128
153
|
/**
|
|
129
|
-
* @dev Cancels a pending ownership transfer transaction
|
|
154
|
+
* @dev Cancels a pending ownership transfer transaction.
|
|
155
|
+
* @notice Only the **current** `getRecovery()` may cancel. After a recovery rotation, the prior recovery
|
|
156
|
+
* address can no longer cancel.
|
|
130
157
|
* @param txId The transaction ID
|
|
131
|
-
* @return The
|
|
158
|
+
* @return The transaction ID
|
|
132
159
|
*/
|
|
133
|
-
function transferOwnershipCancellation(uint256 txId) public returns (
|
|
160
|
+
function transferOwnershipCancellation(uint256 txId) public returns (uint256) {
|
|
134
161
|
SharedValidation.validateRecovery(getRecovery());
|
|
135
|
-
return
|
|
162
|
+
return _completeCancel(_cancelTransaction(txId));
|
|
136
163
|
}
|
|
137
164
|
|
|
138
165
|
/**
|
|
139
166
|
* @dev Cancels a pending ownership transfer transaction using a meta-transaction
|
|
140
167
|
* @param metaTx The meta-transaction
|
|
141
|
-
* @return The
|
|
168
|
+
* @return The transaction ID
|
|
142
169
|
*/
|
|
143
|
-
function transferOwnershipCancellationWithMetaTx(EngineBlox.MetaTransaction memory metaTx) public returns (
|
|
170
|
+
function transferOwnershipCancellationWithMetaTx(EngineBlox.MetaTransaction memory metaTx) public returns (uint256) {
|
|
144
171
|
_validateBroadcasterAndOwnerSigner(metaTx);
|
|
145
|
-
|
|
146
|
-
return _completeOwnershipCancel(_cancelTransactionWithMetaTx(metaTx));
|
|
172
|
+
return _completeCancel(_cancelTransactionWithMetaTx(metaTx));
|
|
147
173
|
}
|
|
148
174
|
|
|
149
175
|
// Broadcaster Management
|
|
150
176
|
/**
|
|
151
|
-
* @dev
|
|
152
|
-
* @
|
|
153
|
-
* @
|
|
177
|
+
* @dev Requests a broadcaster role change identified by addresses.
|
|
178
|
+
* @notice Requires no pending broadcaster-update and no pending ownership-transfer request.
|
|
179
|
+
* @param newBroadcaster New broadcaster (`address(0)` to revoke `currentBroadcaster`)
|
|
180
|
+
* @param currentBroadcaster Existing broadcaster to replace or revoke; `address(0)` to add `newBroadcaster`
|
|
181
|
+
* @return txId The transaction ID for the pending request (use getTransaction(txId) for full record)
|
|
154
182
|
*/
|
|
155
|
-
function updateBroadcasterRequest(address newBroadcaster) public returns (
|
|
183
|
+
function updateBroadcasterRequest(address newBroadcaster, address currentBroadcaster) public returns (uint256 txId) {
|
|
156
184
|
SharedValidation.validateOwner(owner());
|
|
157
|
-
_requireNoPendingRequest();
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
185
|
+
_requireNoPendingRequest(SecureOwnableDefinitions.BROADCASTER_UPDATE);
|
|
186
|
+
_requireNoPendingRequest(SecureOwnableDefinitions.OWNERSHIP_TRANSFER);
|
|
187
|
+
|
|
188
|
+
_validateBroadcasterUpdatePair(newBroadcaster, currentBroadcaster);
|
|
189
|
+
|
|
161
190
|
EngineBlox.TxRecord memory txRecord = _requestTransaction(
|
|
162
191
|
msg.sender,
|
|
163
192
|
address(this),
|
|
@@ -165,69 +194,69 @@ abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
|
165
194
|
0, // gas limit
|
|
166
195
|
SecureOwnableDefinitions.BROADCASTER_UPDATE,
|
|
167
196
|
SecureOwnableDefinitions.UPDATE_BROADCASTER_SELECTOR,
|
|
168
|
-
abi.encode(newBroadcaster)
|
|
197
|
+
abi.encode(newBroadcaster, currentBroadcaster)
|
|
169
198
|
);
|
|
170
199
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return txRecord;
|
|
200
|
+
_hasOpenBroadcasterRequest = true;
|
|
201
|
+
_logAddressPairEvent(currentBroadcaster, newBroadcaster);
|
|
202
|
+
return txRecord.txId;
|
|
174
203
|
}
|
|
175
204
|
|
|
176
205
|
/**
|
|
177
206
|
* @dev Approves a pending broadcaster update transaction after the release time
|
|
178
207
|
* @param txId The transaction ID
|
|
179
|
-
* @return The
|
|
208
|
+
* @return The transaction ID
|
|
180
209
|
*/
|
|
181
|
-
function updateBroadcasterDelayedApproval(uint256 txId) public returns (
|
|
210
|
+
function updateBroadcasterDelayedApproval(uint256 txId) public returns (uint256) {
|
|
182
211
|
SharedValidation.validateOwner(owner());
|
|
183
|
-
return
|
|
212
|
+
return _completeApprove(_approveTransaction(txId));
|
|
184
213
|
}
|
|
185
214
|
|
|
186
215
|
/**
|
|
187
216
|
* @dev Approves a pending broadcaster update transaction using a meta-transaction
|
|
188
217
|
* @param metaTx The meta-transaction
|
|
189
|
-
* @return The
|
|
218
|
+
* @return The transaction ID
|
|
190
219
|
*/
|
|
191
|
-
function updateBroadcasterApprovalWithMetaTx(EngineBlox.MetaTransaction memory metaTx) public returns (
|
|
220
|
+
function updateBroadcasterApprovalWithMetaTx(EngineBlox.MetaTransaction memory metaTx) public returns (uint256) {
|
|
192
221
|
_validateBroadcasterAndOwnerSigner(metaTx);
|
|
193
|
-
|
|
194
|
-
return _completeBroadcasterApprove(_approveTransactionWithMetaTx(metaTx));
|
|
222
|
+
return _completeApprove(_approveTransactionWithMetaTx(metaTx));
|
|
195
223
|
}
|
|
196
224
|
|
|
197
225
|
/**
|
|
198
226
|
* @dev Cancels a pending broadcaster update transaction
|
|
199
227
|
* @param txId The transaction ID
|
|
200
|
-
* @return The
|
|
228
|
+
* @return The transaction ID
|
|
201
229
|
*/
|
|
202
|
-
function updateBroadcasterCancellation(uint256 txId) public returns (
|
|
230
|
+
function updateBroadcasterCancellation(uint256 txId) public returns (uint256) {
|
|
203
231
|
SharedValidation.validateOwner(owner());
|
|
204
|
-
return
|
|
232
|
+
return _completeCancel(_cancelTransaction(txId));
|
|
205
233
|
}
|
|
206
234
|
|
|
207
235
|
/**
|
|
208
236
|
* @dev Cancels a pending broadcaster update transaction using a meta-transaction
|
|
209
237
|
* @param metaTx The meta-transaction
|
|
210
|
-
* @return The
|
|
238
|
+
* @return The transaction ID
|
|
211
239
|
*/
|
|
212
|
-
function updateBroadcasterCancellationWithMetaTx(EngineBlox.MetaTransaction memory metaTx) public returns (
|
|
240
|
+
function updateBroadcasterCancellationWithMetaTx(EngineBlox.MetaTransaction memory metaTx) public returns (uint256) {
|
|
213
241
|
_validateBroadcasterAndOwnerSigner(metaTx);
|
|
214
|
-
|
|
215
|
-
return _completeBroadcasterCancel(_cancelTransactionWithMetaTx(metaTx));
|
|
242
|
+
return _completeCancel(_cancelTransactionWithMetaTx(metaTx));
|
|
216
243
|
}
|
|
217
244
|
|
|
218
245
|
// Recovery Management
|
|
219
246
|
|
|
220
247
|
/**
|
|
221
|
-
* @dev Requests and approves a recovery address update using a meta-transaction
|
|
248
|
+
* @dev Requests and approves a recovery address update using a meta-transaction (owner signs, broadcaster submits).
|
|
249
|
+
* @notice Does **not** revert when an ownership transfer is pending. A pending transfer continues to target
|
|
250
|
+
* the recovery address snapshotted at its request until executed or cancelled by **current** recovery.
|
|
222
251
|
* @param metaTx The meta-transaction
|
|
223
|
-
* @return The transaction
|
|
252
|
+
* @return The transaction ID
|
|
224
253
|
*/
|
|
225
254
|
function updateRecoveryRequestAndApprove(
|
|
226
255
|
EngineBlox.MetaTransaction memory metaTx
|
|
227
|
-
) public returns (
|
|
256
|
+
) public returns (uint256) {
|
|
228
257
|
_validateBroadcasterAndOwnerSigner(metaTx);
|
|
229
|
-
|
|
230
|
-
return
|
|
258
|
+
EngineBlox.TxRecord memory txRecord = _requestAndApproveTransaction(metaTx);
|
|
259
|
+
return txRecord.txId;
|
|
231
260
|
}
|
|
232
261
|
|
|
233
262
|
// TimeLock Management
|
|
@@ -235,20 +264,21 @@ abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
|
235
264
|
/**
|
|
236
265
|
* @dev Requests and approves a time lock period update using a meta-transaction
|
|
237
266
|
* @param metaTx The meta-transaction
|
|
238
|
-
* @return The transaction
|
|
267
|
+
* @return The transaction ID
|
|
239
268
|
*/
|
|
240
269
|
function updateTimeLockRequestAndApprove(
|
|
241
270
|
EngineBlox.MetaTransaction memory metaTx
|
|
242
|
-
) public returns (
|
|
271
|
+
) public returns (uint256) {
|
|
243
272
|
_validateBroadcasterAndOwnerSigner(metaTx);
|
|
244
|
-
|
|
245
|
-
return
|
|
273
|
+
EngineBlox.TxRecord memory txRecord = _requestAndApproveTransaction(metaTx);
|
|
274
|
+
return txRecord.txId;
|
|
246
275
|
}
|
|
247
276
|
|
|
248
277
|
// Execution Functions
|
|
249
278
|
/**
|
|
250
|
-
* @dev External function that can only be called by the contract itself to execute ownership transfer
|
|
251
|
-
* @param newOwner The new owner address
|
|
279
|
+
* @dev External function that can only be called by the contract itself to execute ownership transfer.
|
|
280
|
+
* @param newOwner The new owner; for the OWNERSHIP_TRANSFER flow this is the recovery address encoded at
|
|
281
|
+
* request time (see `transferOwnershipRequest`), not necessarily `getRecovery()` at execution time.
|
|
252
282
|
*/
|
|
253
283
|
function executeTransferOwnership(address newOwner) external {
|
|
254
284
|
_validateExecuteBySelf();
|
|
@@ -257,11 +287,12 @@ abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
|
257
287
|
|
|
258
288
|
/**
|
|
259
289
|
* @dev External function that can only be called by the contract itself to execute broadcaster update
|
|
260
|
-
* @param newBroadcaster
|
|
290
|
+
* @param newBroadcaster New broadcaster (`address(0)` to revoke `currentBroadcaster`)
|
|
291
|
+
* @param currentBroadcaster Existing broadcaster to replace or revoke; `address(0)` to add `newBroadcaster`
|
|
261
292
|
*/
|
|
262
|
-
function executeBroadcasterUpdate(address newBroadcaster) external {
|
|
293
|
+
function executeBroadcasterUpdate(address newBroadcaster, address currentBroadcaster) external {
|
|
263
294
|
_validateExecuteBySelf();
|
|
264
|
-
_updateBroadcaster(newBroadcaster,
|
|
295
|
+
_updateBroadcaster(newBroadcaster, currentBroadcaster);
|
|
265
296
|
}
|
|
266
297
|
|
|
267
298
|
/**
|
|
@@ -284,12 +315,6 @@ abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
|
284
315
|
|
|
285
316
|
// ============ INTERNAL FUNCTIONS ============
|
|
286
317
|
|
|
287
|
-
/**
|
|
288
|
-
* @dev Reverts if an ownership-transfer or broadcaster-update request is already pending.
|
|
289
|
-
*/
|
|
290
|
-
function _requireNoPendingRequest() internal view {
|
|
291
|
-
if (_hasOpenRequest) revert SharedValidation.PendingSecureRequest();
|
|
292
|
-
}
|
|
293
318
|
|
|
294
319
|
/**
|
|
295
320
|
* @dev Validates that the caller is the broadcaster and that the meta-tx signer is the owner.
|
|
@@ -301,37 +326,54 @@ abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
|
301
326
|
}
|
|
302
327
|
|
|
303
328
|
/**
|
|
304
|
-
* @dev Completes ownership flow after approval:
|
|
329
|
+
* @dev Completes ownership/broadcaster flow after approval: clears the matching pending flag and returns txId.
|
|
330
|
+
* @param updatedRecord The updated transaction record from approval
|
|
331
|
+
* @return txId The transaction ID
|
|
305
332
|
*/
|
|
306
|
-
function
|
|
307
|
-
|
|
308
|
-
return updatedRecord;
|
|
333
|
+
function _completeApprove(EngineBlox.TxRecord memory updatedRecord) internal returns (uint256 txId) {
|
|
334
|
+
_clearPendingFlagForOperation(updatedRecord.params.operationType);
|
|
335
|
+
return updatedRecord.txId;
|
|
309
336
|
}
|
|
310
337
|
|
|
311
338
|
/**
|
|
312
|
-
* @dev Completes ownership flow after cancellation:
|
|
339
|
+
* @dev Completes ownership/broadcaster flow after cancellation: clears the matching pending flag and returns txId.
|
|
340
|
+
* @param updatedRecord The updated transaction record from cancellation
|
|
341
|
+
* @return txId The transaction ID
|
|
313
342
|
*/
|
|
314
|
-
function
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
return updatedRecord;
|
|
343
|
+
function _completeCancel(EngineBlox.TxRecord memory updatedRecord) internal returns (uint256 txId) {
|
|
344
|
+
_clearPendingFlagForOperation(updatedRecord.params.operationType);
|
|
345
|
+
return updatedRecord.txId;
|
|
318
346
|
}
|
|
319
347
|
|
|
320
348
|
/**
|
|
321
|
-
* @dev
|
|
349
|
+
* @dev Reverts if the pending flag for `requestOperationType` is already set (one lane per call).
|
|
350
|
+
* `OWNERSHIP_TRANSFER` checks only `_hasOpenOwnershipRequest` (a broadcaster update may still be pending).
|
|
351
|
+
* `BROADCASTER_UPDATE` checks only `_hasOpenBroadcasterRequest`. Callers that need both lanes idle
|
|
352
|
+
* (e.g. `updateBroadcasterRequest`) invoke this once per operation type.
|
|
353
|
+
* @param requestOperationType Lane to validate (`OWNERSHIP_TRANSFER` or `BROADCASTER_UPDATE`).
|
|
322
354
|
*/
|
|
323
|
-
function
|
|
324
|
-
|
|
325
|
-
|
|
355
|
+
function _requireNoPendingRequest(bytes32 requestOperationType) internal view {
|
|
356
|
+
if (requestOperationType == SecureOwnableDefinitions.OWNERSHIP_TRANSFER) {
|
|
357
|
+
if (_hasOpenOwnershipRequest) revert SharedValidation.PendingSecureRequest();
|
|
358
|
+
} else if (requestOperationType == SecureOwnableDefinitions.BROADCASTER_UPDATE) {
|
|
359
|
+
if (_hasOpenBroadcasterRequest) revert SharedValidation.PendingSecureRequest();
|
|
360
|
+
} else {
|
|
361
|
+
revert();
|
|
362
|
+
}
|
|
326
363
|
}
|
|
327
364
|
|
|
328
365
|
/**
|
|
329
|
-
* @dev
|
|
366
|
+
* @dev Clears the pending flag for a completed or cancelled secure op (approve/cancel paths).
|
|
367
|
+
* @param operationType The tx record's `operationType` (`OWNERSHIP_TRANSFER` or `BROADCASTER_UPDATE`).
|
|
330
368
|
*/
|
|
331
|
-
function
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
369
|
+
function _clearPendingFlagForOperation(bytes32 operationType) private {
|
|
370
|
+
if (operationType == SecureOwnableDefinitions.OWNERSHIP_TRANSFER) {
|
|
371
|
+
_hasOpenOwnershipRequest = false;
|
|
372
|
+
} else if (operationType == SecureOwnableDefinitions.BROADCASTER_UPDATE) {
|
|
373
|
+
_hasOpenBroadcasterRequest = false;
|
|
374
|
+
} else {
|
|
375
|
+
revert();
|
|
376
|
+
}
|
|
335
377
|
}
|
|
336
378
|
|
|
337
379
|
/**
|
|
@@ -340,54 +382,47 @@ abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
|
340
382
|
*/
|
|
341
383
|
function _transferOwnership(address newOwner) internal virtual {
|
|
342
384
|
address oldOwner = owner();
|
|
343
|
-
|
|
344
|
-
|
|
385
|
+
_updateWallet(EngineBlox.OWNER_ROLE, newOwner, oldOwner);
|
|
386
|
+
_logAddressPairEvent(oldOwner, newOwner);
|
|
345
387
|
}
|
|
346
388
|
|
|
347
389
|
/**
|
|
348
|
-
* @dev
|
|
349
|
-
* @param newBroadcaster
|
|
350
|
-
* @param
|
|
351
|
-
*
|
|
352
|
-
* Logic:
|
|
353
|
-
* - If a broadcaster exists at `location` and `newBroadcaster` is non-zero,
|
|
354
|
-
* update that slot from old to new (role remains full).
|
|
355
|
-
* - If no broadcaster exists at `location` and `newBroadcaster` is non-zero,
|
|
356
|
-
* assign `newBroadcaster` to the broadcaster role (respecting maxWallets).
|
|
357
|
-
* - If `newBroadcaster` is the zero address and a broadcaster exists at `location`,
|
|
358
|
-
* revoke that broadcaster from the role.
|
|
390
|
+
* @dev Validates broadcaster update pair at request time.
|
|
391
|
+
* @param newBroadcaster New broadcaster (`address(0)` to revoke)
|
|
392
|
+
* @param currentBroadcaster Existing broadcaster; `address(0)` to add `newBroadcaster`
|
|
359
393
|
*/
|
|
360
|
-
function
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
address oldBroadcaster;
|
|
364
|
-
uint256 length = role.walletCount;
|
|
365
|
-
|
|
366
|
-
if (location < length) {
|
|
367
|
-
oldBroadcaster = _getAuthorizedWalletAt(EngineBlox.BROADCASTER_ROLE, location);
|
|
368
|
-
} else {
|
|
369
|
-
oldBroadcaster = address(0);
|
|
394
|
+
function _validateBroadcasterUpdatePair(address newBroadcaster, address currentBroadcaster) internal view {
|
|
395
|
+
if (newBroadcaster == address(0) && currentBroadcaster == address(0)) {
|
|
396
|
+
revert SharedValidation.InvalidOperation(address(0));
|
|
370
397
|
}
|
|
398
|
+
bytes32 role = EngineBlox.BROADCASTER_ROLE;
|
|
399
|
+
if (currentBroadcaster != address(0)) {
|
|
400
|
+
if (!hasRole(role, currentBroadcaster)) revert SharedValidation.ItemNotFound(currentBroadcaster);
|
|
401
|
+
}
|
|
402
|
+
if (newBroadcaster != address(0)) {
|
|
403
|
+
if (hasRole(role, newBroadcaster)) revert SharedValidation.ItemAlreadyExists(newBroadcaster);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
371
406
|
|
|
372
|
-
|
|
407
|
+
/**
|
|
408
|
+
* @dev Updates the broadcaster role by address pair (revoke, replace, or add).
|
|
409
|
+
* @param newBroadcaster New broadcaster (`address(0)` to revoke `currentBroadcaster`)
|
|
410
|
+
* @param currentBroadcaster Existing broadcaster; `address(0)` to add `newBroadcaster`
|
|
411
|
+
*/
|
|
412
|
+
function _updateBroadcaster(address newBroadcaster, address currentBroadcaster) internal virtual {
|
|
413
|
+
bytes32 role = EngineBlox.BROADCASTER_ROLE;
|
|
373
414
|
if (newBroadcaster == address(0)) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
_logComponentEvent(abi.encode(oldBroadcaster, address(0)));
|
|
377
|
-
}
|
|
415
|
+
_revokeWallet(role, currentBroadcaster);
|
|
416
|
+
_logAddressPairEvent(currentBroadcaster, address(0));
|
|
378
417
|
return;
|
|
379
418
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
_updateAssignedWallet(EngineBlox.BROADCASTER_ROLE, newBroadcaster, oldBroadcaster);
|
|
384
|
-
_logComponentEvent(abi.encode(oldBroadcaster, newBroadcaster));
|
|
419
|
+
if (currentBroadcaster == address(0)) {
|
|
420
|
+
_assignWallet(role, newBroadcaster);
|
|
421
|
+
_logAddressPairEvent(address(0), newBroadcaster);
|
|
385
422
|
return;
|
|
386
423
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
_assignWallet(EngineBlox.BROADCASTER_ROLE, newBroadcaster);
|
|
390
|
-
_logComponentEvent(abi.encode(address(0), newBroadcaster));
|
|
424
|
+
_updateWallet(role, newBroadcaster, currentBroadcaster);
|
|
425
|
+
_logAddressPairEvent(currentBroadcaster, newBroadcaster);
|
|
391
426
|
}
|
|
392
427
|
|
|
393
428
|
/**
|
|
@@ -396,17 +431,16 @@ abstract contract SecureOwnable is BaseStateMachine, ISecureOwnable {
|
|
|
396
431
|
*/
|
|
397
432
|
function _updateRecoveryAddress(address newRecoveryAddress) internal virtual {
|
|
398
433
|
address oldRecovery = getRecovery();
|
|
399
|
-
|
|
400
|
-
|
|
434
|
+
_updateWallet(EngineBlox.RECOVERY_ROLE, newRecoveryAddress, oldRecovery);
|
|
435
|
+
_logAddressPairEvent(oldRecovery, newRecoveryAddress);
|
|
401
436
|
}
|
|
402
437
|
|
|
403
438
|
/**
|
|
404
|
-
* @dev
|
|
405
|
-
* @param
|
|
439
|
+
* @dev Emits ComponentEvent with ABI-encoded (address, address) payload. Reused to reduce contract size.
|
|
440
|
+
* @param a First address
|
|
441
|
+
* @param b Second address
|
|
406
442
|
*/
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
super._updateTimeLockPeriod(newTimeLockPeriodSec);
|
|
410
|
-
_logComponentEvent(abi.encode(oldPeriod, newTimeLockPeriodSec));
|
|
443
|
+
function _logAddressPairEvent(address a, address b) internal {
|
|
444
|
+
_logComponentEvent(abi.encode(a, b));
|
|
411
445
|
}
|
|
412
446
|
}
|