From 7903c060a01af300297bd9b9b48d7a8cdba8a3ce Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Wed, 30 Apr 2025 16:46:19 -0400 Subject: [PATCH 1/2] fix: ejection manager inclusive ejection --- src/EjectionManager.sol | 12 +------- test/unit/EjectionManagerUnit.t.sol | 45 +++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/EjectionManager.sol b/src/EjectionManager.sol index 234b6f1dc..d24889f72 100644 --- a/src/EjectionManager.sol +++ b/src/EjectionManager.sol @@ -61,17 +61,7 @@ contract EjectionManager is OwnableUpgradeable, EjectionManagerStorage { && stakeForEjection + operatorStake > amountEjectable ) { ratelimitHit = true; - - stakeForEjection += operatorStake; - ++ejectedOperators; - - slashingRegistryCoordinator.ejectOperator( - slashingRegistryCoordinator.getOperatorFromId(operatorIds[i][j]), - abi.encodePacked(quorumNumber) - ); - - emit OperatorEjected(operatorIds[i][j], quorumNumber); - + break; } diff --git a/test/unit/EjectionManagerUnit.t.sol b/test/unit/EjectionManagerUnit.t.sol index 3e77a790d..c1ffddac4 100644 --- a/test/unit/EjectionManagerUnit.t.sol +++ b/test/unit/EjectionManagerUnit.t.sol @@ -150,9 +150,10 @@ contract EjectionManagerUnitTests is MockAVSDeployer { ); } } - + function testEjectOperators_MultipleOperatorOutsideRatelimit() public { - uint8 operatorsCanEject = 2; + /// @dev Since the `ejectableStakePercent` is 10%, only 1 operator can be ejected + uint8 operatorsCanEject = 1; uint8 operatorsToEject = 10; uint8 numOperators = 10; uint96 stake = 1 ether; @@ -199,8 +200,46 @@ contract EjectionManagerUnitTests is MockAVSDeployer { } } + /// @dev Regression test for Hexens Slashing EG2-2 + /// @dev The quorum ejection params are set to 10%. Previously an operator would have been ejected due to + /// @dev a bug that allowed an ejection to occur if there was at least 1 operator to eject, regardless of the limit. + function testEjectOperators_regression_inclusiveRateLimit() public { + /// @dev The quorum ejection params are set to 10%, which does not need to be updated + uint8 numOperators = 1; + uint96 stake = 1 ether; + + _registerOperators(numOperators, stake); + + bytes32[][] memory operatorIds = new bytes32[][](numQuorums); + for (uint8 i = 0; i < numQuorums; i++) { + operatorIds[i] = new bytes32[](numOperators); + for (uint256 j = 0; j < numOperators; j++) { + operatorIds[i][j] = + registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + } + } + + for (uint8 i = 0; i < numOperators; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); + } + + cheats.prank(ejector); + ejectionManager.ejectOperators(operatorIds); + + /// @dev The operator should not be ejected due to the rate limit + for (uint8 i = 0; i < numOperators; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); + } + } + function testEjectOperators_NoEjectionForNoEjectableStake() public { - uint8 operatorsCanEject = 2; + uint8 operatorsCanEject = 1; uint8 operatorsToEject = 10; uint8 numOperators = 10; uint96 stake = 1 ether; From 02d07610abc9617e9c4b1aba14db858ee3ac0c70 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Wed, 30 Apr 2025 16:54:45 -0400 Subject: [PATCH 2/2] chore: format --- src/EjectionManager.sol | 2 +- test/unit/EjectionManagerUnit.t.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EjectionManager.sol b/src/EjectionManager.sol index d24889f72..369827b63 100644 --- a/src/EjectionManager.sol +++ b/src/EjectionManager.sol @@ -61,7 +61,7 @@ contract EjectionManager is OwnableUpgradeable, EjectionManagerStorage { && stakeForEjection + operatorStake > amountEjectable ) { ratelimitHit = true; - + break; } diff --git a/test/unit/EjectionManagerUnit.t.sol b/test/unit/EjectionManagerUnit.t.sol index c1ffddac4..25ef6ee16 100644 --- a/test/unit/EjectionManagerUnit.t.sol +++ b/test/unit/EjectionManagerUnit.t.sol @@ -150,7 +150,7 @@ contract EjectionManagerUnitTests is MockAVSDeployer { ); } } - + function testEjectOperators_MultipleOperatorOutsideRatelimit() public { /// @dev Since the `ejectableStakePercent` is 10%, only 1 operator can be ejected uint8 operatorsCanEject = 1; @@ -202,7 +202,7 @@ contract EjectionManagerUnitTests is MockAVSDeployer { /// @dev Regression test for Hexens Slashing EG2-2 /// @dev The quorum ejection params are set to 10%. Previously an operator would have been ejected due to - /// @dev a bug that allowed an ejection to occur if there was at least 1 operator to eject, regardless of the limit. + /// @dev a bug that allowed an ejection to occur if there was at least 1 operator to eject, regardless of the limit. function testEjectOperators_regression_inclusiveRateLimit() public { /// @dev The quorum ejection params are set to 10%, which does not need to be updated uint8 numOperators = 1;