Skip to content

Commit 999b54b

Browse files
committed
fix: require slasher to be zero address
1 parent 5b8a9d0 commit 999b54b

File tree

4 files changed

+71
-55
lines changed

4 files changed

+71
-55
lines changed

src/contracts/core/AllocationManager.sol

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ contract AllocationManager is
269269
}
270270

271271
/// @inheritdoc IAllocationManager
272+
/// @notice This function will be deprecated in Early Q2 2026 in favor of `createOperatorSets` which takes in `CreateSetParamsV2`
272273
function createOperatorSets(address avs, CreateSetParams[] calldata params) external checkCanCall(avs) {
273274
createOperatorSets(avs, _convertCreateSetParams(params, avs));
274275
}
@@ -281,6 +282,16 @@ contract AllocationManager is
281282
}
282283
}
283284

285+
/// @inheritdoc IAllocationManager
286+
/// @notice This function will be deprecated in Early Q2 2026 in favor of `createRedistributingOperatorSets` which takes in `CreateSetParamsV2`
287+
function createRedistributingOperatorSets(
288+
address avs,
289+
CreateSetParams[] calldata params,
290+
address[] calldata redistributionRecipients
291+
) external checkCanCall(avs) {
292+
createRedistributingOperatorSets(avs, _convertCreateSetParams(params, avs), redistributionRecipients);
293+
}
294+
284295
/// @inheritdoc IAllocationManager
285296
function createRedistributingOperatorSets(
286297
address avs,
@@ -297,15 +308,6 @@ contract AllocationManager is
297308
}
298309
}
299310

300-
/// @inheritdoc IAllocationManager
301-
function createRedistributingOperatorSets(
302-
address avs,
303-
CreateSetParams[] calldata params,
304-
address[] calldata redistributionRecipients
305-
) external checkCanCall(avs) {
306-
createRedistributingOperatorSets(avs, _convertCreateSetParams(params, avs), redistributionRecipients);
307-
}
308-
309311
/// @inheritdoc IAllocationManager
310312
function addStrategiesToOperatorSet(
311313
address avs,
@@ -338,9 +340,12 @@ contract AllocationManager is
338340
}
339341

340342
/// @inheritdoc IAllocationManager
341-
function setSlasher(OperatorSet memory operatorSet, address slasher) external checkCanCall(operatorSet.avs) {
343+
function updateSlasher(OperatorSet memory operatorSet, address slasher) external checkCanCall(operatorSet.avs) {
342344
require(_operatorSets[operatorSet.avs].contains(operatorSet.id), InvalidOperatorSet());
343-
_setSlasher({operatorSet: operatorSet, slasher: slasher, instantEffectBlock: false});
345+
// Prevent updating a slasher if one is not already set
346+
// A slasher is set either on operatorSet creation or, for operatorSets created prior to v1.9.0, via `migrateSlashers`
347+
require(getSlasher(operatorSet) != address(0), SlasherNotSet());
348+
_updateSlasher({operatorSet: operatorSet, slasher: slasher, instantEffectBlock: false});
344349
}
345350

346351
/// @inheritdoc IAllocationManager
@@ -371,7 +376,7 @@ contract AllocationManager is
371376
slasher = slashers[0];
372377
}
373378

374-
_setSlasher({operatorSet: operatorSets[i], slasher: slasher, instantEffectBlock: true});
379+
_updateSlasher({operatorSet: operatorSets[i], slasher: slasher, instantEffectBlock: true});
375380
}
376381
}
377382

@@ -528,7 +533,7 @@ contract AllocationManager is
528533
}
529534

530535
// Update the slasher for the operator set
531-
_setSlasher({operatorSet: operatorSet, slasher: params.slasher, instantEffectBlock: true});
536+
_updateSlasher({operatorSet: operatorSet, slasher: params.slasher, instantEffectBlock: true});
532537
}
533538

