Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: operator set migration-2-create quorum #287

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2bc5224
chore: checkout migration branch
stevennevins Jul 22, 2024
ea886ab
feat: implement migration function
stevennevins Jul 22, 2024
1e9f5ff
chore: update to release branch
stevennevins Jul 22, 2024
70a0d46
feat: operator set creation for each quorum number
stevennevins Jul 22, 2024
3eb3d83
feat: migration with merge sorted array of operators and their quorums
stevennevins Jul 22, 2024
464d2fe
feat: operator set migration working
stevennevins Jul 22, 2024
09d716e
chore: revert change from testing
stevennevins Jul 23, 2024
01fa4cc
chore: revert change from testing
stevennevins Jul 23, 2024
5d9ae5b
chore: remove extra logging and commented out asserts
stevennevins Jul 23, 2024
367f45c
chore: remove unused file
stevennevins Jul 23, 2024
09f7305
fix: remove console logs
stevennevins Jul 23, 2024
3ed86c3
feat: create quorum post operator set migration
stevennevins Jul 25, 2024
ffab895
test(wip): create quorum test adds new operator set
stevennevins Jul 25, 2024
75541cc
test: migration create quorum
stevennevins Aug 2, 2024
cff25e6
refactor: to view functions
stevennevins Aug 5, 2024
97fb672
chore: nit and remove unneeded function
stevennevins Aug 5, 2024
8b46f83
fix: remove duplication of looping for all the operators
stevennevins Aug 5, 2024
415058d
chore: remove comment
stevennevins Aug 6, 2024
011516e
fix: updates from bumping dep
stevennevins Aug 8, 2024
951b832
Merge branch 'feat/migration' into feat/migration-2-create-quorum
stevennevins Aug 8, 2024
3fcc68e
feat: allow migrating in two transactions
stevennevins Aug 8, 2024
c4665be
feat: finalization of migration
stevennevins Aug 8, 2024
62dc6d2
chore: use string errors and fix migration issues
stevennevins Aug 8, 2024
ac6305c
Merge branch 'feat/migration' into feat/migration-2-create-quorum
stevennevins Aug 8, 2024
39d56b1
chore: rename
stevennevins Aug 8, 2024
2ee11df
feat: use library for merge sort
stevennevins Aug 9, 2024
6f37c47
test: fuzz view function
stevennevins Aug 9, 2024
cd02131
Merge branch 'feat/migration' into feat/migration-2-create-quorum
stevennevins Aug 9, 2024
37ed99e
fix: updates from merge
stevennevins Aug 9, 2024
0ad7205
Merge branch 'feat/operate-set-release-branch' into feat/migration-2-…
stevennevins Aug 12, 2024
b69d253
chore: use interface
stevennevins Aug 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/RegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.12;

import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can we import interface? Does the interface not have all functions we need?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the interface was missing a function initially.

I'll double check if it's there now

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was there now b69d253

import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol";
import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
Expand Down Expand Up @@ -732,6 +733,14 @@ contract RegistryCoordinator is
stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams);
indexRegistry.initializeQuorum(quorumNumber);
blsApkRegistry.initializeQuorum(quorumNumber);
// Check if the AVS has migrated to operator sets
AVSDirectory avsDirectory = AVSDirectory(serviceManager.avsDirectory());
if (avsDirectory.isOperatorSetAVS(address(serviceManager))){
ypatil12 marked this conversation as resolved.
Show resolved Hide resolved
// Create an operator set for the new quorum
uint32[] memory operatorSetIds = new uint32[](1);
operatorSetIds[0] = uint32(quorumNumber);
serviceManager.createOperatorSets(operatorSetIds);
}
}

