diff --git a/docs/release/slashing/AllocationManager.md b/docs/release/slashing/AllocationManager.md index 945432fb3..1be5fde84 100644 --- a/docs/release/slashing/AllocationManager.md +++ b/docs/release/slashing/AllocationManager.md @@ -106,7 +106,7 @@ Completing a deallocation decreases the encumbered magnitude for the strategy, a * @param operator the address to slash * @param operatorSetId the ID of the operatorSet the operator is being slashed on behalf of * @param strategies the set of strategies to slash - * @param wadToSlash the parts in 1e18 to slash, this will be proportional to the operator's + * @param wadsToSlash the parts in 1e18 to slash, this will be proportional to the operator's * slashable stake allocation for the operatorSet * @param description the description of the slashing provided by the AVS for legibility */ @@ -114,7 +114,7 @@ struct SlashingParams { address operator; uint32 operatorSetId; IStrategy[] strategies; - uint256 wadToSlash; + uint256[] wadsToSlash; string description; } diff --git a/lib/zeus-templates b/lib/zeus-templates index 51a667085..87e5faca6 160000 --- a/lib/zeus-templates +++ b/lib/zeus-templates @@ -1 +1 @@ -Subproject commit 51a667085a4029a311156431c77b57f48f2cae9f +Subproject commit 87e5faca6fde0ba39ab63ad7f3cf275c92779823 diff --git a/script/tasks/slash_operatorSet.s.sol b/script/tasks/slash_operatorSet.s.sol index d17feb63b..4b078c263 100644 --- a/script/tasks/slash_operatorSet.s.sol +++ b/script/tasks/slash_operatorSet.s.sol @@ -9,10 +9,16 @@ import "forge-std/Test.sol"; // use forge: // RUST_LOG=forge,foundry=trace forge script script/tasks/slash_operatorSet.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address operator,uint32 operatorSetId,uint256 wadToSlash)" -- // RUST_LOG=forge,foundry=trace forge script script/tasks/slash_operatorSet.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --sig "run(string memory configFile,address operator,uint32 operatorSetId,uint256 wadToSlash)" -- local/slashing_output.json 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 00000001 05000000 -contract SlashOperatorSet is Script, Test { +contract SlashOperatorSet is Script, Test, IAllocationManagerTypes { Vm cheats = Vm(VM_ADDRESS); - function run(string memory configFile, address operator, uint32 operatorSetId, uint256 wadToSlash) public { + function run( + string memory configFile, + address operator, + uint32 operatorSetId, + IStrategy[] memory strategies, + uint256[] memory wadsToSlash + ) public { // Load config string memory deployConfigPath = string(bytes(string.concat("script/output/", configFile))); string memory config_data = vm.readFile(deployConfigPath); @@ -27,10 +33,11 @@ contract SlashOperatorSet is Script, Test { AllocationManager am = AllocationManager(allocationManager); // Define SlashingParams struct instance with correct array initialization - IAllocationManagerTypes.SlashingParams memory slashing = IAllocationManagerTypes.SlashingParams({ + SlashingParams memory slashing = SlashingParams({ operator: operator, operatorSetId: operatorSetId, - wadToSlash: wadToSlash, + strategies: strategies, + wadsToSlash: wadsToSlash, description: "slashed" }); diff --git a/src/contracts/core/AllocationManager.sol b/src/contracts/core/AllocationManager.sol index e43961e8e..cbb4e6ff5 100644 --- a/src/contracts/core/AllocationManager.sol +++ b/src/contracts/core/AllocationManager.sol @@ -59,28 +59,33 @@ contract AllocationManager is function slashOperator( address avs, SlashingParams calldata params - ) external onlyWhenNotPaused(PAUSED_OPERATOR_SLASHING) { - // Check that the msg.sender can call - we don't use a modifier to avoid `stack too deep` errors - require(_checkCanCall(avs), InvalidCaller()); - require(0 < params.wadToSlash && params.wadToSlash <= WAD, InvalidWadToSlash()); - + ) external onlyWhenNotPaused(PAUSED_OPERATOR_SLASHING) checkCanCall(avs) { // Check that the operator set exists and the operator is registered to it OperatorSet memory operatorSet = OperatorSet(avs, params.operatorSetId); bool isRegistered = _isRegistered(params.operator, operatorSet); require(_operatorSets[operatorSet.avs].contains(operatorSet.id), InvalidOperatorSet()); require(isRegistered, NotMemberOfSet()); - uint256 length = _operatorSetStrategies[operatorSet.key()].length(); - IStrategy[] memory strategiesSlashed = new IStrategy[](length); - uint256[] memory wadSlashed = new uint256[](length); + uint256[] memory wadSlashed = new uint256[](params.strategies.length); // For each strategy in the operator set, slash any existing allocation - for (uint256 i = 0; i < length; i++) { + for (uint256 i = 0; i < params.strategies.length; i++) { + // Check that `strategies` is in ascending order. + require( + i == 0 || uint160(address(params.strategies[i])) > uint160(address(params.strategies[i - 1])), + StrategiesMustBeInAscendingOrder() + ); + // Check that `wadToSlash` is within acceptable bounds. + require(0 < params.wadsToSlash[i] && params.wadsToSlash[i] <= WAD, InvalidWadToSlash()); + // Check that the operator set contains the strategy. + require( + _operatorSetStrategies[operatorSet.key()].contains(address(params.strategies[i])), + StrategyNotInOperatorSet() + ); + // 1. Get the operator's allocation info for the strategy and operator set - IStrategy strategy = IStrategy(_operatorSetStrategies[operatorSet.key()].at(i)); (StrategyInfo memory info, Allocation memory allocation) = - _getUpdatedAllocation(params.operator, operatorSet.key(), strategy); - strategiesSlashed[i] = strategy; + _getUpdatedAllocation(params.operator, operatorSet.key(), params.strategies[i]); // 2. Skip if the operator does not have a slashable allocation // NOTE: this "if" is equivalent to: `if (!_isAllocationSlashable)`, because the other @@ -92,10 +97,9 @@ contract AllocationManager is // 3. Calculate the amount of magnitude being slashed, and subtract from // the operator's currently-allocated magnitude, as well as the strategy's // max and encumbered magnitudes - uint64 slashedMagnitude = uint64(uint256(allocation.currentMagnitude).mulWadRoundUp(params.wadToSlash)); - uint256 sharesWadSlashed = uint256(slashedMagnitude).divWad(info.maxMagnitude); - wadSlashed[i] = sharesWadSlashed; + uint64 slashedMagnitude = uint64(uint256(allocation.currentMagnitude).mulWadRoundUp(params.wadsToSlash[i])); uint64 prevMaxMagnitude = info.maxMagnitude; + wadSlashed[i] = uint256(slashedMagnitude).divWad(info.maxMagnitude); allocation.currentMagnitude -= slashedMagnitude; info.maxMagnitude -= slashedMagnitude; @@ -105,32 +109,32 @@ contract AllocationManager is // This ensures that when the deallocation is completed, less magnitude is freed. if (allocation.pendingDiff < 0) { uint64 slashedPending = - uint64(uint256(uint128(-allocation.pendingDiff)).mulWadRoundUp(params.wadToSlash)); + uint64(uint256(uint128(-allocation.pendingDiff)).mulWadRoundUp(params.wadsToSlash[i])); allocation.pendingDiff += int128(uint128(slashedPending)); emit AllocationUpdated( params.operator, operatorSet, - strategy, + params.strategies[i], _addInt128(allocation.currentMagnitude, allocation.pendingDiff), allocation.effectBlock ); } // 5. Update state - _updateAllocationInfo(params.operator, operatorSet.key(), strategy, info, allocation); - _updateMaxMagnitude(params.operator, strategy, info.maxMagnitude); + _updateAllocationInfo(params.operator, operatorSet.key(), params.strategies[i], info, allocation); + _updateMaxMagnitude(params.operator, params.strategies[i], info.maxMagnitude); // 6. Decrease and burn operators shares in the DelegationManager delegation.burnOperatorShares({ operator: params.operator, - strategy: strategy, + strategy: params.strategies[i], prevMaxMagnitude: prevMaxMagnitude, newMaxMagnitude: info.maxMagnitude }); } - emit OperatorSlashed(params.operator, operatorSet, strategiesSlashed, wadSlashed, params.description); + emit OperatorSlashed(params.operator, operatorSet, params.strategies, wadSlashed, params.description); } /// @inheritdoc IAllocationManager diff --git a/src/contracts/interfaces/IAllocationManager.sol b/src/contracts/interfaces/IAllocationManager.sol index 0b57ee729..05ee9d117 100644 --- a/src/contracts/interfaces/IAllocationManager.sol +++ b/src/contracts/interfaces/IAllocationManager.sol @@ -45,6 +45,8 @@ interface IAllocationManagerErrors { error InvalidOperatorSet(); /// @dev Thrown when a strategy is referenced that does not belong to an operator set. error InvalidStrategy(); + /// @dev Thrown when provided `strategies` are not in ascending order. + error StrategiesMustBeInAscendingOrder(); /// @dev Thrown when trying to add a strategy to an operator set that already contains it. error StrategyAlreadyInOperatorSet(); /// @dev Thrown when trying to remove a strategy from an operator set it is not a part of. @@ -113,14 +115,16 @@ interface IAllocationManagerTypes { * @notice Struct containing parameters to slashing * @param operator the address to slash * @param operatorSetId the ID of the operatorSet the operator is being slashed on behalf of - * @param wadToSlash the parts in 1e18 to slash, this will be proportional to the operator's + * @param strategies the set of strategies to slash + * @param wadsToSlash the parts in 1e18 to slash, this will be proportional to the operator's * slashable stake allocation for the operatorSet * @param description the description of the slashing provided by the AVS for legibility */ struct SlashingParams { address operator; uint32 operatorSetId; - uint256 wadToSlash; + IStrategy[] strategies; + uint256[] wadsToSlash; string description; } diff --git a/src/test/DevnetLifecycle.t.sol b/src/test/DevnetLifecycle.t.sol index 5769b22c3..2c67a6854 100644 --- a/src/test/DevnetLifecycle.t.sol +++ b/src/test/DevnetLifecycle.t.sol @@ -8,12 +8,15 @@ import "../../src/contracts/core/AVSDirectory.sol"; import "../../src/contracts/core/AllocationManager.sol"; import "../../src/contracts/strategies/StrategyBase.sol"; +import "src/test/utils/ArrayLib.sol"; + // Test import "forge-std/Test.sol"; /// @notice Tests deployed contracts as part of the public devnet /// Run with: forge test --mc Devnet_Lifecycle_Test --rpc-url $RPC_HOLESKY -contract Devnet_Lifecycle_Test is Test { +contract Devnet_Lifecycle_Test is Test, IAllocationManagerTypes { + using ArrayLib for *; // Contracts DelegationManager public delegationManager; @@ -53,15 +56,11 @@ contract Devnet_Lifecycle_Test is Test { } function _getOperatorSetArray() internal view returns (uint32[] memory) { - uint32[] memory operatorSets = new uint32[](1); - operatorSets[0] = operatorSetId; - return operatorSets; + return operatorSetId.toArrayU32(); } function _getOperatorSetsArray() internal view returns (OperatorSet[] memory) { - OperatorSet[] memory operatorSets = new OperatorSet[](1); - operatorSets[0] = OperatorSet({avs: avs, id: operatorSetId}); - return operatorSets; + return OperatorSet({avs: avs, id: operatorSetId}).toArray(); } function test() public { @@ -134,51 +133,35 @@ contract Devnet_Lifecycle_Test is Test { function _registerAVS() internal { cheats.startPrank(avs); - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = wethStrategy; - IAllocationManagerTypes.CreateSetParams memory createSetParams = IAllocationManagerTypes.CreateSetParams({ + CreateSetParams memory createSetParams =CreateSetParams({ operatorSetId: operatorSetId, - strategies: strategies + strategies: wethStrategy.toArray() }); - IAllocationManagerTypes.CreateSetParams[] memory array = new IAllocationManagerTypes.CreateSetParams[](1); - array[0] = createSetParams; - - allocationManager.createOperatorSets(avs, array); + allocationManager.createOperatorSets(avs, createSetParams.toArray()); cheats.stopPrank(); } function _registerOperatorToAVS() public { cheats.prank(operator); - - uint32[] memory operatorSetIds = new uint32[](1); - operatorSetIds[0] = operatorSetId; - - allocationManager.registerForOperatorSets(operator, IAllocationManagerTypes.RegisterParams(avs, operatorSetIds, "")); - + allocationManager.registerForOperatorSets(operator,RegisterParams(avs, operatorSetId.toArrayU32(), "")); assertEq(allocationManager.getMembers(OperatorSet(avs, operatorSetId))[0], operator); } function _setMagnitude() public { - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = wethStrategy; - - uint64[] memory magnitudes = new uint64[](1); - magnitudes[0] = magnitudeToSet; - - IAllocationManagerTypes.AllocateParams[] memory allocations = new IAllocationManagerTypes.AllocateParams[](1); - allocations[0] = IAllocationManagerTypes.AllocateParams({ + AllocateParams[] memory allocations = new AllocateParams[](1); + allocations[0] = AllocateParams({ operatorSet: operatorSet, - strategies: strategies, - newMagnitudes: magnitudes + strategies: wethStrategy.toArray(), + newMagnitudes: magnitudeToSet.toArrayU64() }); cheats.prank(operator); allocationManager.modifyAllocations(operator, allocations); // Assert storage - IAllocationManagerTypes.Allocation memory info = allocationManager.getAllocation(operator, operatorSet, wethStrategy); + Allocation memory info = allocationManager.getAllocation(operator, operatorSet, wethStrategy); assertEq(info.currentMagnitude, 0); assertEq(info.pendingDiff, int128(uint128(magnitudeToSet))); assertEq(info.effectBlock, block.number + 1); @@ -193,12 +176,11 @@ contract Devnet_Lifecycle_Test is Test { function _slashOperator() public { // Get slashing params - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = wethStrategy; - IAllocationManagerTypes.SlashingParams memory slashingParams = IAllocationManagerTypes.SlashingParams({ + SlashingParams memory slashingParams = SlashingParams({ operator: operator, operatorSetId: 1, - wadToSlash: 5e17, + strategies: wethStrategy.toArray(), + wadsToSlash: 5e17.toArrayU256(), description: "test" }); @@ -207,14 +189,13 @@ contract Devnet_Lifecycle_Test is Test { allocationManager.slashOperator(avs, slashingParams); // Assert storage - IAllocationManagerTypes.Allocation memory info = allocationManager.getAllocation(operator, operatorSet, wethStrategy); + Allocation memory info = allocationManager.getAllocation(operator, operatorSet, wethStrategy); assertEq(info.currentMagnitude, magnitudeToSet - 5e17); } function _withdrawStaker() public { // Generate queued withdrawal params - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = wethStrategy; + IStrategy[] memory strategies = wethStrategy.toArray(); (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategies); IDelegationManagerTypes.QueuedWithdrawalParams[] memory queuedWithdrawals = new IDelegationManagerTypes.QueuedWithdrawalParams[](1); queuedWithdrawals[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ @@ -224,8 +205,6 @@ contract Devnet_Lifecycle_Test is Test { }); // Generate withdrawal params - uint256[] memory scaledShares = new uint256[](1); - scaledShares[0] = 100e18; IDelegationManagerTypes.Withdrawal memory withdrawal = IDelegationManagerTypes.Withdrawal({ staker: staker, delegatedTo: operator, @@ -233,7 +212,7 @@ contract Devnet_Lifecycle_Test is Test { nonce: delegationManager.cumulativeWithdrawalsQueued(staker), startBlock: uint32(block.number), strategies: strategies, - scaledShares: scaledShares + scaledShares: 100e18.toArrayU256() }); // bytes32 withdrawalRoot = delegationManager.calculateWithdrawalRoot(withdrawal); // Generate complete withdrawal params diff --git a/src/test/integration/IntegrationBase.t.sol b/src/test/integration/IntegrationBase.t.sol index 228af0435..62eedb6e8 100644 --- a/src/test/integration/IntegrationBase.t.sol +++ b/src/test/integration/IntegrationBase.t.sol @@ -20,6 +20,8 @@ abstract contract IntegrationBase is IntegrationDeployer { using Strings for *; using print for *; + using ArrayLib for IStrategy[]; + uint numStakers = 0; uint numOperators = 0; uint numAVSs = 0; @@ -890,6 +892,30 @@ abstract contract IntegrationBase is IntegrationDeployer { function _randWadToSlash() internal returns (uint) { return _randUint({ min: 0.01 ether, max: 1 ether }); } + + function _randStrategiesAndWadsToSlash( + OperatorSet memory operatorSet + ) internal returns (IStrategy[] memory strategies, uint[] memory wadsToSlash) { + // Get list of all strategies in an operator set. + strategies = allocationManager.getStrategiesInOperatorSet(operatorSet); + + // Randomly select a subset of strategies to slash. + uint len = _randUint({ min: 1, max: strategies.length }); + + // Update length of strategies array. + assembly { + mstore(strategies, len) + } + + wadsToSlash = new uint[](len); + + // Randomly select a `wadToSlash` for each strategy. + for (uint i; i < len; ++i) { + wadsToSlash[i] = _randWadToSlash(); + } + + return (strategies.sort(), wadsToSlash); + } function _randMagnitudes(uint64 sum, uint256 len) internal returns (uint64[] memory magnitudes) { magnitudes = new uint64[](len); diff --git a/src/test/integration/IntegrationDeployer.t.sol b/src/test/integration/IntegrationDeployer.t.sol index 0e8cd6c72..0c9509923 100644 --- a/src/test/integration/IntegrationDeployer.t.sol +++ b/src/test/integration/IntegrationDeployer.t.sol @@ -46,6 +46,8 @@ uint8 constant PAUSED_NON_PROOF_WITHDRAWALS = 5; abstract contract IntegrationDeployer is ExistingDeploymentParser, Logger { using StdStyle for *; + using ArrayLib for *; + using ArrayLib for IStrategy[]; // Fork ids for specific fork tests bool isUpgraded; diff --git a/src/test/integration/tests/Deposit_Delegate_Allocate.t.sol b/src/test/integration/tests/Deposit_Delegate_Allocate.t.sol index ee3958dcf..f9964c67a 100644 --- a/src/test/integration/tests/Deposit_Delegate_Allocate.t.sol +++ b/src/test/integration/tests/Deposit_Delegate_Allocate.t.sol @@ -38,7 +38,8 @@ contract Integration_Deposit_Delegate_Allocate is IntegrationCheckUtils { for (uint i; i < operatorSets.length; ++i) { uint256 len = allocationManager.getStrategiesInOperatorSet(operatorSets[i]).length; operator.modifyAllocations(operatorSets[i], _randMagnitudes({ sum: 1 ether / uint64(operatorSets.length), len: len })); - avs.slashOperator(operator, operatorSets[i].id, _randWadToSlash()); + (IStrategy[] memory strategiesToSlash, uint[] memory wadsToSlash) = _randStrategiesAndWadsToSlash(operatorSets[i]); + avs.slashOperator(operator, operatorSets[i].id, strategiesToSlash, wadsToSlash); } // TODO: write checks for slashing... diff --git a/src/test/integration/users/AVS.t.sol b/src/test/integration/users/AVS.t.sol index 1678762e8..643a0693f 100644 --- a/src/test/integration/users/AVS.t.sol +++ b/src/test/integration/users/AVS.t.sol @@ -11,7 +11,7 @@ import "src/test/integration/users/User.t.sol"; import "src/test/integration/TimeMachine.t.sol"; import "src/test/utils/Logger.t.sol"; -import "src/test/utils/SingleItemArrayLib.sol"; +import "src/test/utils/ArrayLib.sol"; import "src/contracts/interfaces/IAVSRegistrar.sol"; interface IAVSDeployer { @@ -22,7 +22,9 @@ interface IAVSDeployer { contract AVS is Logger, IAllocationManagerTypes, IAVSRegistrar { using print for *; - using SingleItemArrayLib for *; + using ArrayLib for *; + + IStrategy constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); AllocationManager immutable allocationManager; StrategyFactory immutable strategyFactory; @@ -88,27 +90,36 @@ contract AVS is Logger, IAllocationManagerTypes, IAVSRegistrar { allocationManager.createOperatorSets(address(this), p); } - function slashOperator(User operator, uint32 operatorSetId, uint256 wadToSlash) public createSnapshot { - print.method( - "slashOperator", - string.concat( - "{operator: ", - operator.NAME_COLORED(), - ", operatorSetId: ", - cheats.toString(operatorSetId), - ", wadToSlash: ", - wadToSlash.asWad(), - "}" - ) - ); - + function slashOperator(User operator, uint32 operatorSetId, IStrategy[] memory strategies, uint256[] memory wadsToSlash) public createSnapshot { SlashingParams memory p = SlashingParams({ operator: address(operator), operatorSetId: operatorSetId, - wadToSlash: wadToSlash, + strategies: strategies, + wadsToSlash: wadsToSlash, description: "bad operator" }); + for (uint256 i; i < strategies.length; ++i) { + string memory strategyName = strategies[i] == beaconChainETHStrategy ? + "Native ETH" : + IERC20Metadata(address(strategies[i].underlyingToken())).name(); + + print.method( + "slashOperator", + string.concat( + "{operator: ", + operator.NAME_COLORED(), + ", operatorSetId: ", + cheats.toString(operatorSetId), + ", strategy: ", + strategyName, + ", wadToSlash: ", + wadsToSlash[i].asWad(), + "}" + ) + ); + } + allocationManager.slashOperator(address(this), p); } diff --git a/src/test/integration/users/User.t.sol b/src/test/integration/users/User.t.sol index dd772cf29..205553010 100644 --- a/src/test/integration/users/User.t.sol +++ b/src/test/integration/users/User.t.sol @@ -12,7 +12,7 @@ import "src/contracts/pods/EigenPod.sol"; import "src/test/integration/TimeMachine.t.sol"; import "src/test/integration/mocks/BeaconChainMock.t.sol"; import "src/test/utils/Logger.t.sol"; -import "src/test/utils/SingleItemArrayLib.sol"; +import "src/test/utils/ArrayLib.sol"; struct Validator { uint40 index; @@ -28,7 +28,7 @@ interface IUserDeployer { } contract User is Logger, IDelegationManagerTypes, IAllocationManagerTypes { - using SingleItemArrayLib for *; + using ArrayLib for *; using print for *; AllocationManager allocationManager; diff --git a/src/test/unit/AllocationManagerUnit.t.sol b/src/test/unit/AllocationManagerUnit.t.sol index 9b9016a0c..cfb6b0026 100644 --- a/src/test/unit/AllocationManagerUnit.t.sol +++ b/src/test/unit/AllocationManagerUnit.t.sol @@ -7,7 +7,7 @@ import "src/test/mocks/MockAVSRegistrar.sol"; contract AllocationManagerUnitTests is EigenLayerUnitTestSetup, IAllocationManagerErrors, IAllocationManagerEvents { using StdStyle for *; - using SingleItemArrayLib for *; + using ArrayLib for *; /// ----------------------------------------------------------------------- /// Constants @@ -122,8 +122,7 @@ contract AllocationManagerUnitTests is EigenLayerUnitTestSetup, IAllocationManag ) internal returns (OperatorSet memory) { cheats.prank(operatorSet.avs); allocationManager.createOperatorSets( - operatorSet.avs, - CreateSetParams({operatorSetId: operatorSet.id, strategies: strategies}).toArray() + operatorSet.avs, CreateSetParams({operatorSetId: operatorSet.id, strategies: strategies}).toArray() ); return operatorSet; } @@ -142,8 +141,7 @@ contract AllocationManagerUnitTests is EigenLayerUnitTestSetup, IAllocationManag function _registerForOperatorSet(address operator, OperatorSet memory operatorSet) internal { cheats.prank(operator); allocationManager.registerForOperatorSets( - operator, - RegisterParams({avs: operatorSet.avs, operatorSetIds: operatorSet.id.toArrayU32(), data: ""}) + operator, RegisterParams({avs: operatorSet.avs, operatorSetIds: operatorSet.id.toArrayU32(), data: ""}) ); } @@ -245,11 +243,11 @@ contract AllocationManagerUnitTests is EigenLayerUnitTestSetup, IAllocationManag address operator, OperatorSet memory operatorSet, IStrategy[] memory strategies, - uint256[] memory wadToSlash, + uint256[] memory wadsToSlash, string memory description ) internal { cheats.expectEmit(true, false, false, false, address(allocationManager)); - emit OperatorSlashed(operator, operatorSet, strategies, wadToSlash, description); + emit OperatorSlashed(operator, operatorSet, strategies, wadsToSlash, description); } /// ----------------------------------------------------------------------- @@ -434,7 +432,7 @@ contract AllocationManagerUnitTests_Initialization_Setters is AllocationManagerU } contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; using SlashingLib for *; /// ----------------------------------------------------------------------- @@ -445,7 +443,8 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests return SlashingParams({ operator: operator, operatorSetId: operatorSetId, - wadToSlash: random().Uint256(1, WAD), + strategies: defaultStrategies, + wadsToSlash: random().Uint256(1, WAD).toArrayU256(), description: "test" }); } @@ -458,7 +457,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests function test_revert_slashZero() public { SlashingParams memory slashingParams = _randSlashingParams(defaultOperator, 0); - slashingParams.wadToSlash = 0; + slashingParams.wadsToSlash[0] = 0; cheats.prank(defaultAVS); cheats.expectRevert(InvalidWadToSlash.selector); @@ -467,7 +466,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests function test_revert_slashGreaterThanWAD() public { SlashingParams memory slashingParams = _randSlashingParams(defaultOperator, 0); - slashingParams.wadToSlash = WAD + 1; + slashingParams.wadsToSlash[0] = WAD + 1; cheats.prank(defaultAVS); cheats.expectRevert(InvalidWadToSlash.selector); @@ -480,6 +479,55 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests allocationManager.slashOperator(defaultAVS, _randSlashingParams(random().Address(), 0)); } + function test_revert_StrategiesMustBeInAscendingOrder() public { + IStrategy[] memory strategies = new IStrategy[](3); + strategies[0] = IStrategy(address(3)); + strategies[1] = IStrategy(address(2)); + strategies[2] = IStrategy(address(1)); + + _createOperatorSet(OperatorSet(defaultAVS, 1), strategies); + _registerForOperatorSet(defaultOperator, OperatorSet(defaultAVS, 1)); + + cheats.prank(defaultAVS); + cheats.expectRevert(StrategiesMustBeInAscendingOrder.selector); + allocationManager.slashOperator( + defaultAVS, + SlashingParams({ + operator: defaultOperator, + operatorSetId: 1, + strategies: strategies, + wadsToSlash: uint256(0.5 ether).toArrayU256(3), + description: "test" + }) + ); + } + + function test_revert_StrategyNotInOperatorSet() public { + IStrategy[] memory strategies = new IStrategy[](3); + strategies[0] = IStrategy(address(1)); + strategies[1] = IStrategy(address(2)); + strategies[2] = IStrategy(address(3)); + + _createOperatorSet(OperatorSet(defaultAVS, 1), strategies); + _registerForOperatorSet(defaultOperator, OperatorSet(defaultAVS, 1)); + + strategies = strategies.setLength(4); + strategies[3] = IStrategy(address(4)); + + cheats.prank(defaultAVS); + cheats.expectRevert(StrategyNotInOperatorSet.selector); + allocationManager.slashOperator( + defaultAVS, + SlashingParams({ + operator: defaultOperator, + operatorSetId: 1, + strategies: defaultStrategies, + wadsToSlash: uint256(0.5 ether).toArrayU256(), + description: "test" + }) + ); + } + function test_revert_operatorAllocated_notActive() public { AllocateParams[] memory allocateParams = _newAllocateParams(defaultOperatorSet, WAD); @@ -488,11 +536,12 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests cheats.prank(defaultAVS); allocationManager.slashOperator( - defaultAVS, + defaultAVS, SlashingParams({ operator: defaultOperator, operatorSetId: allocateParams[0].operatorSet.id, - wadToSlash: WAD, + strategies: defaultStrategies, + wadsToSlash: WAD.toArrayU256(), description: "test" }) ); @@ -545,7 +594,8 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests SlashingParams({ operator: defaultOperator, operatorSetId: defaultOperatorSet.id, - wadToSlash: 25e16, + strategies: defaultStrategies, + wadsToSlash: 25e16.toArrayU256(), description: "test" }) ); @@ -595,7 +645,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests uint64 allocatedMagnitude = allocateParams[0].newMagnitudes[0]; uint64 expectedSlashedMagnitude = - uint64(SlashingLib.mulWadRoundUp(allocatedMagnitude, slashingParams.wadToSlash)); + uint64(SlashingLib.mulWadRoundUp(allocatedMagnitude, slashingParams.wadsToSlash[0])); uint64 expectedEncumberedMagnitude = allocatedMagnitude - expectedSlashedMagnitude; uint64 maxMagnitudeAfterSlash = WAD - expectedSlashedMagnitude; uint256 slashedStake = DEFAULT_OPERATOR_SHARES.mulWad(expectedSlashedMagnitude); @@ -605,7 +655,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operator: defaultOperator, operatorSet: defaultOperatorSet, strategies: defaultStrategies, - wadToSlash: uint256(expectedSlashedMagnitude).toArrayU256(), + wadsToSlash: uint256(expectedSlashedMagnitude).toArrayU256(), description: "test" }); @@ -695,7 +745,8 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests SlashingParams memory slashingParams = SlashingParams({ operator: defaultOperator, operatorSetId: defaultOperatorSet.id, - wadToSlash: wadToSlash, + strategies: defaultStrategies, + wadsToSlash: wadToSlash.toArrayU256(), description: "test" }); @@ -704,7 +755,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests uint64 magnitudeAfterSlash = totalAllocated - uint64(uint256(totalAllocated) * uint256(wadToSlash) / WAD); uint64 maxMagnitudeAfterSlash = expectedEncumberedMagnitude; - uint64 expectedSlashedMagnitude = uint64(totalAllocated.mulWadRoundUp(slashingParams.wadToSlash)); + uint64 expectedSlashedMagnitude = uint64(totalAllocated.mulWadRoundUp(slashingParams.wadsToSlash[0])); uint256 newSlashableMagnitude = uint256(magnitudeAfterSlash).divWad(maxMagnitudeAfterSlash); uint256 slashedStake = DEFAULT_OPERATOR_SHARES.mulWad(expectedSlashedMagnitude); uint256 newTotalStake = DEFAULT_OPERATOR_SHARES - slashedStake; @@ -714,7 +765,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // // operator: defaultOperator, // // operatorSet: defaultOperatorSet, // // strategies: defaultStrategies, - // // wadToSlash: uint256(wadToSlash).toArrayU256(), + // // wadsToSlash: uint256(wadToSlash).toArrayU256(), // // description: "test" // // }); @@ -812,7 +863,8 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests SlashingParams memory slashingParams = SlashingParams({ operator: defaultOperator, operatorSetId: defaultOperatorSet.id, - wadToSlash: 99e16, + strategies: defaultStrategies, + wadsToSlash: 99e16.toArrayU256(), description: "test" }); @@ -824,7 +876,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operator: defaultOperator, operatorSet: defaultOperatorSet, strategies: defaultStrategies, - wadToSlash: uint256(99e16).toArrayU256(), + wadsToSlash: uint256(99e16).toArrayU256(), description: "test" }); @@ -859,7 +911,8 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests slashingParams = SlashingParams({ operator: defaultOperator, operatorSetId: defaultOperatorSet.id, - wadToSlash: 9999e14, + strategies: defaultStrategies, + wadsToSlash: 9999e14.toArrayU256(), description: "test" }); expectedEncumberedMagnitude = 1e12; // After slashing 99.99%, only 0.01% expected encumberedMagnitude @@ -870,7 +923,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operator: defaultOperator, operatorSet: defaultOperatorSet, strategies: defaultStrategies, - wadToSlash: uint256(9999e14).toArrayU256(), + wadsToSlash: uint256(9999e14).toArrayU256(), description: "test" }); @@ -903,7 +956,8 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests slashingParams = SlashingParams({ operator: defaultOperator, operatorSetId: defaultOperatorSet.id, - wadToSlash: WAD - 1e3, + strategies: defaultStrategies, + wadsToSlash: (WAD - 1e3).toArrayU256(), description: "test" }); // Should technically be 1e3 remaining but with rounding error and rounding up slashed amounts @@ -916,7 +970,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operator: defaultOperator, operatorSet: defaultOperatorSet, strategies: defaultStrategies, - wadToSlash: uint256(WAD - 1e3).toArrayU256(), + wadsToSlash: uint256(WAD - 1e3).toArrayU256(), description: "test" }); @@ -966,7 +1020,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests RegisterParams memory registerParams = r.RegisterParams(allocateParams); SlashingParams memory slashingParams = r.SlashingParams(defaultOperator, allocateParams[0]); - console.log("wadToSlash: %d", slashingParams.wadToSlash); + console.log("wadsToSlash: %d", slashingParams.wadsToSlash[0]); delegationManagerMock.setOperatorShares( defaultOperator, allocateParams[0].strategies[0], DEFAULT_OPERATOR_SHARES @@ -1001,16 +1055,16 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests uint256 magnitudeAllocated = allocateParams[0].newMagnitudes[0]; uint256 magnitudeDeallocated = magnitudeAllocated - deallocateParams[0].newMagnitudes[0]; - uint256 magnitudeSlashed = magnitudeAllocated.mulWad(slashingParams.wadToSlash); + uint256 magnitudeSlashed = magnitudeAllocated.mulWad(slashingParams.wadsToSlash[0]); uint256 expectedCurrentMagnitude = magnitudeAllocated - magnitudeSlashed; int128 expectedPendingDiff = - -int128(uint128(magnitudeDeallocated - magnitudeDeallocated.mulWadRoundUp(slashingParams.wadToSlash))); + -int128(uint128(magnitudeDeallocated - magnitudeDeallocated.mulWadRoundUp(slashingParams.wadsToSlash[0]))); _checkSlashEvents({ operator: defaultOperator, operatorSet: allocateParams[0].operatorSet, strategies: allocateParams[0].strategies, - wadToSlash: slashingParams.wadToSlash.toArrayU256(), + wadsToSlash: slashingParams.wadsToSlash, description: "test" }); @@ -1049,7 +1103,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests "encumberedMagnitude not updated" ); assertEq( - WAD - slashingParams.wadToSlash, + WAD - slashingParams.wadsToSlash[0], allocationManager.getMaxMagnitudes(defaultOperator, allocateParams[0].strategies)[0], "maxMagnitude not updated" ); @@ -1062,7 +1116,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operatorSet: allocateParams[0].operatorSet, strategy: allocateParams[0].strategies[0], expectedCurrentMagnitude: deallocateParams[0].newMagnitudes[0] - - deallocateParams[0].newMagnitudes[0] * slashingParams.wadToSlash / WAD, + - deallocateParams[0].newMagnitudes[0] * slashingParams.wadsToSlash[0] / WAD, expectedPendingDiff: 0, expectedEffectBlock: 0 }); @@ -1092,7 +1146,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operator: defaultOperator, operatorSet: defaultOperatorSet, strategies: defaultStrategies, - wadToSlash: WAD.toArrayU256(), + wadsToSlash: WAD.toArrayU256(), description: "test" }); @@ -1103,7 +1157,8 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests SlashingParams({ operator: defaultOperator, operatorSetId: allocateParams[0].operatorSet.id, - wadToSlash: WAD, + strategies: defaultStrategies, + wadsToSlash: WAD.toArrayU256(), description: "test" }) ); @@ -1138,7 +1193,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operator: defaultOperator, operatorSet: defaultOperatorSet, strategies: defaultStrategies, - wadToSlash: WAD.toArrayU256(), + wadsToSlash: WAD.toArrayU256(), description: "test" }); @@ -1149,7 +1204,8 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests SlashingParams({ operator: defaultOperator, operatorSetId: defaultOperatorSet.id, - wadToSlash: WAD, + strategies: defaultStrategies, + wadsToSlash: WAD.toArrayU256(), description: "test" }) ); @@ -1206,7 +1262,8 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests SlashingParams memory slashingParams = SlashingParams({ operator: defaultOperator, operatorSetId: defaultOperatorSet.id, - wadToSlash: 25e16, + strategies: defaultStrategies, + wadsToSlash: 25e16.toArrayU256(), description: "test" }); @@ -1218,7 +1275,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operator: defaultOperator, operatorSet: defaultOperatorSet, strategies: defaultStrategies, - wadToSlash: uint256(25e16).toArrayU256(), + wadsToSlash: uint256(25e16).toArrayU256(), description: "test" }); @@ -1301,7 +1358,8 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests SlashingParams memory slashingParams = SlashingParams({ operator: defaultOperator, operatorSetId: allocateParams[0].operatorSet.id, - wadToSlash: 5e17, + strategies: defaultStrategies, + wadsToSlash: 5e17.toArrayU256(), description: "test" }); @@ -1309,7 +1367,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operator: defaultOperator, operatorSet: operatorSet, strategies: defaultStrategies, - wadToSlash: uint256(5e17).toArrayU256(), + wadsToSlash: slashingParams.wadsToSlash, description: "test" }); @@ -1369,11 +1427,16 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests allocationManager.modifyAllocations(defaultOperator, allocateParams.toArray()); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); + uint256[] memory wadsToSlash = new uint256[](2); + wadsToSlash[0] = 6e17; + wadsToSlash[1] = 6e17; + // Slash operator on both strategies for 60% SlashingParams memory slashingParams = SlashingParams({ operator: defaultOperator, operatorSetId: operatorSet.id, - wadToSlash: 6e17, + strategies: strategies, + wadsToSlash: wadsToSlash, description: "test" }); @@ -1393,7 +1456,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operator: defaultOperator, operatorSet: operatorSet, strategies: strategies, - wadToSlash: uint256(6e17).toArrayU256(), + wadsToSlash: wadsToSlash, description: "test" }); @@ -1451,12 +1514,13 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests SlashingParams memory slashingParams = SlashingParams({ operator: defaultOperator, operatorSetId: operatorSet.id, - wadToSlash: r.Uint64(0.01 ether, 0.99 ether), + strategies: strategy.toArray(), + wadsToSlash: r.Uint64(0.01 ether, 0.99 ether).toArrayU256(), description: "test" }); uint256 magnitudeBeforeSlash = deallocateParams[0].newMagnitudes[0]; - uint256 slashedMagnitude = magnitudeBeforeSlash * slashingParams.wadToSlash / WAD; + uint256 slashedMagnitude = magnitudeBeforeSlash * slashingParams.wadsToSlash[0] / WAD; uint256 currentMagnitude = magnitudeBeforeSlash - slashedMagnitude - 1; uint256 maxMagnitude = WAD - slashedMagnitude - 1; @@ -1464,7 +1528,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests operator: defaultOperator, operatorSet: operatorSet, strategies: strategy.toArray(), - wadToSlash: uint256(slashingParams.wadToSlash).toArrayU256(), + wadsToSlash: slashingParams.wadsToSlash, description: "test" }); @@ -1514,7 +1578,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests } contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; using OperatorSetLib for *; function test_revert_paused() public { @@ -2543,7 +2607,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe } contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; /// ----------------------------------------------------------------------- /// clearModificationQueue() @@ -2909,7 +2973,7 @@ contract AllocationManagerUnitTests_SetAllocationDelay is AllocationManagerUnitT } contract AllocationManagerUnitTests_registerForOperatorSets is AllocationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; RegisterParams defaultRegisterParams; @@ -2994,7 +3058,7 @@ contract AllocationManagerUnitTests_registerForOperatorSets is AllocationManager } contract AllocationManagerUnitTests_deregisterFromOperatorSets is AllocationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; DeregisterParams defaultDeregisterParams; @@ -3090,7 +3154,7 @@ contract AllocationManagerUnitTests_deregisterFromOperatorSets is AllocationMana } contract AllocationManagerUnitTests_addStrategiesToOperatorSet is AllocationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; function test_addStrategiesToOperatorSet_InvalidOperatorSet() public { cheats.prank(defaultAVS); @@ -3110,21 +3174,17 @@ contract AllocationManagerUnitTests_addStrategiesToOperatorSet is AllocationMana allocationManager.addStrategiesToOperatorSet( defaultAVS, defaultOperatorSet.id, new IStrategy[](MAX_OPERATOR_SET_STRATEGY_LIST_LENGTH + 1) ); - + for (uint256 i; i < MAX_OPERATOR_SET_STRATEGY_LIST_LENGTH - 1; ++i) { allocationManager.addStrategiesToOperatorSet( - defaultAVS, - defaultOperatorSet.id, - IStrategy(cheats.randomAddress()).toArray() + defaultAVS, defaultOperatorSet.id, IStrategy(cheats.randomAddress()).toArray() ); } cheats.expectRevert(MaxStrategiesExceeded.selector); allocationManager.addStrategiesToOperatorSet( - defaultAVS, - defaultOperatorSet.id, - IStrategy(cheats.randomAddress()).toArray() - ); + defaultAVS, defaultOperatorSet.id, IStrategy(cheats.randomAddress()).toArray() + ); } function testFuzz_addStrategiesToOperatorSet_Correctness( @@ -3152,7 +3212,7 @@ contract AllocationManagerUnitTests_addStrategiesToOperatorSet is AllocationMana } contract AllocationManagerUnitTests_removeStrategiesFromOperatorSet is AllocationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; function test_removeStrategiesFromOperatorSet_InvalidOperatorSet() public { cheats.prank(defaultAVS); @@ -3197,23 +3257,22 @@ contract AllocationManagerUnitTests_removeStrategiesFromOperatorSet is Allocatio } contract AllocationManagerUnitTests_createOperatorSets is AllocationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; function testRevert_createOperatorSets_InvalidOperatorSet() public { cheats.prank(defaultAVS); cheats.expectRevert(InvalidOperatorSet.selector); - allocationManager.createOperatorSets(defaultAVS, CreateSetParams(defaultOperatorSet.id, defaultStrategies).toArray()); + allocationManager.createOperatorSets( + defaultAVS, CreateSetParams(defaultOperatorSet.id, defaultStrategies).toArray() + ); } function testRevert_createOperatorSets_MaxStrategiesExceeded() public { cheats.prank(defaultAVS); cheats.expectRevert(MaxStrategiesExceeded.selector); allocationManager.createOperatorSets( - defaultAVS, - CreateSetParams( - defaultOperatorSet.id, - new IStrategy[](MAX_OPERATOR_SET_STRATEGY_LIST_LENGTH + 1) - ).toArray() + defaultAVS, + CreateSetParams(defaultOperatorSet.id, new IStrategy[](MAX_OPERATOR_SET_STRATEGY_LIST_LENGTH + 1)).toArray() ); } @@ -3314,4 +3373,4 @@ contract AllocationManagerUnitTests_getStrategyAllocations is AllocationManagerU expectedEffectBlock: 0 }); } -} \ No newline at end of file +} diff --git a/src/test/unit/DelegationUnit.t.sol b/src/test/unit/DelegationUnit.t.sol index 734253ac2..7baa2e4c6 100644 --- a/src/test/unit/DelegationUnit.t.sol +++ b/src/test/unit/DelegationUnit.t.sol @@ -8,7 +8,7 @@ import "src/contracts/core/DelegationManager.sol"; import "src/contracts/strategies/StrategyBase.sol"; import "src/test/utils/EigenLayerUnitTestSetup.sol"; import "src/contracts/libraries/SlashingLib.sol"; -import "src/test/utils/SingleItemArrayLib.sol"; +import "src/test/utils/ArrayLib.sol"; // TODO: add upgrade tests for completing withdrawals queued before upgrade in integration tests // TODO: add slashing cases for withdrawing as shares (can also be in integration tests) @@ -21,7 +21,7 @@ import "src/test/utils/SingleItemArrayLib.sol"; */ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManagerEvents, IDelegationManagerErrors { using SlashingLib for *; - using SingleItemArrayLib for *; + using ArrayLib for *; // Contract under test DelegationManager delegationManager; @@ -1153,7 +1153,7 @@ contract DelegationManagerUnitTests_RegisterModifyOperator is DelegationManagerU } contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; using SlashingLib for *; function test_Revert_WhenPaused() public { @@ -2497,7 +2497,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { } contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; using SlashingLib for *; /// @notice Verifies that `DelegationManager.increaseDelegatedShares` reverts if not called by the StrategyManager nor EigenPodManager @@ -3271,7 +3271,7 @@ contract DelegationManagerUnitTests_Redelegate is DelegationManagerUnitTests { contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { using SlashingLib for uint256; - using SingleItemArrayLib for *; + using ArrayLib for *; using Math for uint256; // @notice Verifies that undelegating is not possible when the "undelegation paused" switch is flipped @@ -4086,7 +4086,7 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTests { using SlashingLib for *; - using SingleItemArrayLib for *; + using ArrayLib for *; function test_Revert_WhenEnterQueueWithdrawalsPaused() public { cheats.prank(pauser); @@ -4784,7 +4784,7 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes } contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; using SlashingLib for *; using Math for uint256; @@ -5400,7 +5400,7 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage } contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; /** * @notice Test burning shares for an operator with no queued withdrawals @@ -6462,7 +6462,7 @@ contract DelegationManagerUnitTests_SharesUnderflowChecks is DelegationManagerUn */ contract DelegationManagerUnitTests_Lifecycle is DelegationManagerUnitTests { - using SingleItemArrayLib for *; + using ArrayLib for *; // 2. RegisterOperator, Deposit, Delegate, Queue, Complete function test_register_operator_deposit_delegate_queue_complete(Randomness r) public rand(r) { diff --git a/src/test/utils/SingleItemArrayLib.sol b/src/test/utils/ArrayLib.sol similarity index 53% rename from src/test/utils/SingleItemArrayLib.sol rename to src/test/utils/ArrayLib.sol index b0da93810..6ae3d434d 100644 --- a/src/test/utils/SingleItemArrayLib.sol +++ b/src/test/utils/ArrayLib.sol @@ -3,10 +3,13 @@ pragma solidity ^0.8.27; import "src/contracts/interfaces/IAllocationManager.sol"; -/// @dev Helper library for simplifying the syntax for creating single item arrays for inputs. -library SingleItemArrayLib { +library ArrayLib { + using ArrayLib for *; + using ArrayLib for uint256[]; + using ArrayLib for address[]; + /// ----------------------------------------------------------------------- - /// Native Types + /// Single Item Arrays /// ----------------------------------------------------------------------- function toArrayU16( @@ -85,10 +88,6 @@ library SingleItemArrayLib { array[0] = x; } - /// ----------------------------------------------------------------------- - /// EigenLayer Types - /// ----------------------------------------------------------------------- - function toArray( IERC20 token ) internal pure returns (IERC20[] memory array) { @@ -123,4 +122,120 @@ library SingleItemArrayLib { array = new IAllocationManagerTypes.AllocateParams[](1); array[0] = allocateParams; } + + /// ----------------------------------------------------------------------- + /// Sorting + /// ----------------------------------------------------------------------- + + function sort( + IStrategy[] memory array + ) internal pure returns (IStrategy[] memory) { + if (array.length <= 1) { + return array; + } + + for (uint256 i = 1; i < array.length; i++) { + IStrategy key = array[i]; + uint256 j = i - 1; + + while (j > 0 && uint256(uint160(address(array[j]))) > uint256(uint160(address(key)))) { + array[j + 1] = array[j]; + j--; + } + + // Special case for the first element + if (j == 0 && uint256(uint160(address(array[j]))) > uint256(uint160(address(key)))) { + array[j + 1] = array[j]; + array[j] = key; + } else if (j < i - 1) { + array[j + 1] = key; + } + } + + return array; + } + + /// ----------------------------------------------------------------------- + /// Length Updates + /// ----------------------------------------------------------------------- + + function setLength( + uint16[] memory array, + uint256 len + ) internal pure returns (uint16[] memory) { + assembly { + mstore(array, len) + } + return array; + } + + function setLength( + uint32[] memory array, + uint256 len + ) internal pure returns (uint32[] memory) { + assembly { + mstore(array, len) + } + return array; + } + + function setLength( + uint64[] memory array, + uint256 len + ) internal pure returns (uint64[] memory) { + assembly { + mstore(array, len) + } + return array; + } + + function setLength( + uint256[] memory array, + uint256 len + ) internal pure returns (uint256[] memory) { + assembly { + mstore(array, len) + } + return array; + } + + function setLength( + address[] memory array, + uint256 len + ) internal pure returns (address[] memory) { + assembly { + mstore(array, len) + } + return array; + } + + function setLength( + IERC20[] memory array, + uint256 len + ) internal pure returns (IERC20[] memory) { + assembly { + mstore(array, len) + } + return array; + } + + function setLength( + IStrategy[] memory array, + uint256 len + ) internal pure returns (IStrategy[] memory) { + assembly { + mstore(array, len) + } + return array; + } + + function setLength( + OperatorSet[] memory array, + uint256 len + ) internal pure returns (OperatorSet[] memory) { + assembly { + mstore(array, len) + } + return array; + } } \ No newline at end of file diff --git a/src/test/utils/EigenLayerUnitTestSetup.sol b/src/test/utils/EigenLayerUnitTestSetup.sol index 15c3941ae..a8ac0e187 100644 --- a/src/test/utils/EigenLayerUnitTestSetup.sol +++ b/src/test/utils/EigenLayerUnitTestSetup.sol @@ -18,13 +18,13 @@ import "src/test/mocks/DelegationManagerMock.sol"; import "src/test/mocks/EigenPodManagerMock.sol"; import "src/test/mocks/EmptyContract.sol"; -import "src/test/utils/SingleItemArrayLib.sol"; +import "src/test/utils/ArrayLib.sol"; import "src/test/utils/Random.sol"; -import "src/test/utils/SingleItemArrayLib.sol"; +import "src/test/utils/ArrayLib.sol"; abstract contract EigenLayerUnitTestSetup is Test { - using SingleItemArrayLib for *; + using ArrayLib for *; uint256 internal constant MAX_PRIVATE_KEY = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140; diff --git a/src/test/utils/Random.sol b/src/test/utils/Random.sol index 62a4361ee..8023206ad 100644 --- a/src/test/utils/Random.sol +++ b/src/test/utils/Random.sol @@ -296,10 +296,26 @@ library Random { address operator, IAllocationManagerTypes.AllocateParams memory allocateParams ) internal returns (IAllocationManagerTypes.SlashingParams memory params) { + IStrategy[] memory strategies = allocateParams.strategies; + params.operator = operator; params.operatorSetId = allocateParams.operatorSet.id; - params.wadToSlash = r.Uint256(0.01 ether, 1 ether); - params.description = "test"; + + // Randomly select a subset of strategies to slash. + uint len = r.Uint256({ min: 1, max: strategies.length }); + + // Update length of strategies array. + assembly { + mstore(strategies, len) + } + + params.strategies = strategies; + params.wadsToSlash = new uint[](len); + + // Randomly select a `wadToSlash` for each strategy. + for (uint i; i < len; ++i) { + params.wadsToSlash[i] = r.Uint256({ min: 0.001 ether, max: 1 ether }); + } } /// -----------------------------------------------------------------------