534539
/**
@@ -772,9 +777,9 @@ contract AllocationManager is
772777
* @param operatorSet the operator set to update the slasher for
773778
* @param slasher the new slasher
774779
* @param instantEffectBlock Whether the new slasher will take effect immediately. Instant if on operatorSet creation or migration function.
775-
* The new slasher will take `ALLOCATION_CONFIGURATION_DELAY` blocks to take effect if called by the `setSlasher` function.
780+
* The new slasher will take `ALLOCATION_CONFIGURATION_DELAY` blocks to take effect if called by the `updateSlasher` function.
776781
*/
777-
function _setSlasher(OperatorSet memory operatorSet, address slasher, bool instantEffectBlock) internal {
782+
function _updateSlasher(OperatorSet memory operatorSet, address slasher, bool instantEffectBlock) internal {
778783
// Ensure that the slasher address is not the 0 address, which is used to denote if the slasher is not set
779784
require(slasher != address(0), InputAddressZero());
780785

src/contracts/interfaces/IAllocationManager.sol

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ interface IAllocationManagerErrors {
6363
error ModificationAlreadyPending();
6464
/// @dev Thrown when an allocation is attempted that exceeds a given operators total allocatable magnitude.
6565
error InsufficientMagnitude();
66+
67+
/// SlasherStatus
68+
69+
/// @dev Thrown when an operator set does not have a slasher set
70+
error SlasherNotSet();
6671
}
6772

6873
interface IAllocationManagerTypes {
@@ -373,6 +378,7 @@ interface IAllocationManager is IAllocationManagerErrors, IAllocationManagerEven
373378
* @notice Allows an AVS to create new operator sets, defining strategies that the operator set uses
374379
* @dev Upon creation, the address that can slash the operatorSet is the `avs` address. If you would like to use a different address,
375380
* use the `createOperatorSets` method which takes in `CreateSetParamsV2` instead.
381+
* @dev THIS FUNCTION WILL BE DEPRECATED IN EARLY Q2 2026 IN FAVOR OF `createOperatorSets` WHICH TAKES IN `CreateSetParamsV2`
376382
*/
377383
function createOperatorSets(address avs, CreateSetParams[] calldata params) external;
378384

@@ -390,6 +396,7 @@ interface IAllocationManager is IAllocationManagerErrors, IAllocationManagerEven
390396
* Additionally, emits `RedistributionOperatorSetCreated` event instead of `OperatorSetCreated` for each created operator set.
391397
* @dev The address that can slash the operatorSet is the `avs` address. If you would like to use a different address,
392398
* use the `createOperatorSets` method which takes in `CreateSetParamsV2` instead.
399+
* @dev THIS FUNCTION WILL BE DEPRECATED IN EARLY Q2 2026 IN FAVOR OF `createRedistributingOperatorSets` WHICH TAKES IN `CreateSetParamsV2`
393400
*/
394401
function createRedistributingOperatorSets(
395402
address avs,
@@ -439,11 +446,14 @@ interface IAllocationManager is IAllocationManagerErrors, IAllocationManagerEven
439446
* @param operatorSet the operator set to update the slasher for
440447
* @param slasher the new slasher
441448
* @dev The new slasher will take effect in DEALLOCATION_DELAY blocks
449+
* @dev The slasher can only be updated if it has already been set. The slasher is set either on operatorSet creation or,
450+
* for operatorSets created prior to v1.9.0, via `migrateSlashers`
442451
* @dev Reverts for:
443-
* - InvalidOperatorSet: The operator set does not exist
444452
* - InvalidCaller: The caller cannot update the slasher for the operator set
453+
* - InvalidOperatorSet: The operator set does not exist
454+
* - SlasherNotSet: The slasher has not been set yet
445455
*/
446-
function setSlasher(OperatorSet memory operatorSet, address slasher) external;
456+
function updateSlasher(OperatorSet memory operatorSet, address slasher) external;
447457

448458
/**
449459
* @notice Allows any address to migrate the slasher from the permission controller to the ALM
@@ -453,7 +463,7 @@ interface IAllocationManager is IAllocationManagerErrors, IAllocationManagerEven
453463
* @dev A migration can only be called once for a given operatorSet
454464
* @dev This function does not revert, it will no-op if:
455465
* - The operator set does not exist
456-
* - The operator set has already been migrated
466+
* - The slasherhas already been set, either via migration or creation of the operatorSet
457467
*/
458468
function migrateSlashers(
459469
OperatorSet[] memory operatorSets

src/test/harnesses/AllocationManagerHarness.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ contract AllocationManagerHarness is AllocationManager {
3131
return deallocationQueue[operator][strategy].at(index);
3232
}
3333

34-
function setSlasherZero(OperatorSet memory operatorSet) external {
34+
function setSlasherToZero(OperatorSet memory operatorSet) external {
3535
_slashers[operatorSet.key()] = SlasherParams(address(0), address(0), 0);
3636
}
3737
}

src/test/unit/AllocationManagerUnit.t.sol

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1806,7 +1806,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests
18061806
// Update the slasher
18071807
address appointee1 = address(0x1);
18081808
cheats.prank(defaultAVS);
1809-
allocationManager.setSlasher(defaultOperatorSet, appointee1);
1809+
allocationManager.updateSlasher(defaultOperatorSet, appointee1);
18101810

18111811
// Warp to just before the effect block of the slasher - fail to slash
18121812
cheats.roll(block.number + ALLOCATION_CONFIGURATION_DELAY);
@@ -4224,9 +4224,9 @@ contract AllocationManagerUnitTests_createRedistributingOperatorSetsV2 is Alloca
42244224
}
42254225
}
42264226

4227-
contract AllocationManagerUnitTests_SetSlasher is AllocationManagerUnitTests, IPermissionControllerErrors {
4227+
contract AllocationManagerUnitTests_updateSlasher is AllocationManagerUnitTests, IPermissionControllerErrors {
42284228
/// -----------------------------------------------------------------------
4229-
/// setSlasher() + getSlasher() + getPendingSlasher()
4229+
/// updateSlasher() + getSlasher() + getPendingSlasher()
42304230
/// -----------------------------------------------------------------------
42314231

42324232
/// @dev Thrown when the caller is not allowed to call a function on behalf of an account.
@@ -4242,13 +4242,36 @@ contract AllocationManagerUnitTests_SetSlasher is AllocationManagerUnitTests, IP
42424242

42434243
cheats.prank(caller);
42444244
cheats.expectRevert(InvalidPermissions.selector);
4245-
allocationManager.setSlasher(defaultOperatorSet, r.Address());
4245+
allocationManager.updateSlasher(defaultOperatorSet, r.Address());
4246+
}
4247+
4248+
function test_revert_invalidOperatorSet() public {
4249+
cheats.prank(defaultAVS);
4250+
cheats.expectRevert(InvalidOperatorSet.selector);
4251+
allocationManager.updateSlasher(OperatorSet(defaultAVS, 1), defaultAVS);
4252+
}
4253+
4254+
function test_revert_slasherNotSet() public {
4255+
cheats.prank(defaultAVS);
4256+
4257+
// Zero out the slasher address
4258+
allocationManager.setSlasherToZero(defaultOperatorSet);
4259+
4260+
cheats.prank(defaultAVS);
4261+
cheats.expectRevert(SlasherNotSet.selector);
4262+
allocationManager.updateSlasher(defaultOperatorSet, defaultAVS);
4263+
}
4264+
4265+
function test_revert_slasherZeroAddress() public {
4266+
cheats.prank(defaultAVS);
4267+
cheats.expectRevert(IPausable.InputAddressZero.selector);
4268+
allocationManager.updateSlasher(defaultOperatorSet, address(0));
42464269
}
42474270

42484271
function test_slasher_boundary(Randomness r) public rand(r) {
42494272
address slasher = r.Address();
42504273
cheats.prank(defaultAVS);
4251-
allocationManager.setSlasher(defaultOperatorSet, slasher);
4274+
allocationManager.updateSlasher(defaultOperatorSet, slasher);
42524275
uint32 effectBlock = uint32(block.number + ALLOCATION_CONFIGURATION_DELAY + 1);
42534276

42544277
// Warp to the allocation config delay - the slasher should still be the defaultAVS and the pending slasher should be the new slasher
@@ -4266,15 +4289,15 @@ contract AllocationManagerUnitTests_SetSlasher is AllocationManagerUnitTests, IP
42664289
assertEq(returnedEffectBlock, 0, "effect block should be 0");
42674290
}
42684291

4269-
function testFuzz_setSlasher(Randomness r) public rand(r) {
4292+
function testFuzz_updateSlasher(Randomness r) public rand(r) {
42704293
address slasher = r.Address();
42714294

42724295
// Set slasher
42734296
cheats.expectEmit(true, true, true, true, address(allocationManager));
42744297
uint32 effectBlock = uint32(block.number + ALLOCATION_CONFIGURATION_DELAY + 1);
42754298
emit SlasherUpdated(defaultOperatorSet, slasher, effectBlock);
42764299
cheats.prank(defaultAVS);
4277-
allocationManager.setSlasher(defaultOperatorSet, slasher);
4300+
allocationManager.updateSlasher(defaultOperatorSet, slasher);
42784301

42794302
// Check values after set
42804303
(address returnedSlasher) = allocationManager.getSlasher(defaultOperatorSet);
@@ -4294,13 +4317,13 @@ contract AllocationManagerUnitTests_SetSlasher is AllocationManagerUnitTests, IP
42944317
assertEq(returnedEffectBlock, 0, "effect block should be 0");
42954318
}
42964319

4297-
function test_fuzz_setSlasher_multipleTimesWithinConfigurationDelay(Randomness r) public rand(r) {
4320+
function test_fuzz_updateSlasher_multipleTimesWithinConfigurationDelay(Randomness r) public rand(r) {
42984321
address firstSlasher = r.Address();
42994322
address secondSlasher = r.Address();
43004323

43014324
// Set slasher
43024325
cheats.prank(defaultAVS);
4303-
allocationManager.setSlasher(defaultOperatorSet, firstSlasher);
4326+
allocationManager.updateSlasher(defaultOperatorSet, firstSlasher);
43044327
(address pendingSlasher,) = allocationManager.getPendingSlasher(defaultOperatorSet);
43054328
assertEq(pendingSlasher, firstSlasher, "pending slasher should be the first slasher");
43064329

@@ -4312,7 +4335,7 @@ contract AllocationManagerUnitTests_SetSlasher is AllocationManagerUnitTests, IP
43124335
uint32 secondSlasherEffectBlock = uint32(block.number + ALLOCATION_CONFIGURATION_DELAY + 1);
43134336
emit SlasherUpdated(defaultOperatorSet, secondSlasher, secondSlasherEffectBlock);
43144337
cheats.prank(defaultAVS);
4315-
allocationManager.setSlasher(defaultOperatorSet, secondSlasher);
4338+
allocationManager.updateSlasher(defaultOperatorSet, secondSlasher);
43164339

43174340
// Warp to effect block of first slasher
43184341
cheats.roll(block.number + 1);
@@ -4336,14 +4359,14 @@ contract AllocationManagerUnitTests_SetSlasher is AllocationManagerUnitTests, IP
43364359

43374360
// Set Slasher
43384361
cheats.prank(defaultAVS);
4339-
allocationManager.setSlasher(defaultOperatorSet, firstSlasher);
4362+
allocationManager.updateSlasher(defaultOperatorSet, firstSlasher);
43404363

43414364
// Warp to effect block of first slasher
43424365
cheats.roll(block.number + ALLOCATION_CONFIGURATION_DELAY + 1);
43434366

43444367
// Set slasher again
43454368
cheats.prank(defaultAVS);
4346-
allocationManager.setSlasher(defaultOperatorSet, secondSlasher);
4369+
allocationManager.updateSlasher(defaultOperatorSet, secondSlasher);
43474370

43484371
// Assert that first slasher is the current slasher
43494372
(address returnedSlasher) = allocationManager.getSlasher(defaultOperatorSet);
@@ -4374,7 +4397,7 @@ contract AllocationManagerUnitTests_migrateSlashers is AllocationManagerUnitTest
43744397

43754398
// Manually set the slasher of the defaultAVS to be address(0)
43764399
// Given that the slasher is already set to the defaultAVS, we need to manually update so that the `migrateSlashers` function will not noop
4377-
allocationManager.setSlasherZero(defaultOperatorSet);
4400+
allocationManager.setSlasherToZero(defaultOperatorSet);
43784401
}
43794402

43804403
function test_noop_invalidOperatorSet() public {
@@ -4492,28 +4515,6 @@ contract AllocationManagerUnitTests_migrateSlashers is AllocationManagerUnitTest
44924515
_assertNothingPending(defaultOperatorSet);
44934516
}
44944517

4495-
/**
4496-
* @notice Test for when an AVS sets the slasher first and then a migration occurs
4497-
* @dev This is a known race condition. Migration will take precedence over setting a slasher
4498-
* via `setSlasher`. The operatorSet should wait until the migration is complete before setting a slasher.
4499-
*/
4500-
function test_setSlasher_migrate() public {
4501-
// Set the slasher
4502-
cheats.prank(defaultAVS);
4503-
allocationManager.setSlasher(defaultOperatorSet, appointee1);
4504-
4505-
// Set the slasher in the permission controller
4506-
cheats.prank(defaultAVS);
4507-
permissionController.setAppointee(defaultAVS, appointee2, address(allocationManager), allocationManager.slashOperator.selector);
4508-
4509-
// Migrate the slasher
4510-
allocationManager.migrateSlashers(defaultOperatorSet.toArray());
4511-
4512-
// The slasher should be set to the second appointee
4513-
assertEq(allocationManager.getSlasher(defaultOperatorSet), appointee2, "slasher should be the second appointee");
4514-
_assertNothingPending(defaultOperatorSet);
4515-
}
4516-
45174518
function testFuzz_migrateSlashers_Correctness(Randomness r) public rand(r) {
45184519
address avs = r.Address();
45194520
uint numOpSets = r.Uint256(1, FUZZ_MAX_OP_SETS);
@@ -4536,7 +4537,7 @@ contract AllocationManagerUnitTests_migrateSlashers is AllocationManagerUnitTest
45364537

45374538
// Set slashers to zero address on all previously create opSets so we can migrate them
45384539
for (uint i = 0; i < numOpSets; ++i) {
4539-
allocationManager.setSlasherZero(operatorSets[i]);
4540+
allocationManager.setSlasherToZero(operatorSets[i]);
45404541
}
45414542

45424543
// Expect event emits

0 commit comments

Comments
 (0)