/**
Expand Down
8 changes: 5 additions & 3 deletions src/ServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pragma solidity ^0.8.12;
import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";
import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol";
import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";

import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol";
Expand Down Expand Up @@ -108,6 +107,9 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {

_rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions);
}
function createOperatorSets(uint32[] memory operatorSetIds) external onlyRegistryCoordinator{
_avsDirectory.createOperatorSets(operatorSetIds);
}

/**
* @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator registration with the AVS
Expand Down Expand Up @@ -154,11 +156,11 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {

function _migrateAndCreateOperatorSetIds(uint32[] memory operatorSetIdsToCreate) internal {
_avsDirectory.becomeOperatorSetAVS();
AVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate);
IAVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate);
}

function _migrateToOperatorSets(uint32[][] memory operatorSetIds, address[] memory operators) internal {
AVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets(operators, operatorSetIds);
IAVSDirectory(address(_avsDirectory)).migrateOperatorsToOperatorSets(operators, operatorSetIds);
}

function getOperatorsToMigrate() public view returns (uint32[] memory operatorSetIdsToCreate, uint32[][] memory operatorSetIds, address[] memory allOperators) {
Expand Down
2 changes: 2 additions & 0 deletions src/interfaces/IServiceManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ interface IServiceManager is IServiceManagerUI {
*/
function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) external;

function createOperatorSets(uint32[] memory operatorSetIds) external ;

// EVENTS
event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator);
}
9 changes: 8 additions & 1 deletion test/mocks/ECDSAServiceManagerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase {
address _rewardsCoordinator,
address _delegationManager
)
ECDSAServiceManagerBase(_avsDirectory, _stakeRegistry, _rewardsCoordinator, _delegationManager)
ECDSAServiceManagerBase(
_avsDirectory,
_stakeRegistry,
_rewardsCoordinator,
_delegationManager
)
{}

function initialize(
Expand All @@ -19,4 +24,6 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase {
) public virtual initializer {
__ServiceManagerBase_init(initialOwner, rewardsInitiator);
}

function createOperatorSets(uint32[] memory) external {}
}
200 changes: 200 additions & 0 deletions test/unit/RegistryCoordinatorMigration.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol";
import {
RewardsCoordinator,
IRewardsCoordinator,
IERC20
} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol";
import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol";
import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol";
import {AVSDirectoryHarness} from "../harnesses/AVSDirectoryHarness.sol";

import "../utils/MockAVSDeployer.sol";

