@@ -4299,6 +4299,206 @@ contract AllocationManagerUnitTests_SetSlasher is AllocationManagerUnitTests, IP
42994299 }
43004300}
43014301
4302+ contract AllocationManagerUnitTests_migrateSlashers is AllocationManagerUnitTests {
4303+ using ArrayLib for * ;
4304+
4305+ // Test appointees
4306+ address appointee1 = address (0x1 );
4307+ address appointee2 = address (0x2 );
4308+
4309+ function _assertNothingPending (OperatorSet memory operatorSet ) internal view {
4310+ (address returnedPendingSlasher , uint32 returnedEffectBlock ) = allocationManager.getPendingSlasher (operatorSet);
4311+ assertEq (returnedPendingSlasher, address (0 ), "pending slasher should be the 0 address " );
4312+ assertEq (returnedEffectBlock, 0 , "effect block should be 0 " );
4313+ }
4314+
4315+ function setUp () public override {
4316+ AllocationManagerUnitTests.setUp ();
4317+
4318+ // Manually set the slasher of the defaultAVS to be address(0)
4319+ // Given that the slasher is already set to the defaultAVS, we need to manually update so that the `migrateSlashers` function will not noop
4320+ allocationManager.setSlasherZero (defaultOperatorSet);
4321+ }
4322+
4323+ function test_noop_invalidOperatorSet () public {
4324+ OperatorSet memory operatorSet = OperatorSet (defaultAVS, 1 );
4325+
4326+ // Start recording
4327+ vm.record ();
4328+ allocationManager.migrateSlashers (operatorSet.toArray ());
4329+
4330+ (bytes32 [] memory reads ,) = vm.accesses (address (allocationManager));
4331+ assertEq (reads.length , 3 , "should have 3 reads " );
4332+ }
4333+
4334+ function test_noop_slasherAlreadySet () public {
4335+ // Register the operatorSet
4336+ OperatorSet memory operatorSet = OperatorSet (defaultAVS, 1 );
4337+ CreateSetParams[] memory createSetParams = new CreateSetParams [](1 );
4338+ createSetParams[0 ] = CreateSetParams (operatorSet.id, new IStrategy [](0 ));
4339+ cheats.prank (defaultAVS);
4340+ allocationManager.createOperatorSets (defaultAVS, createSetParams);
4341+
4342+ // Start recording - in this case the slasher is already set, so we noop after
4343+ vm.record ();
4344+ allocationManager.migrateSlashers (operatorSet.toArray ());
4345+
4346+ (bytes32 [] memory reads ,) = vm.accesses (address (allocationManager));
4347+ assertEq (reads.length , 5 , "should have 5 reads " );
4348+ }
4349+
4350+ function test_noSlasherInPC () public {
4351+ // Migrate the slasher
4352+ cheats.expectEmit (true , true , true , true , address (allocationManager));
4353+ emit SlasherUpdated (defaultOperatorSet, defaultAVS, uint32 (block .number ));
4354+ vm.record ();
4355+ allocationManager.migrateSlashers (defaultOperatorSet.toArray ());
4356+
4357+ // Sanity check on number of reads (greater than previous test)
4358+ (bytes32 [] memory reads ,) = vm.accesses (address (allocationManager));
4359+ assertGt (reads.length , 5 , "should have greater than 5 reads " );
4360+
4361+ // Check that the slasher is set to the defaultAVS
4362+ assertEq (allocationManager.getSlasher (defaultOperatorSet), defaultAVS, "slasher should be the defaultAVS " );
4363+ _assertNothingPending (defaultOperatorSet);
4364+ }
4365+
4366+ function test_zeroAddressInPC () public {
4367+ // Add an appointee for the zero address
4368+ cheats.prank (defaultAVS);
4369+ permissionController.setAppointee (defaultAVS, address (0 ), address (allocationManager), allocationManager.slashOperator.selector );
4370+
4371+ // Migrate the slasher
4372+ cheats.expectEmit (true , true , true , true , address (allocationManager));
4373+ emit SlasherUpdated (defaultOperatorSet, defaultAVS, uint32 (block .number ));
4374+ allocationManager.migrateSlashers (defaultOperatorSet.toArray ());
4375+
4376+ // Check that the slasher is set to the defaultAVS
4377+ assertEq (allocationManager.getSlasher (defaultOperatorSet), defaultAVS, "slasher should be the defaultAVS " );
4378+ _assertNothingPending (defaultOperatorSet);
4379+ }
4380+
4381+ function test_multipleAppointees () public {
4382+ // Add two appointees
4383+ cheats.startPrank (defaultAVS);
4384+ permissionController.setAppointee (defaultAVS, appointee1, address (allocationManager), allocationManager.slashOperator.selector );
4385+ permissionController.setAppointee (defaultAVS, appointee2, address (allocationManager), allocationManager.slashOperator.selector );
4386+ cheats.stopPrank ();
4387+
4388+ // Migrate the slasher - only the first appointee should be set
4389+ cheats.expectEmit (true , true , true , true , address (allocationManager));
4390+ emit SlasherUpdated (defaultOperatorSet, appointee1, uint32 (block .number ));
4391+ allocationManager.migrateSlashers (defaultOperatorSet.toArray ());
4392+
4393+ // Check that the slasher is set to the first appointee
4394+ assertEq (allocationManager.getSlasher (defaultOperatorSet), appointee1, "slasher should be the first appointee " );
4395+ _assertNothingPending (defaultOperatorSet);
4396+ }
4397+
4398+ /// @notice Same as previous test, bus since appointee2 is added first, the slasher should be the second appointee
4399+ function test_multipleAppointees_differentOrder () public {
4400+ // Add two appointees
4401+ cheats.startPrank (defaultAVS);
4402+ permissionController.setAppointee (defaultAVS, appointee2, address (allocationManager), allocationManager.slashOperator.selector );
4403+ permissionController.setAppointee (defaultAVS, appointee1, address (allocationManager), allocationManager.slashOperator.selector );
4404+ cheats.stopPrank ();
4405+
4406+ // Migrate the slasher - only the second appointee should be set
4407+ cheats.expectEmit (true , true , true , true , address (allocationManager));
4408+ emit SlasherUpdated (defaultOperatorSet, appointee2, uint32 (block .number ));
4409+ allocationManager.migrateSlashers (defaultOperatorSet.toArray ());
4410+
4411+ // Check that the slasher is set to the second appointee
4412+ assertEq (allocationManager.getSlasher (defaultOperatorSet), appointee2, "slasher should be the second appointee " );
4413+ _assertNothingPending (defaultOperatorSet);
4414+ }
4415+
4416+ function test_cannotMigrateMultipleTimes () public {
4417+ // Migrate the slasher
4418+ allocationManager.migrateSlashers (defaultOperatorSet.toArray ());
4419+ assertEq (allocationManager.getSlasher (defaultOperatorSet), defaultAVS, "slasher should be the defaultAVS " );
4420+
4421+ // Set an appointee for the slasher
4422+ cheats.prank (defaultAVS);
4423+ permissionController.setAppointee (defaultAVS, appointee1, address (allocationManager), allocationManager.slashOperator.selector );
4424+
4425+ // Migrate the slasher again - should noop
4426+ vm.record ();
4427+ allocationManager.migrateSlashers (defaultOperatorSet.toArray ());
4428+
4429+ // Sanity check on number of reads (should be 5)
4430+ (bytes32 [] memory reads ,) = vm.accesses (address (allocationManager));
4431+ assertEq (reads.length , 5 , "should have 5 reads " );
4432+
4433+ // Check that the slasher is still set to the defaultAVS
4434+ assertEq (allocationManager.getSlasher (defaultOperatorSet), defaultAVS, "slasher should be the defaultAVS " );
4435+ _assertNothingPending (defaultOperatorSet);
4436+ }
4437+
4438+ /**
4439+ * @notice Test for when an AVS sets the slasher first and then a migration occurs
4440+ * @dev This is a known race condition. Migration will take precedence over setting a slasher
4441+ * via `setSlasher`. The operatorSet should wait until the migration is complete before setting a slasher.
4442+ */
4443+ function test_setSlasher_migrate () public {
4444+ // Set the slasher
4445+ cheats.prank (defaultAVS);
4446+ allocationManager.setSlasher (defaultOperatorSet, appointee1);
4447+
4448+ // Set the slasher in the permission controller
4449+ cheats.prank (defaultAVS);
4450+ permissionController.setAppointee (defaultAVS, appointee2, address (allocationManager), allocationManager.slashOperator.selector );
4451+
4452+ // Migrate the slasher
4453+ allocationManager.migrateSlashers (defaultOperatorSet.toArray ());
4454+
4455+ // The slasher should be set to the second appointee
4456+ assertEq (allocationManager.getSlasher (defaultOperatorSet), appointee2, "slasher should be the second appointee " );
4457+ _assertNothingPending (defaultOperatorSet);
4458+ }
4459+
4460+ function testFuzz_migrateSlashers_Correctness (Randomness r ) public rand (r) {
4461+ address avs = r.Address ();
4462+ uint numOpSets = r.Uint256 (1 , FUZZ_MAX_OP_SETS);
4463+
4464+ cheats.prank (avs);
4465+ allocationManager.updateAVSMetadataURI (avs, "https://example.com " );
4466+
4467+ CreateSetParamsV2[] memory createSetParams = new CreateSetParamsV2 [](numOpSets);
4468+ OperatorSet[] memory operatorSets = new OperatorSet [](numOpSets);
4469+
4470+ for (uint i = 0 ; i < numOpSets; ++ i) {
4471+ createSetParams[i].operatorSetId = r.Uint32 (1 , type (uint32 ).max);
4472+ createSetParams[i].strategies = r.StrategyArray (0 );
4473+ createSetParams[i].slasher = r.Address ();
4474+ operatorSets[i] = OperatorSet (avs, createSetParams[i].operatorSetId);
4475+ }
4476+
4477+ cheats.prank (avs);
4478+ allocationManager.createOperatorSets (avs, createSetParams);
4479+
4480+ // Set slashers to zero address on all previously create opSets so we can migrate them
4481+ for (uint i = 0 ; i < numOpSets; ++ i) {
4482+ allocationManager.setSlasherZero (operatorSets[i]);
4483+ }
4484+
4485+ // Expect event emits
4486+ for (uint i = 0 ; i < numOpSets; ++ i) {
4487+ cheats.expectEmit (true , true , true , true , address (allocationManager));
4488+ emit SlasherUpdated (operatorSets[i], avs, uint32 (block .number ));
4489+ }
4490+
4491+ // Migrate the slashers
4492+ allocationManager.migrateSlashers (operatorSets);
4493+
4494+ // Check that the slashers are set to the AVS
4495+ for (uint i = 0 ; i < numOpSets; ++ i) {
4496+ assertEq (allocationManager.getSlasher (operatorSets[i]), avs, "slasher should be the AVS " );
4497+ _assertNothingPending (operatorSets[i]);
4498+ }
4499+ }
4500+ }
4501+
43024502contract AllocationManagerUnitTests_setAVSRegistrar is AllocationManagerUnitTests {
43034503 function test_getAVSRegistrar () public {
43044504 address randomAVS = random ().Address ();
0 commit comments