Skip to content

Commit 2eed9b3

Browse files
authored
Add validators manager delegation to NodesManager (#133)
1 parent f80b46a commit 2eed9b3

File tree

4 files changed

+293
-98
lines changed

4 files changed

+293
-98
lines changed

contracts/interfaces/INodesManager.sol

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ interface INodesManager is IERC5267, IERC1822Proxiable, IMulticall {
122122
address indexed operator, uint128 totalAssets, uint128 cumPenaltyAssets, uint128 cumEarnedFeeShares
123123
);
124124

125+
/**
126+
* @notice Event emitted when the validators manager is updated for an operator
127+
* @param operator The address of the operator
128+
* @param validatorsManager The new validators manager address
129+
*/
130+
event ValidatorsManagerUpdated(address indexed operator, address indexed validatorsManager);
131+
125132
/**
126133
* @notice Event emitted when the state update delay is updated
127134
* @param stateUpdateDelay The new state update delay in seconds
@@ -287,19 +294,37 @@ interface INodesManager is IERC5267, IERC1822Proxiable, IMulticall {
287294
function setWithdrawalsManager(address newWithdrawalsManager) external;
288295

289296
/**
290-
* @notice Registers validators with oracle-approved signatures
297+
* @notice The validators manager address for the given operator
298+
* @param operator The operator address
299+
* @return The validators manager address
300+
*/
301+
function validatorsManagers(address operator) external view returns (address);
302+
303+
/**
304+
* @notice Sets the validators manager address for the calling operator
305+
* @param validatorsManager The new validators manager address
306+
*/
307+
function setValidatorsManager(address validatorsManager) external;
308+
309+
/**
310+
* @notice Registers validators with oracle-approved signatures. Can only be called by the operator's validators manager.
311+
* @param operator The address of the operator
291312
* @param keeperParams The keeper approval parameters containing validator data
292313
* @param signatures The concatenation of the oracles' signatures
293314
*/
294-
function registerValidators(IKeeperValidators.ApprovalParams calldata keeperParams, bytes calldata signatures)
295-
external;
315+
function registerValidators(
316+
address operator,
317+
IKeeperValidators.ApprovalParams calldata keeperParams,
318+
bytes calldata signatures
319+
) external;
296320

297321
/**
298-
* @notice Funds validators with oracle-approved signatures
322+
* @notice Funds validators with oracle-approved signatures. Can only be called by the operator's validators manager.
323+
* @param operator The address of the operator
299324
* @param validators The concatenation of the validators' data
300325
* @param signatures The concatenation of the oracles' signatures approving the funding
301326
*/
302-
function fundValidators(bytes calldata validators, bytes calldata signatures) external;
327+
function fundValidators(address operator, bytes calldata validators, bytes calldata signatures) external;
303328

304329
/**
305330
* @notice Enters the exit queue by locking operator shares in the vault's exit queue
@@ -324,9 +349,10 @@ interface INodesManager is IERC5267, IERC1822Proxiable, IMulticall {
324349

325350
/**
326351
* @notice Updates the operator state by verifying a merkle proof against the current state root
352+
* @param operator The address of the operator to update
327353
* @param params The parameters for updating the operator state
328354
*/
329-
function updateOperatorState(OperatorStateUpdateParams calldata params) external;
355+
function updateOperatorState(address operator, OperatorStateUpdateParams calldata params) external;
330356

331357
/**
332358
* @notice Checks whether state can be updated

contracts/nodes/NodesManager.sol

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ abstract contract NodesManager is
6666
/// @inheritdoc INodesManager
6767
mapping(address operator => uint256 penaltyAssets) public override pendingPenaltyAssets;
6868

69+
/// @inheritdoc INodesManager
70+
mapping(address operator => address manager) public override validatorsManagers;
71+
6972
mapping(uint256 positionTicket => address operator) private _exitPositions;
7073

7174
/**
@@ -128,6 +131,13 @@ abstract contract NodesManager is
128131
emit WithdrawalsManagerUpdated(newWithdrawalsManager);
129132
}
130133

134+
/// @inheritdoc INodesManager
135+
function setValidatorsManager(address validatorsManager) external override {
136+
if (validatorsManagers[msg.sender] == validatorsManager) revert Errors.ValueNotChanged();
137+
validatorsManagers[msg.sender] = validatorsManager;
138+
emit ValidatorsManagerUpdated(msg.sender, validatorsManager);
139+
}
140+
131141
/// @inheritdoc INodesManager
132142
function canUpdateState() external view override returns (bool) {
133143
// SLOAD to memory
@@ -182,17 +192,19 @@ abstract contract NodesManager is
182192
}
183193

184194
/// @inheritdoc INodesManager
185-
function updateOperatorState(OperatorStateUpdateParams calldata params) external override {
195+
function updateOperatorState(address operator, OperatorStateUpdateParams calldata params) external override {
196+
if (operator == address(0)) revert Errors.ZeroAddress();
197+
186198
// check whether the vault is harvested
187199
if (_keeper.isHarvestRequired(vault)) revert Errors.NotHarvested();
188200

189201
// SLOAD to memory
190-
OperatorState memory operatorState = operatorStates[msg.sender];
202+
OperatorState memory operatorState = operatorStates[operator];
191203
StateData memory _stateData = stateData;
192204
uint128 currentNonce = _stateData.currentNonce;
193205

194206
// skip update if the state is already up to date
195-
if (operatorNonces[msg.sender][OperatorNonceType.LastStateUpdate] == currentNonce) return;
207+
if (operatorNonces[operator][OperatorNonceType.LastStateUpdate] == currentNonce) return;
196208

197209
// verify merkle proof against current state root
198210
if (!MerkleProof.verifyCalldata(
@@ -201,9 +213,7 @@ abstract contract NodesManager is
201213
keccak256(
202214
bytes.concat(
203215
keccak256(
204-
abi.encode(
205-
msg.sender, params.totalAssets, params.cumPenaltyAssets, params.cumEarnedFeeShares
206-
)
216+
abi.encode(operator, params.totalAssets, params.cumPenaltyAssets, params.cumEarnedFeeShares)
207217
)
208218
)
209219
)
@@ -218,7 +228,7 @@ abstract contract NodesManager is
218228
// calculate total penalty including any pending penalty from previous updates
219229
uint256 totalPenaltyShares;
220230
uint256 penaltyAssetsDelta = params.cumPenaltyAssets - operatorState.cumPenaltyAssets;
221-
uint256 totalPenaltyAssets = penaltyAssetsDelta + pendingPenaltyAssets[msg.sender];
231+
uint256 totalPenaltyAssets = penaltyAssetsDelta + pendingPenaltyAssets[operator];
222232
if (totalPenaltyAssets > 0) {
223233
totalPenaltyShares = IVaultState(vault).convertToShares(totalPenaltyAssets);
224234
}
@@ -227,11 +237,11 @@ abstract contract NodesManager is
227237
uint256 penaltySharesToDonate;
228238
if (totalPenaltyShares <= availableShares) {
229239
operatorState.balanceShares = SafeCast.toUint128(availableShares - totalPenaltyShares);
230-
pendingPenaltyAssets[msg.sender] = 0;
240+
pendingPenaltyAssets[operator] = 0;
231241
penaltySharesToDonate = totalPenaltyShares;
232242
} else {
233243
uint256 coveredPenaltyAssets = IVaultState(vault).convertToAssets(availableShares);
234-
pendingPenaltyAssets[msg.sender] = totalPenaltyAssets - coveredPenaltyAssets;
244+
pendingPenaltyAssets[operator] = totalPenaltyAssets - coveredPenaltyAssets;
235245
operatorState.balanceShares = 0;
236246
penaltySharesToDonate = availableShares;
237247
}
@@ -240,27 +250,32 @@ abstract contract NodesManager is
240250
operatorState.totalAssets = params.totalAssets;
241251
operatorState.cumPenaltyAssets = params.cumPenaltyAssets;
242252
operatorState.cumEarnedFeeShares = params.cumEarnedFeeShares;
243-
operatorStates[msg.sender] = operatorState;
244-
operatorNonces[msg.sender][OperatorNonceType.LastStateUpdate] = currentNonce;
253+
operatorStates[operator] = operatorState;
254+
operatorNonces[operator][OperatorNonceType.LastStateUpdate] = currentNonce;
245255

246256
// donate penalty shares to the vault
247257
if (penaltySharesToDonate > 0) {
248258
IVaultState(vault).donateShares(penaltySharesToDonate);
249259
}
250260

251-
emit OperatorStateUpdated(msg.sender, params.totalAssets, params.cumPenaltyAssets, params.cumEarnedFeeShares);
261+
emit OperatorStateUpdated(operator, params.totalAssets, params.cumPenaltyAssets, params.cumEarnedFeeShares);
252262
}
253263

254264
/// @inheritdoc INodesManager
255-
function registerValidators(IKeeperValidators.ApprovalParams calldata keeperParams, bytes calldata signatures)
256-
external
257-
override
258-
{
265+
function registerValidators(
266+
address operator,
267+
IKeeperValidators.ApprovalParams calldata keeperParams,
268+
bytes calldata signatures
269+
) external override {
270+
if (validatorsManagers[operator] != msg.sender) {
271+
revert Errors.AccessDenied();
272+
}
273+
259274
// verify oracles approved registering these validators
260-
uint256 nonce = _useOperatorNonce(msg.sender, OperatorNonceType.RegisterValidatorsSig);
275+
uint256 nonce = _useOperatorNonce(operator, OperatorNonceType.RegisterValidatorsSig);
261276
bytes32 digest = _hashTypedDataV4(
262277
keccak256(
263-
abi.encode(_registerValidatorsTypeHash, msg.sender, nonce, vault, keccak256(keeperParams.validators))
278+
abi.encode(_registerValidatorsTypeHash, operator, nonce, vault, keccak256(keeperParams.validators))
264279
)
265280
);
266281
_verifySignatures(digest, signatures);
@@ -269,35 +284,37 @@ abstract contract NodesManager is
269284
IVaultValidators(vault).registerValidators(keeperParams, bytes(""));
270285

271286
// save state nonce at validator change
272-
operatorNonces[msg.sender][OperatorNonceType.LastValidatorChange] = stateData.currentNonce;
287+
operatorNonces[operator][OperatorNonceType.LastValidatorChange] = stateData.currentNonce;
273288

274289
// extract public keys from validators data
275290
bytes memory publicKeys = _getValidatorsPublicKeys(keeperParams.validators);
276291

277292
// emit event
278-
emit ValidatorsRegistered(msg.sender, nonce, publicKeys);
293+
emit ValidatorsRegistered(operator, nonce, publicKeys);
279294
}
280295

281296
/// @inheritdoc INodesManager
282-
function fundValidators(bytes calldata validators, bytes calldata signatures) external override {
297+
function fundValidators(address operator, bytes calldata validators, bytes calldata signatures) external override {
298+
if (validatorsManagers[operator] != msg.sender) revert Errors.AccessDenied();
299+
283300
// verify oracles approved funding these validators
284-
uint256 nonce = _useOperatorNonce(msg.sender, OperatorNonceType.FundValidatorsSig);
301+
uint256 nonce = _useOperatorNonce(operator, OperatorNonceType.FundValidatorsSig);
285302
bytes32 digest = _hashTypedDataV4(
286-
keccak256(abi.encode(_fundValidatorsTypeHash, msg.sender, nonce, vault, keccak256(validators)))
303+
keccak256(abi.encode(_fundValidatorsTypeHash, operator, nonce, vault, keccak256(validators)))
287304
);
288305
_verifySignatures(digest, signatures);
289306

290307
// fund validators in the vault
291308
IVaultValidators(vault).fundValidators(validators, bytes(""));
292309

293310
// save state nonce at validator change
294-
operatorNonces[msg.sender][OperatorNonceType.LastValidatorChange] = stateData.currentNonce;
311+
operatorNonces[operator][OperatorNonceType.LastValidatorChange] = stateData.currentNonce;
295312

296313
// extract public keys from validators data
297314
bytes memory publicKeys = _getValidatorsPublicKeys(validators);
298315

299316
// emit event
300-
emit ValidatorsFunded(msg.sender, nonce, publicKeys);
317+
emit ValidatorsFunded(operator, nonce, publicKeys);
301318
}
302319

303320
/// @inheritdoc INodesManager

snapshots/EthNodesManagerTest.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
2-
"EthNodesManagerTest_test_claimExitedAssets": "99189",
3-
"EthNodesManagerTest_test_deposit": "124426",
4-
"EthNodesManagerTest_test_deposit_multipleDeposits_first": "124426",
5-
"EthNodesManagerTest_test_deposit_multipleDeposits_second": "106677",
6-
"EthNodesManagerTest_test_enterExitQueue": "138603",
7-
"EthNodesManagerTest_test_fundValidators": "161806",
8-
"EthNodesManagerTest_test_registerValidators": "321566",
9-
"EthNodesManagerTest_test_setMinBalancePercent": "40917",
2+
"EthNodesManagerTest_test_claimExitedAssets": "99124",
3+
"EthNodesManagerTest_test_deposit": "124404",
4+
"EthNodesManagerTest_test_deposit_multipleDeposits_first": "124404",
5+
"EthNodesManagerTest_test_deposit_multipleDeposits_second": "106655",
6+
"EthNodesManagerTest_test_enterExitQueue": "138581",
7+
"EthNodesManagerTest_test_fundValidators": "164727",
8+
"EthNodesManagerTest_test_registerValidators": "324473",
9+
"EthNodesManagerTest_test_setMinBalancePercent": "40850",
1010
"EthNodesManagerTest_test_setMinDepositAssets": "40222",
1111
"EthNodesManagerTest_test_setWithdrawalsManager": "40580",
12-
"EthNodesManagerTest_test_withdrawValidators": "87428"
12+
"EthNodesManagerTest_test_withdrawValidators": "87361"
1313
}

0 commit comments

Comments
 (0)