contract RegistryCoordinatorMigrationUnit is MockAVSDeployer, IServiceManagerBaseEvents {
// RewardsCoordinator config
address rewardsUpdater = address(uint160(uint256(keccak256("rewardsUpdater"))));
uint32 CALCULATION_INTERVAL_SECONDS = 7 days;
uint32 MAX_REWARDS_DURATION = 70 days;
uint32 MAX_RETROACTIVE_LENGTH = 84 days;
uint32 MAX_FUTURE_LENGTH = 28 days;
uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_188_800;
uint256 MAX_REWARDS_AMOUNT = 1e38 - 1;
/// @notice Delay in timestamp before a posted root can be claimed against
uint32 activationDelay = 7 days;
/// @notice the commission for all operators across all avss
uint16 globalCommissionBips = 1000;

// Testing Config and Mocks
address serviceManagerOwner;
IERC20[] rewardTokens;
uint256 mockTokenInitialSupply = 10e50;
IStrategy strategyMock1;
IStrategy strategyMock2;
IStrategy strategyMock3;
StrategyBase strategyImplementation;
IRewardsCoordinator.StrategyAndMultiplier[] defaultStrategyAndMultipliers;
AVSDirectoryHarness avsDirectoryHarness;

// mapping to setting fuzzed inputs
mapping(address => bool) public addressIsExcludedFromFuzzedInputs;

modifier filterFuzzedAddressInputs(address fuzzedAddress) {
cheats.assume(!addressIsExcludedFromFuzzedInputs[fuzzedAddress]);
_;
}

function setUp() public virtual {
numQuorums = maxQuorumsToRegisterFor;
_deployMockEigenLayerAndAVS();

serviceManagerImplementation = new ServiceManagerMock(
avsDirectory,
IRewardsCoordinator(address(rewardsCoordinatorMock)),
registryCoordinator,
stakeRegistry
);
avsDirectoryHarness = new AVSDirectoryHarness(delegationMock);

serviceManagerImplementation = new ServiceManagerMock(
avsDirectory,
rewardsCoordinatorMock,
registryCoordinator,
stakeRegistry
);
/// Needed to upgrade to a service manager that points to an AVS Directory that can track state
vm.prank(proxyAdmin.owner());
proxyAdmin.upgrade(
TransparentUpgradeableProxy(payable(address(serviceManager))),
address(serviceManagerImplementation)
);

serviceManagerOwner = serviceManager.owner();

_setUpDefaultStrategiesAndMultipliers();

addressIsExcludedFromFuzzedInputs[address(pauserRegistry)] = true;
addressIsExcludedFromFuzzedInputs[address(proxyAdmin)] = true;
}

function _setUpDefaultStrategiesAndMultipliers() internal virtual {
// Deploy Mock Strategies
IERC20 token1 = new ERC20PresetFixedSupply(
"dog wif hat", "MOCK1", mockTokenInitialSupply, address(this)
);
IERC20 token2 =
new ERC20PresetFixedSupply("jeo boden", "MOCK2", mockTokenInitialSupply, address(this));
IERC20 token3 = new ERC20PresetFixedSupply(
"pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this)
);
strategyImplementation = new StrategyBase(strategyManagerMock);
strategyMock1 = StrategyBase(
address(
new TransparentUpgradeableProxy(
address(strategyImplementation),
address(proxyAdmin),
abi.encodeWithSelector(StrategyBase.initialize.selector, token1, pauserRegistry)
)
)
);
strategyMock2 = StrategyBase(
address(
new TransparentUpgradeableProxy(
address(strategyImplementation),
address(proxyAdmin),
abi.encodeWithSelector(StrategyBase.initialize.selector, token2, pauserRegistry)
)
)
);
strategyMock3 = StrategyBase(
address(
new TransparentUpgradeableProxy(
address(strategyImplementation),
address(proxyAdmin),
abi.encodeWithSelector(StrategyBase.initialize.selector, token3, pauserRegistry)
)
)
);
IStrategy[] memory strategies = new IStrategy[](3);
strategies[0] = strategyMock1;
strategies[1] = strategyMock2;
strategies[2] = strategyMock3;
strategies = _sortArrayAsc(strategies);

strategyManagerMock.setStrategyWhitelist(strategies[0], true);
strategyManagerMock.setStrategyWhitelist(strategies[1], true);
strategyManagerMock.setStrategyWhitelist(strategies[2], true);

defaultStrategyAndMultipliers.push(
IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18)
);
defaultStrategyAndMultipliers.push(
IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18)
);
defaultStrategyAndMultipliers.push(
IRewardsCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18)
);
}

/// @dev Sort to ensure that the array is in ascending order for strategies
function _sortArrayAsc(IStrategy[] memory arr) internal pure returns (IStrategy[] memory) {
uint256 l = arr.length;
for (uint256 i = 0; i < l; i++) {
for (uint256 j = i + 1; j < l; j++) {
if (address(arr[i]) > address(arr[j])) {
IStrategy temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}

function test_migrateToOperatorSets() public {
(uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate();
cheats.startPrank(serviceManagerOwner);
serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate);
serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators);
cheats.stopPrank();

assertTrue(avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS");
}



function test_createQuorum() public {
(uint32[] memory operatorSetsToCreate, uint32[][] memory operatorSetIdsToMigrate, address[] memory operators) = serviceManager.getOperatorsToMigrate();
cheats.startPrank(serviceManagerOwner);
serviceManager.migrateAndCreateOperatorSetIds(operatorSetsToCreate);
serviceManager.migrateToOperatorSets(operatorSetIdsToMigrate, operators);
cheats.stopPrank();

assertTrue(avsDirectory.isOperatorSetAVS(address(serviceManager)), "AVS is not an operator set AVS");

uint8 quorumNumber = registryCoordinator.quorumCount();
uint96 minimumStake = 1000;
IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({
maxOperatorCount: 10,
kickBIPsOfOperatorStake: 50,
kickBIPsOfTotalStake: 2
});
IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1);
strategyParams[0] =
IStakeRegistry.StrategyParams({
strategy: IStrategy(address(1000)),
multiplier: 1e16
});

assertFalse(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber), "Operator set already existed");
assertTrue(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber-1), "Operator set doesn't already existed");

vm.prank(registryCoordinator.owner());
registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams);

assertTrue(avsDirectory.isOperatorSet(address(serviceManager), quorumNumber), "Operator set was not created for the quorum");

}
}
Loading