From f1eb96ef9f365f6b04d6db88aeafb010f55bbcb2 Mon Sep 17 00:00:00 2001 From: Yash Patil Date: Sun, 10 Nov 2024 17:23:40 -0800 Subject: [PATCH] feat: unified access management fix: test/compile --- docs/release/slashing/AllocationManager.md | 11 +- .../deploy/devnet/deploy_from_scratch.s.sol | 20 +- .../Deploy_Test_RewardsCoordinator.s.sol | 3 + .../holesky/M2_Deploy_From_Scratch.s.sol | 13 +- ...3-upgrade_testnet_rewardsCoordinator.s.sol | 1 + script/deploy/local/Deploy_From_Scratch.s.sol | 20 +- .../local/deploy_from_scratch.slashing.s.sol | 18 +- .../mainnet/v0.3.0-mainnet-rewards.s.sol | 2 + .../v0.4.3-upgrade_rewardsCoordinator.s.sol | 1 + script/tasks/allocate_operatorSet.s.sol | 2 +- .../register_operator_to_operatorSet.s.sol | 8 +- script/tasks/slash_operatorSet.s.sol | 2 +- script/utils/ExistingDeploymentParser.sol | 3 + src/contracts/core/AllocationManager.sol | 118 ++++--- src/contracts/core/DelegationManager.sol | 23 +- src/contracts/core/RewardsCoordinator.sol | 25 +- .../interfaces/IAllocationManager.sol | 50 +-- .../interfaces/IDelegationManager.sol | 10 +- .../interfaces/IPermissionController.sol | 112 ++++++ .../interfaces/IRewardsCoordinator.sol | 20 +- .../mixins/PermissionControllerMixin.sol | 38 +++ .../permissions/PermissionController.sol | 176 ++++++++++ .../PermissionControllerStorage.sol | 30 ++ src/test/DevnetLifecycle.t.sol | 8 +- .../integration/IntegrationDeployer.t.sol | 21 +- src/test/mocks/PermissionControllerMock.sol | 21 ++ src/test/tree/PermissionControllerUnit.tree | 27 ++ src/test/unit/AllocationManagerUnit.t.sol | 299 +++++++++------- src/test/unit/DelegationUnit.t.sol | 11 +- src/test/unit/PermissionControllerUnit.t.sol | 318 ++++++++++++++++++ src/test/unit/RewardsCoordinatorUnit.t.sol | 25 +- src/test/utils/BeaconChainProofsWrapper.sol | 41 +++ src/test/utils/EigenLayerUnitTestSetup.sol | 10 +- src/test/utils/Operators.sol | 48 +++ src/test/utils/Owners.sol | 47 +++ src/test/utils/SignatureCompaction.sol | 29 ++ src/test/utils/Utils.sol | 23 ++ 37 files changed, 1350 insertions(+), 284 deletions(-) create mode 100644 src/contracts/interfaces/IPermissionController.sol create mode 100644 src/contracts/mixins/PermissionControllerMixin.sol create mode 100644 src/contracts/permissions/PermissionController.sol create mode 100644 src/contracts/permissions/PermissionControllerStorage.sol create mode 100644 src/test/mocks/PermissionControllerMock.sol create mode 100644 src/test/tree/PermissionControllerUnit.tree create mode 100644 src/test/unit/PermissionControllerUnit.t.sol create mode 100644 src/test/utils/BeaconChainProofsWrapper.sol create mode 100644 src/test/utils/Operators.sol create mode 100644 src/test/utils/Owners.sol create mode 100644 src/test/utils/SignatureCompaction.sol create mode 100644 src/test/utils/Utils.sol diff --git a/docs/release/slashing/AllocationManager.md b/docs/release/slashing/AllocationManager.md index 6480893e6..945432fb3 100644 --- a/docs/release/slashing/AllocationManager.md +++ b/docs/release/slashing/AllocationManager.md @@ -22,21 +22,12 @@ The AllocationManager contract manages the allocation and reallocation of operat ```solidity /** - * @notice Called by the delagation manager to set delay when operators register. + * @notice Called by operators or the delegation manager to set their allocation delay. * @param operator The operator to set the delay on behalf of. * @param delay The allocation delay in seconds. - * @dev msg.sender is assumed to be the delegation manager. */ function setAllocationDelay(address operator, uint32 delay) external; -/** - * @notice Called by operators to set their allocation delay. - * @param delay the allocation delay in seconds - * @dev msg.sender is assumed to be the operator - */ -function setAllocationDelay(uint32 delay) external; -``` - These functions allow operators to set their allocation delay. The first variant is called by the DelegationManager upon operator registration for all new operators created after the slashing release. The second variant is called by operators themselves to update their allocation delay or set it for the first time if they joined before the slashing release. The allocation delay takes effect in `ALLOCATION_CONFIGURATION_DELAY` seconds. diff --git a/script/deploy/devnet/deploy_from_scratch.s.sol b/script/deploy/devnet/deploy_from_scratch.s.sol index a9ee32313..72189d9d4 100644 --- a/script/deploy/devnet/deploy_from_scratch.s.sol +++ b/script/deploy/devnet/deploy_from_scratch.s.sol @@ -13,6 +13,7 @@ import "../../../src/contracts/core/DelegationManager.sol"; import "../../../src/contracts/core/AVSDirectory.sol"; import "../../../src/contracts/core/RewardsCoordinator.sol"; import "../../../src/contracts/core/AllocationManager.sol"; +import "../../../src/contracts/permissions/PermissionController.sol"; import "../../../src/contracts/strategies/StrategyBaseTVLLimits.sol"; import "../../../src/contracts/strategies/StrategyFactory.sol"; @@ -60,6 +61,8 @@ contract DeployFromScratch is Script, Test { StrategyBase public baseStrategyImplementation; AllocationManager public allocationManagerImplementation; AllocationManager public allocationManager; + PermissionController public permissionController; + PermissionController public permissionControllerImplementation; EmptyContract public emptyContract; @@ -210,6 +213,9 @@ contract DeployFromScratch is Script, Test { strategyFactory = StrategyFactory( address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) ); + permissionController = PermissionController( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); // if on mainnet, use the ETH2 deposit contract address if (chainId == 1) { @@ -228,7 +234,7 @@ contract DeployFromScratch is Script, Test { // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs - delegationImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, MIN_WITHDRAWAL_DELAY); + delegationImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, permissionController, MIN_WITHDRAWAL_DELAY); strategyManagerImplementation = new StrategyManager(delegation, eigenLayerPauserReg); avsDirectoryImplementation = new AVSDirectory(delegation, eigenLayerPauserReg); eigenPodManagerImplementation = new EigenPodManager( @@ -242,13 +248,15 @@ contract DeployFromScratch is Script, Test { delegation, strategyManager, eigenLayerPauserReg, + permissionController, REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS, REWARDS_COORDINATOR_MAX_REWARDS_DURATION, REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH, REWARDS_COORDINATOR_MAX_FUTURE_LENGTH, REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP ); - allocationManagerImplementation = new AllocationManager(delegation, eigenLayerPauserReg, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY); + allocationManagerImplementation = new AllocationManager(delegation, eigenLayerPauserReg, permissionController, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY); + permissionControllerImplementation = new PermissionController(); strategyFactoryImplementation = new StrategyFactory(strategyManager, eigenLayerPauserReg); // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. @@ -310,6 +318,14 @@ contract DeployFromScratch is Script, Test { ) ); + eigenLayerProxyAdmin.upgradeAndCall( + ITransparentUpgradeableProxy(payable(address(permissionController))), + address(permissionControllerImplementation), + abi.encodeWithSelector( + PermissionController.initialize.selector + ) + ); + // Deploy strategyFactory & base // Create base strategy implementation baseStrategyImplementation = new StrategyBase(strategyManager, eigenLayerPauserReg); diff --git a/script/deploy/holesky/Deploy_Test_RewardsCoordinator.s.sol b/script/deploy/holesky/Deploy_Test_RewardsCoordinator.s.sol index f9000a88b..5b4d8df8a 100644 --- a/script/deploy/holesky/Deploy_Test_RewardsCoordinator.s.sol +++ b/script/deploy/holesky/Deploy_Test_RewardsCoordinator.s.sol @@ -47,6 +47,7 @@ contract Deploy_Test_RewardsCoordinator is ExistingDeploymentParser { delegationManager, strategyManager, eigenLayerPauserReg, + permissionController, REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS, REWARDS_COORDINATOR_MAX_REWARDS_DURATION, REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH, @@ -80,6 +81,7 @@ contract Deploy_Test_RewardsCoordinator is ExistingDeploymentParser { delegationManager, strategyManager, eigenLayerPauserReg, + permissionController, REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS, REWARDS_COORDINATOR_MAX_REWARDS_DURATION, REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH, @@ -121,6 +123,7 @@ contract Deploy_Test_RewardsCoordinator is ExistingDeploymentParser { delegationManager, strategyManager, eigenLayerPauserReg, + permissionController, REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS, REWARDS_COORDINATOR_MAX_REWARDS_DURATION, REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH, diff --git a/script/deploy/holesky/M2_Deploy_From_Scratch.s.sol b/script/deploy/holesky/M2_Deploy_From_Scratch.s.sol index 244bfc59d..6d90221c5 100644 --- a/script/deploy/holesky/M2_Deploy_From_Scratch.s.sol +++ b/script/deploy/holesky/M2_Deploy_From_Scratch.s.sol @@ -77,7 +77,7 @@ contract M2_Deploy_Holesky_From_Scratch is ExistingDeploymentParser { eigenPodBeacon = new UpgradeableBeacon(address(eigenPodImplementation)); avsDirectoryImplementation = new AVSDirectory(delegationManager, eigenLayerPauserReg); - delegationManagerImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, MIN_WITHDRAWAL_DELAY); + delegationManagerImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, permissionController, MIN_WITHDRAWAL_DELAY); strategyManagerImplementation = new StrategyManager(delegationManager, eigenLayerPauserReg); eigenPodManagerImplementation = new EigenPodManager( IETHPOSDeposit(ETHPOSDepositAddress), @@ -86,7 +86,8 @@ contract M2_Deploy_Holesky_From_Scratch is ExistingDeploymentParser { delegationManager, eigenLayerPauserReg ); - allocationManagerImplementation = new AllocationManager(delegationManager, eigenLayerPauserReg, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY); + allocationManagerImplementation = new AllocationManager(delegationManager, eigenLayerPauserReg, permissionController, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY); + permissionControllerImplementation = new PermissionController(); // Third, upgrade the proxy contracts to point to the implementations IStrategy[] memory initializeStrategiesToSetDelayBlocks = new IStrategy[](0); @@ -150,6 +151,14 @@ contract M2_Deploy_Holesky_From_Scratch is ExistingDeploymentParser { ALLOCATION_MANAGER_INIT_PAUSED_STATUS ) ); + // PermissionController + eigenLayerProxyAdmin.upgradeAndCall( + ITransparentUpgradeableProxy(payable(address(permissionController))), + address(permissionControllerImplementation), + abi.encodeWithSelector( + PermissionController.initialize.selector + ) + ); // Deploy Strategies baseStrategyImplementation = new StrategyBaseTVLLimits(strategyManager, eigenLayerPauserReg); diff --git a/script/deploy/holesky/v0.4.3-upgrade_testnet_rewardsCoordinator.s.sol b/script/deploy/holesky/v0.4.3-upgrade_testnet_rewardsCoordinator.s.sol index bc7d0a9f1..f872e5c8e 100644 --- a/script/deploy/holesky/v0.4.3-upgrade_testnet_rewardsCoordinator.s.sol +++ b/script/deploy/holesky/v0.4.3-upgrade_testnet_rewardsCoordinator.s.sol @@ -25,6 +25,7 @@ contract Upgrade_Testnet_RewardsCoordinator is Deploy_Test_RewardsCoordinator, T delegationManager, strategyManager, eigenLayerPauserReg, + permissionController, REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS, REWARDS_COORDINATOR_MAX_REWARDS_DURATION, REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH, diff --git a/script/deploy/local/Deploy_From_Scratch.s.sol b/script/deploy/local/Deploy_From_Scratch.s.sol index a12b1b2f3..9c9040d36 100644 --- a/script/deploy/local/Deploy_From_Scratch.s.sol +++ b/script/deploy/local/Deploy_From_Scratch.s.sol @@ -13,6 +13,7 @@ import "../../../src/contracts/core/DelegationManager.sol"; import "../../../src/contracts/core/AVSDirectory.sol"; import "../../../src/contracts/core/RewardsCoordinator.sol"; import "../../../src/contracts/core/AllocationManager.sol"; +import "../../../src/contracts/permissions/PermissionController.sol"; import "../../../src/contracts/strategies/StrategyBaseTVLLimits.sol"; @@ -63,6 +64,8 @@ contract DeployFromScratch is Script, Test { StrategyBase public baseStrategyImplementation; AllocationManager public allocationManagerImplementation; AllocationManager public allocationManager; + PermissionController public permissionController; + PermissionController public permissionControllerImplementation; EmptyContract public emptyContract; @@ -216,6 +219,9 @@ contract DeployFromScratch is Script, Test { allocationManager = AllocationManager( address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) ); + permissionController = PermissionController( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); // if on mainnet, use the ETH2 deposit contract address if (chainId == 1) { @@ -234,7 +240,7 @@ contract DeployFromScratch is Script, Test { // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs - delegationImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, MIN_WITHDRAWAL_DELAY); + delegationImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, permissionController, MIN_WITHDRAWAL_DELAY); strategyManagerImplementation = new StrategyManager(delegation, eigenLayerPauserReg); avsDirectoryImplementation = new AVSDirectory(delegation, eigenLayerPauserReg); eigenPodManagerImplementation = new EigenPodManager( @@ -248,13 +254,15 @@ contract DeployFromScratch is Script, Test { delegation, strategyManager, eigenLayerPauserReg, + permissionController, REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS, REWARDS_COORDINATOR_MAX_REWARDS_DURATION, REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH, REWARDS_COORDINATOR_MAX_FUTURE_LENGTH, REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP ); - allocationManagerImplementation = new AllocationManager(delegation, eigenLayerPauserReg, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY); + allocationManagerImplementation = new AllocationManager(delegation, eigenLayerPauserReg, permissionController, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY); + permissionControllerImplementation = new PermissionController(); // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. { @@ -320,6 +328,14 @@ contract DeployFromScratch is Script, Test { ) ); + eigenLayerProxyAdmin.upgradeAndCall( + ITransparentUpgradeableProxy(payable(address(permissionController))), + address(permissionControllerImplementation), + abi.encodeWithSelector( + PermissionController.initialize.selector + ) + ); + // deploy StrategyBaseTVLLimits contract implementation baseStrategyImplementation = new StrategyBaseTVLLimits(strategyManager, eigenLayerPauserReg); // create upgradeable proxies that each point to the implementation and initialize them diff --git a/script/deploy/local/deploy_from_scratch.slashing.s.sol b/script/deploy/local/deploy_from_scratch.slashing.s.sol index 67dbf5f8d..3446b0e77 100644 --- a/script/deploy/local/deploy_from_scratch.slashing.s.sol +++ b/script/deploy/local/deploy_from_scratch.slashing.s.sol @@ -13,6 +13,7 @@ import "../../../src/contracts/core/DelegationManager.sol"; import "../../../src/contracts/core/AVSDirectory.sol"; import "../../../src/contracts/core/RewardsCoordinator.sol"; import "../../../src/contracts/core/AllocationManager.sol"; +import "../../../src/contracts/permissions/PermissionController.sol"; import "../../../src/contracts/strategies/StrategyBaseTVLLimits.sol"; @@ -63,6 +64,8 @@ contract DeployFromScratch is Script, Test { StrategyBase public baseStrategyImplementation; AllocationManager public allocationManagerImplementation; AllocationManager public allocationManager; + PermissionController public permissionControllerImplementation; + PermissionController public permissionController; EmptyContract public emptyContract; @@ -217,6 +220,9 @@ contract DeployFromScratch is Script, Test { allocationManager = AllocationManager( address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) ); + permissionController = PermissionController( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); // if on mainnet, use the ETH2 deposit contract address if (chainId == 1) { @@ -235,7 +241,7 @@ contract DeployFromScratch is Script, Test { // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs - delegationImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, MIN_WITHDRAWAL_DELAY); + delegationImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, permissionController, MIN_WITHDRAWAL_DELAY); strategyManagerImplementation = new StrategyManager(delegation, eigenLayerPauserReg); avsDirectoryImplementation = new AVSDirectory(delegation, eigenLayerPauserReg); eigenPodManagerImplementation = new EigenPodManager( @@ -249,13 +255,15 @@ contract DeployFromScratch is Script, Test { delegation, strategyManager, eigenLayerPauserReg, + permissionController, REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS, REWARDS_COORDINATOR_MAX_REWARDS_DURATION, REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH, REWARDS_COORDINATOR_MAX_FUTURE_LENGTH, REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP ); - allocationManagerImplementation = new AllocationManager(delegation, eigenLayerPauserReg, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY); + allocationManagerImplementation = new AllocationManager(delegation, eigenLayerPauserReg, permissionController, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY); + permissionControllerImplementation = new PermissionController(); // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. { @@ -321,6 +329,12 @@ contract DeployFromScratch is Script, Test { ) ); + eigenLayerProxyAdmin.upgradeAndCall( + ITransparentUpgradeableProxy(payable(address(permissionController))), + address(permissionControllerImplementation), + abi.encodeWithSelector(PermissionController.initialize.selector) + ); + // deploy StrategyBaseTVLLimits contract implementation baseStrategyImplementation = new StrategyBaseTVLLimits(strategyManager, eigenLayerPauserReg); // create upgradeable proxies that each point to the implementation and initialize them diff --git a/script/deploy/mainnet/v0.3.0-mainnet-rewards.s.sol b/script/deploy/mainnet/v0.3.0-mainnet-rewards.s.sol index 753bb52c1..f561c1137 100644 --- a/script/deploy/mainnet/v0.3.0-mainnet-rewards.s.sol +++ b/script/deploy/mainnet/v0.3.0-mainnet-rewards.s.sol @@ -53,6 +53,7 @@ contract MainnetRewardsCoordinatorDeploy is ExistingDeploymentParser { delegationManager, strategyManager, eigenLayerPauserReg, + permissionController, REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS, REWARDS_COORDINATOR_MAX_REWARDS_DURATION, REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH, @@ -86,6 +87,7 @@ contract MainnetRewardsCoordinatorDeploy is ExistingDeploymentParser { delegationManager, strategyManager, eigenLayerPauserReg, + permissionController, REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS, REWARDS_COORDINATOR_MAX_REWARDS_DURATION, REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH, diff --git a/script/deploy/mainnet/v0.4.3-upgrade_rewardsCoordinator.s.sol b/script/deploy/mainnet/v0.4.3-upgrade_rewardsCoordinator.s.sol index 059b47c57..6f0ff481d 100644 --- a/script/deploy/mainnet/v0.4.3-upgrade_rewardsCoordinator.s.sol +++ b/script/deploy/mainnet/v0.4.3-upgrade_rewardsCoordinator.s.sol @@ -50,6 +50,7 @@ contract Upgrade_Mainnet_RewardsCoordinator is ExistingDeploymentParser, Timeloc delegationManager, strategyManager, eigenLayerPauserReg, + permissionController, REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS, REWARDS_COORDINATOR_MAX_REWARDS_DURATION, REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH, diff --git a/script/tasks/allocate_operatorSet.s.sol b/script/tasks/allocate_operatorSet.s.sol index fdd14f0d7..ab3d633a6 100644 --- a/script/tasks/allocate_operatorSet.s.sol +++ b/script/tasks/allocate_operatorSet.s.sol @@ -51,7 +51,7 @@ contract AllocateOperatorSet is Script, Test { }); // Perform allocation - am.modifyAllocations(allocations); + am.modifyAllocations(msg.sender, allocations); // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT vm.stopBroadcast(); diff --git a/script/tasks/register_operator_to_operatorSet.s.sol b/script/tasks/register_operator_to_operatorSet.s.sol index d0ab368df..9ab8dc252 100644 --- a/script/tasks/register_operator_to_operatorSet.s.sol +++ b/script/tasks/register_operator_to_operatorSet.s.sol @@ -60,15 +60,15 @@ contract RegisterOperatorToOperatorSets is Script, Test { operatorSetId: 1, strategies: strategies }); - allocationManager.createOperatorSets(sets); + allocationManager.createOperatorSets(msg.sender, sets); // Register the Operator to the AVS avsDirectory.registerOperatorToAVS( operator, ISignatureUtils.SignatureWithSaltAndExpiry(abi.encodePacked(r, s, v), bytes32(uint256(0) + 1), expiry) ); - // Deploy and set registrar - allocationManager.setAVSRegistrar(new AVSRegistrar()); + // Deploy and set registrar. + allocationManager.setAVSRegistrar(msg.sender, new AVSRegistrar()); // Register OperatorSet(s) IAllocationManagerTypes.RegisterParams memory register = IAllocationManagerTypes.RegisterParams({ @@ -76,7 +76,7 @@ contract RegisterOperatorToOperatorSets is Script, Test { operatorSetIds: oids, data: "" }); - allocationManager.registerForOperatorSets(register); + allocationManager.registerForOperatorSets(operator, register); // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT vm.stopBroadcast(); diff --git a/script/tasks/slash_operatorSet.s.sol b/script/tasks/slash_operatorSet.s.sol index 136f54d5f..d17feb63b 100644 --- a/script/tasks/slash_operatorSet.s.sol +++ b/script/tasks/slash_operatorSet.s.sol @@ -35,7 +35,7 @@ contract SlashOperatorSet is Script, Test { }); // Perform slashing - am.slashOperator(slashing); + am.slashOperator(operator, slashing); // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT vm.stopBroadcast(); diff --git a/script/utils/ExistingDeploymentParser.sol b/script/utils/ExistingDeploymentParser.sol index 874b12715..f12a225d5 100644 --- a/script/utils/ExistingDeploymentParser.sol +++ b/script/utils/ExistingDeploymentParser.sol @@ -10,6 +10,7 @@ import "../../src/contracts/core/DelegationManager.sol"; import "../../src/contracts/core/AVSDirectory.sol"; import "../../src/contracts/core/RewardsCoordinator.sol"; import "../../src/contracts/core/AllocationManager.sol"; +import "../../src/contracts/permissions/PermissionController.sol"; import "../../src/contracts/strategies/StrategyFactory.sol"; import "../../src/contracts/strategies/StrategyBase.sol"; @@ -64,6 +65,8 @@ contract ExistingDeploymentParser is Script, Test { AllocationManager public allocationManagerImplementation; UpgradeableBeacon public strategyBeacon; StrategyBase public strategyFactoryBeaconImplementation; + PermissionController public permissionController; + PermissionController public permissionControllerImplementation; // Token ProxyAdmin public tokenProxyAdmin; diff --git a/src/contracts/core/AllocationManager.sol b/src/contracts/core/AllocationManager.sol index 96223fdb6..321e7ebab 100644 --- a/src/contracts/core/AllocationManager.sol +++ b/src/contracts/core/AllocationManager.sol @@ -5,6 +5,7 @@ import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import "@openzeppelin-upgrades/contracts/security/ReentrancyGuardUpgradeable.sol"; +import "../mixins/PermissionControllerMixin.sol"; import "../permissions/Pausable.sol"; import "../libraries/SlashingLib.sol"; import "../libraries/OperatorSetLib.sol"; @@ -15,7 +16,8 @@ contract AllocationManager is OwnableUpgradeable, Pausable, AllocationManagerStorage, - ReentrancyGuardUpgradeable + ReentrancyGuardUpgradeable, + PermissionControllerMixin { using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; using EnumerableSet for *; @@ -36,11 +38,13 @@ contract AllocationManager is constructor( IDelegationManager _delegation, IPauserRegistry _pauserRegistry, + IPermissionController _permissionController, uint32 _DEALLOCATION_DELAY, uint32 _ALLOCATION_CONFIGURATION_DELAY ) AllocationManagerStorage(_delegation, _DEALLOCATION_DELAY, _ALLOCATION_CONFIGURATION_DELAY) Pausable(_pauserRegistry) + PermissionControllerMixin(_permissionController) { _disableInitializers(); } @@ -53,12 +57,13 @@ contract AllocationManager is /// @inheritdoc IAllocationManager function slashOperator( + address avs, SlashingParams calldata params - ) external onlyWhenNotPaused(PAUSED_OPERATOR_SLASHING) { + ) external onlyWhenNotPaused(PAUSED_OPERATOR_SLASHING) checkCanCall(avs) { require(0 < params.wadToSlash && params.wadToSlash <= WAD, InvalidWadToSlash()); // Check that the operator set exists and the operator is registered to it - OperatorSet memory operatorSet = OperatorSet(msg.sender, params.operatorSetId); + OperatorSet memory operatorSet = OperatorSet(avs, params.operatorSetId); bool isRegistered = _isRegistered(params.operator, operatorSet); require(_operatorSets[operatorSet.avs].contains(operatorSet.id), InvalidOperatorSet()); require(isRegistered, NotMemberOfSet()); @@ -126,11 +131,20 @@ contract AllocationManager is /// @inheritdoc IAllocationManager function modifyAllocations( - AllocateParams[] calldata params + address operator, + AllocateParams[] memory params ) external onlyWhenNotPaused(PAUSED_MODIFY_ALLOCATIONS) { + // Check that the caller is allowed to modify allocations on behalf of the operator + // We do not use a modifier to avoid `stack too deep` errors + require(_checkCanCall(operator), InvalidCaller()); + // Check that the operator exists and has configured an allocation delay - (bool isSet, uint32 operatorAllocationDelay) = getAllocationDelay(msg.sender); - require(isSet, UninitializedAllocationDelay()); + uint32 operatorAllocationDelay; + { + (bool isSet, uint32 delay) = getAllocationDelay(operator); + require(isSet, UninitializedAllocationDelay()); + operatorAllocationDelay = delay; + } for (uint256 i = 0; i < params.length; i++) { require(params[i].strategies.length == params[i].newMagnitudes.length, InputArrayLengthMismatch()); @@ -140,20 +154,21 @@ contract AllocationManager is // slashable magnitude to the set. In fact, it is expected that operators will // allocate magnitude before registering, as AVS's will likely only accept // registrations from operators that are already slashable. - OperatorSet calldata operatorSet = params[i].operatorSet; - bool isRegistered = _isRegistered(msg.sender, operatorSet); + OperatorSet memory operatorSet = params[i].operatorSet; require(_operatorSets[operatorSet.avs].contains(operatorSet.id), InvalidOperatorSet()); + bool isRegistered = _isRegistered(operator, operatorSet); + for (uint256 j = 0; j < params[i].strategies.length; j++) { IStrategy strategy = params[i].strategies[j]; // 1. If the operator has any pending deallocations for this strategy, clear them // to free up magnitude for allocation. Fetch the operator's up to date allocation // info and ensure there is no remaining pending modification. - _clearDeallocationQueue(msg.sender, strategy, type(uint16).max); + _clearDeallocationQueue(operator, strategy, type(uint16).max); (StrategyInfo memory info, Allocation memory allocation) = - _getUpdatedAllocation(msg.sender, operatorSet.key(), strategy); + _getUpdatedAllocation(operator, operatorSet.key(), strategy); require(allocation.pendingDiff == 0, ModificationAlreadyPending()); // 2. Check whether the operator's allocation is slashable. If not, we allow instant @@ -169,7 +184,7 @@ contract AllocationManager is if (isSlashable) { // If the operator is slashable, deallocated magnitude will be freed after // the deallocation delay. This magnitude remains slashable until then. - deallocationQueue[msg.sender][strategy].pushBack(operatorSet.key()); + deallocationQueue[operator][strategy].pushBack(operatorSet.key()); allocation.effectBlock = uint32(block.number) + DEALLOCATION_DELAY; } else { @@ -189,7 +204,7 @@ contract AllocationManager is } // 5. Update state - _updateAllocationInfo(msg.sender, operatorSet.key(), strategy, info, allocation); + _updateAllocationInfo(operator, operatorSet.key(), strategy, info, allocation); } } } @@ -209,35 +224,37 @@ contract AllocationManager is /// @inheritdoc IAllocationManager function registerForOperatorSets( + address operator, RegisterParams calldata params - ) external onlyWhenNotPaused(PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION) { + ) external onlyWhenNotPaused(PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION) checkCanCall(operator) { // Check that the operator exists - require(delegation.isOperator(msg.sender), InvalidOperator()); + require(delegation.isOperator(operator), InvalidOperator()); for (uint256 i = 0; i < params.operatorSetIds.length; i++) { // Check the operator set exists and the operator is not currently registered to it OperatorSet memory operatorSet = OperatorSet(params.avs, params.operatorSetIds[i]); require(_operatorSets[operatorSet.avs].contains(operatorSet.id), InvalidOperatorSet()); - require(!_isRegistered(msg.sender, operatorSet), AlreadyMemberOfSet()); + require(!_isRegistered(operator, operatorSet), AlreadyMemberOfSet()); // Add operator to operator set - registeredSets[msg.sender].add(operatorSet.key()); - _operatorSetMembers[operatorSet.key()].add(msg.sender); - emit OperatorAddedToOperatorSet(msg.sender, operatorSet); + registeredSets[operator].add(operatorSet.key()); + _operatorSetMembers[operatorSet.key()].add(operator); + emit OperatorAddedToOperatorSet(operator, operatorSet); // Mark the operator registered - registrationStatus[msg.sender][operatorSet.key()].registered = true; + registrationStatus[operator][operatorSet.key()].registered = true; } // Call the AVS to complete registration. If the AVS reverts, registration will fail. - getAVSRegistrar(params.avs).registerOperator(msg.sender, params.operatorSetIds, params.data); + getAVSRegistrar(params.avs).registerOperator(operator, params.operatorSetIds, params.data); } /// @inheritdoc IAllocationManager function deregisterFromOperatorSets( DeregisterParams calldata params ) external onlyWhenNotPaused(PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION) { - require(msg.sender == params.operator || msg.sender == params.avs, InvalidCaller()); + // Check that the caller is either authorized on behalf of the operator or AVS + require(_checkCanCall(params.operator) || _checkCanCall(params.avs), InvalidCaller()); for (uint256 i = 0; i < params.operatorSetIds.length; i++) { // Check the operator set exists and the operator is registered to it @@ -265,43 +282,32 @@ contract AllocationManager is /// @inheritdoc IAllocationManager function setAllocationDelay(address operator, uint32 delay) external { - require(msg.sender == address(delegation), OnlyDelegationManager()); + if (msg.sender != address(delegation)) { + require(_checkCanCall(operator), InvalidCaller()); + require(delegation.isOperator(operator), OperatorNotRegistered()); + } _setAllocationDelay(operator, delay); } /// @inheritdoc IAllocationManager - function setAllocationDelay( - uint32 delay - ) external { - require(delegation.isOperator(msg.sender), OperatorNotRegistered()); - _setAllocationDelay(msg.sender, delay); - } - - /// @inheritdoc IAllocationManager - function setAVSRegistrar( - IAVSRegistrar registrar - ) external { - _avsRegistrar[msg.sender] = registrar; - emit AVSRegistrarSet(msg.sender, getAVSRegistrar(msg.sender)); + function setAVSRegistrar(address avs, IAVSRegistrar registrar) external checkCanCall(avs) { + _avsRegistrar[avs] = registrar; + emit AVSRegistrarSet(avs, getAVSRegistrar(avs)); } /// @inheritdoc IAllocationManager - function updateAVSMetadataURI( - string calldata metadataURI - ) external { - emit AVSMetadataURIUpdated(msg.sender, metadataURI); + function updateAVSMetadataURI(address avs, string calldata metadataURI) external checkCanCall(avs) { + emit AVSMetadataURIUpdated(avs, metadataURI); } /// @inheritdoc IAllocationManager - function createOperatorSets( - CreateSetParams[] calldata params - ) external { + function createOperatorSets(address avs, CreateSetParams[] calldata params) external checkCanCall(avs) { for (uint256 i = 0; i < params.length; i++) { - OperatorSet memory operatorSet = OperatorSet(msg.sender, params[i].operatorSetId); + OperatorSet memory operatorSet = OperatorSet(avs, params[i].operatorSetId); // Create the operator set, ensuring it does not already exist - require(_operatorSets[msg.sender].add(operatorSet.id), InvalidOperatorSet()); - emit OperatorSetCreated(OperatorSet(msg.sender, operatorSet.id)); + require(_operatorSets[avs].add(operatorSet.id), InvalidOperatorSet()); + emit OperatorSetCreated(OperatorSet(avs, operatorSet.id)); // Add strategies to the operator set bytes32 operatorSetKey = operatorSet.key(); @@ -313,9 +319,13 @@ contract AllocationManager is } /// @inheritdoc IAllocationManager - function addStrategiesToOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external { - OperatorSet memory operatorSet = OperatorSet(msg.sender, operatorSetId); - require(_operatorSets[msg.sender].contains(operatorSet.id), InvalidOperatorSet()); + function addStrategiesToOperatorSet( + address avs, + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external checkCanCall(avs) { + OperatorSet memory operatorSet = OperatorSet(avs, operatorSetId); + require(_operatorSets[avs].contains(operatorSet.id), InvalidOperatorSet()); bytes32 operatorSetKey = operatorSet.key(); for (uint256 i = 0; i < strategies.length; i++) { @@ -325,9 +335,13 @@ contract AllocationManager is } /// @inheritdoc IAllocationManager - function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external { - OperatorSet memory operatorSet = OperatorSet(msg.sender, operatorSetId); - require(_operatorSets[msg.sender].contains(operatorSet.id), InvalidOperatorSet()); + function removeStrategiesFromOperatorSet( + address avs, + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external checkCanCall(avs) { + OperatorSet memory operatorSet = OperatorSet(avs, operatorSetId); + require(_operatorSets[avs].contains(operatorSet.id), InvalidOperatorSet()); bytes32 operatorSetKey = operatorSet.key(); for (uint256 i = 0; i < strategies.length; i++) { @@ -355,7 +369,7 @@ contract AllocationManager is while (length > 0 && numCleared < numToClear) { bytes32 operatorSetKey = deallocationQueue[operator][strategy].front(); (StrategyInfo memory info, Allocation memory allocation) = - _getUpdatedAllocation(msg.sender, operatorSetKey, strategy); + _getUpdatedAllocation(operator, operatorSetKey, strategy); // If we've reached a pending deallocation that isn't completable yet, // we can stop. Any subsequent deallocation will also be uncompletable. diff --git a/src/contracts/core/DelegationManager.sol b/src/contracts/core/DelegationManager.sol index bc10837e3..a333cd58a 100644 --- a/src/contracts/core/DelegationManager.sol +++ b/src/contracts/core/DelegationManager.sol @@ -6,6 +6,7 @@ import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import "@openzeppelin-upgrades/contracts/security/ReentrancyGuardUpgradeable.sol"; import "../mixins/SignatureUtils.sol"; +import "../mixins/PermissionControllerMixin.sol"; import "../permissions/Pausable.sol"; import "../libraries/SlashingLib.sol"; import "./DelegationManagerStorage.sol"; @@ -26,7 +27,8 @@ contract DelegationManager is Pausable, DelegationManagerStorage, ReentrancyGuardUpgradeable, - SignatureUtils + SignatureUtils, + PermissionControllerMixin { using SlashingLib for *; using EnumerableSet for EnumerableSet.Bytes32Set; @@ -65,6 +67,7 @@ contract DelegationManager is IEigenPodManager _eigenPodManager, IAllocationManager _allocationManager, IPauserRegistry _pauserRegistry, + IPermissionController _permissionController, uint32 _MIN_WITHDRAWAL_DELAY ) DelegationManagerStorage( @@ -75,6 +78,7 @@ contract DelegationManager is _MIN_WITHDRAWAL_DELAY ) Pausable(_pauserRegistry) + PermissionControllerMixin(_permissionController) { _disableInitializers(); } @@ -111,18 +115,17 @@ contract DelegationManager is /// @inheritdoc IDelegationManager function modifyOperatorDetails( + address operator, OperatorDetails calldata newOperatorDetails - ) external { - require(isOperator(msg.sender), OperatorNotRegistered()); - _setOperatorDetails(msg.sender, newOperatorDetails); + ) external checkCanCall(operator) { + require(isOperator(operator), OperatorNotRegistered()); + _setOperatorDetails(operator, newOperatorDetails); } /// @inheritdoc IDelegationManager - function updateOperatorMetadataURI( - string calldata metadataURI - ) external { - require(isOperator(msg.sender), OperatorNotRegistered()); - emit OperatorMetadataURIUpdated(msg.sender, metadataURI); + function updateOperatorMetadataURI(address operator, string calldata metadataURI) external checkCanCall(operator) { + require(isOperator(operator), OperatorNotRegistered()); + emit OperatorMetadataURIUpdated(operator, metadataURI); } /// @inheritdoc IDelegationManager @@ -147,7 +150,7 @@ contract DelegationManager is require(staker != address(0), InputAddressZero()); address operator = delegatedTo[staker]; require( - msg.sender == staker || msg.sender == operator + msg.sender == staker || _checkCanCall(operator) || msg.sender == _operatorDetails[operator].delegationApprover, CallerCannotUndelegate() ); diff --git a/src/contracts/core/RewardsCoordinator.sol b/src/contracts/core/RewardsCoordinator.sol index 7c4e5d67a..b0b908768 100644 --- a/src/contracts/core/RewardsCoordinator.sol +++ b/src/contracts/core/RewardsCoordinator.sol @@ -10,6 +10,7 @@ import "../libraries/Merkle.sol"; import "../interfaces/IStrategyManager.sol"; import "../permissions/Pausable.sol"; import "./RewardsCoordinatorStorage.sol"; +import "../mixins/PermissionControllerMixin.sol"; /** * @title RewardsCoordinator @@ -25,7 +26,8 @@ contract RewardsCoordinator is OwnableUpgradeable, Pausable, ReentrancyGuardUpgradeable, - RewardsCoordinatorStorage + RewardsCoordinatorStorage, + PermissionControllerMixin { using SafeERC20 for IERC20; @@ -44,6 +46,7 @@ contract RewardsCoordinator is IDelegationManager _delegationManager, IStrategyManager _strategyManager, IPauserRegistry _pauserRegistry, + IPermissionController _permissionController, uint32 _CALCULATION_INTERVAL_SECONDS, uint32 _MAX_REWARDS_DURATION, uint32 _MAX_RETROACTIVE_LENGTH, @@ -60,6 +63,7 @@ contract RewardsCoordinator is _GENESIS_REWARDS_TIMESTAMP ) Pausable(_pauserRegistry) + PermissionControllerMixin(_permissionController) { _disableInitializers(); } @@ -90,8 +94,9 @@ contract RewardsCoordinator is /// @inheritdoc IRewardsCoordinator function createAVSRewardsSubmission( + address avs, RewardsSubmission[] calldata rewardsSubmissions - ) external onlyWhenNotPaused(PAUSED_AVS_REWARDS_SUBMISSION) nonReentrant { + ) external onlyWhenNotPaused(PAUSED_AVS_REWARDS_SUBMISSION) checkCanCall(avs) nonReentrant { for (uint256 i = 0; i < rewardsSubmissions.length; i++) { RewardsSubmission calldata rewardsSubmission = rewardsSubmissions[i]; uint256 nonce = submissionNonce[msg.sender]; @@ -216,9 +221,13 @@ contract RewardsCoordinator is address claimer ) external { address earner = msg.sender; - address prevClaimer = claimerFor[earner]; - claimerFor[earner] = claimer; - emit ClaimerForSet(earner, prevClaimer, claimer); + _setClaimer(earner, claimer); + } + + /// @inheritdoc IRewardsCoordinator + function setClaimerFor(address earner, address claimer) external checkCanCall(earner) { + require(delegationManager.isOperator(earner), EarnerNotOperator()); + _setClaimer(earner, claimer); } /// @inheritdoc IRewardsCoordinator @@ -395,6 +404,12 @@ contract RewardsCoordinator is rewardsUpdater = _rewardsUpdater; } + function _setClaimer(address earner, address claimer) internal { + address prevClaimer = claimerFor[earner]; + claimerFor[earner] = claimer; + emit ClaimerForSet(earner, prevClaimer, claimer); + } + /** * * VIEW FUNCTIONS diff --git a/src/contracts/interfaces/IAllocationManager.sol b/src/contracts/interfaces/IAllocationManager.sol index e3b447046..7c4575b15 100644 --- a/src/contracts/interfaces/IAllocationManager.sol +++ b/src/contracts/interfaces/IAllocationManager.sol @@ -19,8 +19,6 @@ interface IAllocationManagerErrors { /// Caller - /// @dev Thrown when caller is not the delegation manager. - error OnlyDelegationManager(); /// @dev Thrown when caller is not authorized to call a function. error InvalidCaller(); @@ -224,21 +222,18 @@ interface IAllocationManager is ISignatureUtils, IAllocationManagerErrors, IAllo /** * @notice Called by an AVS to slash an operator in a given operator set */ - function slashOperator( - SlashingParams calldata params - ) external; + function slashOperator(address avs, SlashingParams calldata params) external; /** * @notice Modifies the proportions of slashable stake allocated to an operator set from a list of strategies * Note that deallocations remain slashable for DEALLOCATION_DELAY blocks therefore when they are cleared they may * free up less allocatable magnitude than initially deallocated. + * @param operator the operator to modify allocations for * @param params array of magnitude adjustments for one or more operator sets * @dev Updates encumberedMagnitude for the updated strategies * @dev msg.sender is used as operator */ - function modifyAllocations( - AllocateParams[] calldata params - ) external; + function modifyAllocations(address operator, AllocateParams[] calldata params) external; /** * @notice This function takes a list of strategies and for each strategy, removes from the deallocationQueue @@ -263,9 +258,7 @@ interface IAllocationManager is ISignatureUtils, IAllocationManagerErrors, IAllo * @dev After registering within the ALM, this method calls `avs.registerOperator` to complete * registration. This call MUST succeed in order for registration to be successful. */ - function registerForOperatorSets( - RegisterParams calldata params - ) external; + function registerForOperatorSets(address operator, RegisterParams calldata params) external; /** * @notice Allows an operator or AVS to deregister the operator from one or more of the AVS's operator sets. @@ -279,7 +272,7 @@ interface IAllocationManager is ISignatureUtils, IAllocationManagerErrors, IAllo ) external; /** - * @notice Called by the delegation manager to set an operator's allocation delay. + * @notice Called by the delegation manager OR an operator to set an operator's allocation delay. * This is set when the operator first registers, and is the number of blocks between an operator * allocating magnitude to an operator set, and the magnitude becoming slashable. * @param operator The operator to set the delay on behalf of. @@ -287,26 +280,13 @@ interface IAllocationManager is ISignatureUtils, IAllocationManagerErrors, IAllo */ function setAllocationDelay(address operator, uint32 delay) external; - /** - * @notice Called by an operator to set their allocation delay. This is number of blocks between an operator - * allocating magnitude to an operator set, and the magnitude becoming slashable. - * @dev Note that if an operator's allocation delay has not been set, the operator will be unable to allocate - * slashable magnitude to any operator set. - * @param delay the allocation delay in blocks - */ - function setAllocationDelay( - uint32 delay - ) external; - /** * @notice Called by an AVS to configure the address that is called when an operator registers * or is deregistered from the AVS's operator sets. If not set (or set to 0), defaults * to the AVS's address. * @param registrar the new registrar address */ - function setAVSRegistrar( - IAVSRegistrar registrar - ) external; + function setAVSRegistrar(address avs, IAVSRegistrar registrar) external; /** * @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated. @@ -315,32 +295,34 @@ interface IAllocationManager is ISignatureUtils, IAllocationManagerErrors, IAllo * * @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event. */ - function updateAVSMetadataURI( - string calldata metadataURI - ) external; + function updateAVSMetadataURI(address avs, string calldata metadataURI) external; /** * @notice Allows an AVS to create new operator sets, defining strategies that the operator set uses */ - function createOperatorSets( - CreateSetParams[] calldata params - ) external; + function createOperatorSets(address avs, CreateSetParams[] calldata params) external; /** * @notice Allows an AVS to add strategies to an operator set * @dev Strategies MUST NOT already exist in the operator set + * @param avs the avs to set strategies for * @param operatorSetId the operator set to add strategies to * @param strategies the strategies to add */ - function addStrategiesToOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external; + function addStrategiesToOperatorSet(address avs, uint32 operatorSetId, IStrategy[] calldata strategies) external; /** * @notice Allows an AVS to remove strategies from an operator set * @dev Strategies MUST already exist in the operator set + * @param avs the avs to remove strategies for * @param operatorSetId the operator set to remove strategies from * @param strategies the strategies to remove */ - function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external; + function removeStrategiesFromOperatorSet( + address avs, + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external; /** * diff --git a/src/contracts/interfaces/IDelegationManager.sol b/src/contracts/interfaces/IDelegationManager.sol index 9d9937173..0649060c8 100644 --- a/src/contracts/interfaces/IDelegationManager.sol +++ b/src/contracts/interfaces/IDelegationManager.sol @@ -215,22 +215,20 @@ interface IDelegationManager is ISignatureUtils, IDelegationManagerErrors, IDele /** * @notice Updates an operator's stored `OperatorDetails`. + * @param operator is the operator to update. * @param newOperatorDetails is the updated `OperatorDetails` for the operator, to replace their current OperatorDetails`. * * @dev The caller must have previously registered as an operator in EigenLayer. */ - function modifyOperatorDetails( - OperatorDetails calldata newOperatorDetails - ) external; + function modifyOperatorDetails(address operator, OperatorDetails calldata newOperatorDetails) external; /** * @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated. + * @param operator The operator to update metadata for * @param metadataURI The URI for metadata associated with an operator * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event */ - function updateOperatorMetadataURI( - string calldata metadataURI - ) external; + function updateOperatorMetadataURI(address operator, string calldata metadataURI) external; /** * @notice Caller delegates their stake to an operator. diff --git a/src/contracts/interfaces/IPermissionController.sol b/src/contracts/interfaces/IPermissionController.sol new file mode 100644 index 000000000..74df8a6b4 --- /dev/null +++ b/src/contracts/interfaces/IPermissionController.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +interface IPermissionControllerErrors { + /// @notice Thrown when the caller is not the admin + error NotAdmin(); + /// @notice Thrown when an admin is set to the zero address + error AdminAlreadySet(); + /// @notice Thrown when the admin to remove is not an admin + error AdminNotSet(); + /// @notice Thrown when a delegate is already set for the account's function + error DelegateAlreadySet(); + /// @notice Thrown when a delegate is not set for the account's function + error DelegateNotSet(); +} + +interface IPermissionControllerEvents { + /// @notice Emitted when a delegate is set + event DelegateSet(address indexed account, address indexed delegate, address target, bytes4 selector); + + /// @notice Emitted when a delegate is revoked + event DelegateRemoved(address indexed account, address indexed delegate, address target, bytes4 selector); + + /// @notice Emitted when an admin is set for a given account + event AdminSet(address indexed account, address admin); + + /// @notice Emitted when an admin is removed for a given account + event AdminRemoved(address indexed account, address admin); +} + +interface IPermissionController is IPermissionControllerErrors, IPermissionControllerEvents { + /** + * @notice Set the admin of an account + * @param account to set admin for + * @param admin to set + * @dev Multiple admins can be set for an account + */ + function setAdmin(address account, address admin) external; + + /** + * @notice Remove an admin of an account + * @param account to remove admin for + * @param admin to remove + * @dev Only the admin of the account can remove an admin + */ + function removeAdmin(address account, address admin) external; + + /** + * @notice Set a delegate for a given account + * @param account to set delegate for + * @param delegate to set + * @param target to set delegate for + * @param selector to set delegate for + * @dev Only the admin of the account can set a delegate + */ + function setDelegate(address account, address delegate, address target, bytes4 selector) external; + + /** + * Removes a delegate for a given account + * @param account to remove delegate for + * @param delegate to remove + * @param target to remove delegate for + * @param selector to remove delegate for + * @dev Only the admin of the account can remove a delegate + * @dev If all admins are removed, the original account is now the admin + */ + function removeDelegate(address account, address delegate, address target, bytes4 selector) external; + + /** + * @notice Checks if the given caller is an admin of the account + * @dev If the account has no admin, the caller is checked to be the account itself + */ + function isAdmin(address account, address caller) external view returns (bool); + + /** + * @notice Get the admins of an account + * @param account The account to get the admin of + * @dev If the account has no admin, the account itself is returned + */ + function getAdmins( + address account + ) external view returns (address[] memory); + + /** + * @notice Checks if the given caller has permissions to call the fucntion + * @param account to check + * @param caller to check permission for + * @param target to check permission for + * @param selector to check permission for + * @dev Returns `true` if the admin OR the delegate is the caller + */ + function canCall(address account, address caller, address target, bytes4 selector) external returns (bool); + + /** + * @notice Gets the list of permissions of a delegate for a given account + * @param account to get delegate permissions for + * @param delegate to get permissions + */ + function getDelegatePermissions( + address account, + address delegate + ) external returns (address[] memory, bytes4[] memory); + + /** + * @notice Returns the list of delegates for a given account and function + * @param account to get delegates for + * @param target to get delegates for + * @param selector to get delegates for + * @dev Does NOT include admin as a delegate, even though it can call + */ + function getDelegates(address account, address target, bytes4 selector) external returns (address[] memory); +} diff --git a/src/contracts/interfaces/IRewardsCoordinator.sol b/src/contracts/interfaces/IRewardsCoordinator.sol index 6442223b5..29ce6e693 100644 --- a/src/contracts/interfaces/IRewardsCoordinator.sol +++ b/src/contracts/interfaces/IRewardsCoordinator.sol @@ -8,6 +8,8 @@ import "./IStrategy.sol"; interface IRewardsCoordinatorErrors { /// @dev Thrown when msg.sender is not allowed to call a function error UnauthorizedCaller(); + /// @dev Thrown when a earner is not an operator + error EarnerNotOperator(); /// Invalid Inputs @@ -276,9 +278,7 @@ interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorE * @dev This function will revert if the `rewardsSubmission` is malformed, * e.g. if the `strategies` and `weights` arrays are of non-equal lengths */ - function createAVSRewardsSubmission( - RewardsSubmission[] calldata rewardsSubmissions - ) external; + function createAVSRewardsSubmission(address avs, RewardsSubmission[] calldata rewardsSubmissions) external; /** * @notice similar to `createAVSRewardsSubmission` except the rewards are split amongst *all* stakers @@ -332,14 +332,24 @@ interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorE ) external; /** - * @notice Sets the address of the entity that can call `processClaim` on behalf of the earner (msg.sender) + * @notice Sets the address of the entity that can call `processClaim` on ehalf of an earner * @param claimer The address of the entity that can call `processClaim` on behalf of the earner - * @dev Only callable by the `earner` + * @dev Assumes msg.sender is the earner */ function setClaimerFor( address claimer ) external; + /** + * @notice Sets the address of the entity that can call `processClaim` on behalf of an earner + * @param earner The address to set the claimer for + * @param claimer The address of the entity that can call `processClaim` on behalf of the earner + * @dev Only callable by operators, because we have no way of determining whether an address + * is an AVS via the AVS Directory. Once we deprecate that contract this function + * will be updated to allow AVSs to set their own claimers. + */ + function setClaimerFor(address earner, address claimer) external; + /** * @notice Sets the delay in timestamp before a posted root can be claimed against * @dev Only callable by the contract owner diff --git a/src/contracts/mixins/PermissionControllerMixin.sol b/src/contracts/mixins/PermissionControllerMixin.sol new file mode 100644 index 000000000..ac7d5cdd5 --- /dev/null +++ b/src/contracts/mixins/PermissionControllerMixin.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import "../interfaces/IPermissionController.sol"; + +abstract contract PermissionControllerMixin { + /// @dev Thrown when the caller is not allowed to call a function on behalf of an account. + error InvalidPermissions(); + + /// @notice Pointer to the permission controller contract. + IPermissionController public immutable permissionController; + + constructor( + IPermissionController _permissionController + ) { + permissionController = _permissionController; + } + + /// @notice Checks if the caller (msg.sender) can call on behalf of an account. + modifier checkCanCall( + address account + ) { + require(permissionController.canCall(account, msg.sender, address(this), msg.sig), InvalidPermissions()); + _; + } + + /** + * @notice Checks if the caller is allowed to call a function on behalf of an account. + * @param account the account to check + * @dev `msg.sender` is the caller to check that can call the function on behalf of `account`. + * @dev Returns a bool, instead of reverting + */ + function _checkCanCall( + address account + ) internal returns (bool) { + return permissionController.canCall(account, msg.sender, address(this), msg.sig); + } +} diff --git a/src/contracts/permissions/PermissionController.sol b/src/contracts/permissions/PermissionController.sol new file mode 100644 index 000000000..1441a138c --- /dev/null +++ b/src/contracts/permissions/PermissionController.sol @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import "./PermissionControllerStorage.sol"; + +contract PermissionController is Initializable, PermissionControllerStorage { + using EnumerableSet for *; + + modifier onlyAdmin( + address account + ) { + require(isAdmin(account, msg.sender), NotAdmin()); + _; + } + + /** + * + * INITIALIZING FUNCTIONS + * + */ + constructor() { + _disableInitializers(); + } + + function initialize() external initializer {} + + /** + * + * EXTERNAL FUNCTIONS + * + */ + + /// @inheritdoc IPermissionController + function setAdmin(address account, address admin) external onlyAdmin(account) { + // Add the admin to the account's admins + // If the admin is already set, the set will fail + EnumerableSet.AddressSet storage admins = _permissions[account].admins; + require(admins.add(admin), AdminAlreadySet()); + + emit AdminSet(account, admin); + } + + function removeAdmin(address account, address admin) external onlyAdmin(account) { + EnumerableSet.AddressSet storage admins = _permissions[account].admins; + + // Remove the admin from the account's admins + // If the admin is not set, the remove will fail + require(admins.remove(admin), AdminNotSet()); + + emit AdminRemoved(account, admin); + } + + /// @inheritdoc IPermissionController + function setDelegate( + address account, + address delegate, + address target, + bytes4 selector + ) external onlyAdmin(account) { + AccountPermissions storage permissions = _permissions[account]; + + bytes32 targetSelector = _encodeTargetSelector(target, selector); + require(!permissions.delegatePermissions[delegate].contains(targetSelector), DelegateAlreadySet()); + + // Add the delegate to the account's permissions + permissions.delegatePermissions[delegate].add(targetSelector); + permissions.permissionDelegates[targetSelector].add(delegate); + + emit DelegateSet(account, delegate, target, selector); + } + + /// @inheritdoc IPermissionController + function removeDelegate( + address account, + address delegate, + address target, + bytes4 selector + ) external onlyAdmin(account) { + AccountPermissions storage permissions = _permissions[account]; + + bytes32 targetSelector = _encodeTargetSelector(target, selector); + require(permissions.delegatePermissions[delegate].contains(targetSelector), DelegateNotSet()); + + // Remove the delegate from the account's permissions + permissions.delegatePermissions[delegate].remove(targetSelector); + permissions.permissionDelegates[targetSelector].remove(delegate); + + emit DelegateRemoved(account, delegate, target, selector); + } + + /** + * + * INTERNAL FUNCTIONS + * + */ + + /// @dev Encodes the target and selector into a single bytes32 value + function _encodeTargetSelector(address target, bytes4 selector) internal pure returns (bytes32) { + return bytes32(abi.encodePacked(target, uint96(bytes12((selector))))); + } + + /// @dev Decodes the target and selector from a single bytes32 value + function _decodeTargetSelector( + bytes32 targetSelector + ) internal view returns (address, bytes4) { + address target = address(uint160(uint256(targetSelector) >> 96)); + // The selector is in the lower 32 bits of the targetSelector + bytes4 selector = bytes4(uint32(uint256(targetSelector) >> 64)); + + return (target, selector); + } + + /** + * + * VIEW FUNCTIONS + * + */ + + /// @inheritdoc IPermissionController + function isAdmin(address account, address caller) public view returns (bool) { + if (_permissions[account].admins.length() == 0) { + // If the account does not have an admin, the caller must be the account + return account == caller; + } else { + // If the account has an admin, the caller must be an admin + return _permissions[account].admins.contains(caller); + } + } + + /// @inheritdoc IPermissionController + function getAdmins( + address account + ) external view returns (address[] memory) { + if (_permissions[account].admins.length() == 0) { + address[] memory admin = new address[](1); + admin[0] = account; + return admin; + } else { + return _permissions[account].admins.values(); + } + } + + /// @inheritdoc IPermissionController + function canCall(address account, address caller, address target, bytes4 selector) external view returns (bool) { + return isAdmin(account, caller) + || _permissions[account].delegatePermissions[caller].contains(_encodeTargetSelector(target, selector)); + } + + /// @inheritdoc IPermissionController + function getDelegatePermissions( + address account, + address delegate + ) external view returns (address[] memory, bytes4[] memory) { + EnumerableSet.Bytes32Set storage delegatePermissions = _permissions[account].delegatePermissions[delegate]; + + uint256 length = delegatePermissions.length(); + + address[] memory targets = new address[](length); + bytes4[] memory selectors = new bytes4[](length); + + for (uint256 i = 0; i < length; i++) { + (address target, bytes4 selector) = _decodeTargetSelector(delegatePermissions.at(i)); + targets[i] = target; + selectors[i] = selector; + } + + return (targets, selectors); + } + + /// @inheritdoc IPermissionController + function getDelegates(address account, address target, bytes4 selector) external view returns (address[] memory) { + bytes32 targetSelector = _encodeTargetSelector(target, selector); + return _permissions[account].permissionDelegates[targetSelector].values(); + } +} diff --git a/src/contracts/permissions/PermissionControllerStorage.sol b/src/contracts/permissions/PermissionControllerStorage.sol new file mode 100644 index 000000000..6dc0588c1 --- /dev/null +++ b/src/contracts/permissions/PermissionControllerStorage.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + +import "../interfaces/IPermissionController.sol"; + +abstract contract PermissionControllerStorage is IPermissionController { + using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableSet for EnumerableSet.AddressSet; + + struct AccountPermissions { + /// @notice The admins of the account + EnumerableSet.AddressSet admins; + /// @notice Mapping from a delegate to the list of encoded target & selectors + mapping(address delegate => EnumerableSet.Bytes32Set) delegatePermissions; + /// @notice Mapping from encoded target & selector to the list of delegates + mapping(bytes32 targetSelector => EnumerableSet.AddressSet) permissionDelegates; + } + + /// @notice Mapping from an account to its permission + mapping(address account => AccountPermissions) internal _permissions; + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[49] private __gap; +} diff --git a/src/test/DevnetLifecycle.t.sol b/src/test/DevnetLifecycle.t.sol index 546c94e13..d7e268fd3 100644 --- a/src/test/DevnetLifecycle.t.sol +++ b/src/test/DevnetLifecycle.t.sol @@ -150,7 +150,7 @@ contract Devnet_Lifecycle_Test is Test { IAllocationManagerTypes.CreateSetParams[] memory array = new IAllocationManagerTypes.CreateSetParams[](1); array[0] = createSetParams; - allocationManager.createOperatorSets(array); + allocationManager.createOperatorSets(avs, array); cheats.stopPrank(); } @@ -160,7 +160,7 @@ contract Devnet_Lifecycle_Test is Test { uint32[] memory operatorSetIds = new uint32[](1); operatorSetIds[0] = operatorSetId; - allocationManager.registerForOperatorSets(IAllocationManagerTypes.RegisterParams(avs, operatorSetIds, "")); + allocationManager.registerForOperatorSets(operator, IAllocationManagerTypes.RegisterParams(avs, operatorSetIds, "")); assertEq(allocationManager.getMembers(OperatorSet(avs, operatorSetId))[0], operator); } @@ -180,7 +180,7 @@ contract Devnet_Lifecycle_Test is Test { }); cheats.prank(operator); - allocationManager.modifyAllocations(allocations); + allocationManager.modifyAllocations(operator, allocations); // Assert storage IAllocationManagerTypes.Allocation memory info = allocationManager.getAllocation(operator, operatorSet, wethStrategy); @@ -209,7 +209,7 @@ contract Devnet_Lifecycle_Test is Test { // Slash operator cheats.prank(avs); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(avs, slashingParams); // Assert storage IAllocationManagerTypes.Allocation memory info = allocationManager.getAllocation(operator, operatorSet, wethStrategy); diff --git a/src/test/integration/IntegrationDeployer.t.sol b/src/test/integration/IntegrationDeployer.t.sol index ad158628e..ae02405c6 100644 --- a/src/test/integration/IntegrationDeployer.t.sol +++ b/src/test/integration/IntegrationDeployer.t.sol @@ -17,6 +17,7 @@ import "src/contracts/strategies/StrategyBaseTVLLimits.sol"; import "src/contracts/pods/EigenPodManager.sol"; import "src/contracts/pods/EigenPod.sol"; import "src/contracts/permissions/PauserRegistry.sol"; +import "src/contracts/permissions/PermissionController.sol"; import "src/test/mocks/EmptyContract.sol"; import "src/test/mocks/ETHDepositMock.sol"; @@ -244,6 +245,9 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser { allocationManager = AllocationManager( address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) ); + permissionController = PermissionController( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); // Deploy EigenPod Contracts eigenPodImplementation = new EigenPod( @@ -254,7 +258,7 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser { eigenPodBeacon = new UpgradeableBeacon(address(eigenPodImplementation)); // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs - delegationManagerImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, MIN_WITHDRAWAL_DELAY); + delegationManagerImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, permissionController, MIN_WITHDRAWAL_DELAY); strategyManagerImplementation = new StrategyManager(delegationManager, eigenLayerPauserReg); eigenPodManagerImplementation = new EigenPodManager( ethPOSDeposit, @@ -265,7 +269,8 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser { ); avsDirectoryImplementation = new AVSDirectory(delegationManager, eigenLayerPauserReg); strategyFactoryImplementation = new StrategyFactory(strategyManager, eigenLayerPauserReg); - allocationManagerImplementation = new AllocationManager(delegationManager, eigenLayerPauserReg, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY); + allocationManagerImplementation = new AllocationManager(delegationManager, eigenLayerPauserReg, permissionController, DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY); + permissionControllerImplementation = new PermissionController(); // Third, upgrade the proxy contracts to point to the implementations uint256 withdrawalDelayBlocks = 7 days / 12 seconds; @@ -325,6 +330,14 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser { 0 // initialPausedStatus ) ); + //PermissionController + eigenLayerProxyAdmin.upgradeAndCall( + ITransparentUpgradeableProxy(payable(address(permissionController))), + address(permissionControllerImplementation), + abi.encodeWithSelector( + PermissionController.initialize.selector + ) + ); // Create base strategy implementation and deploy a few strategies baseStrategyImplementation = new StrategyBase(strategyManager, eigenLayerPauserReg); @@ -392,7 +405,7 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser { ); // First, deploy the *implementation* contracts, using the *proxy contracts* as inputs - delegationManagerImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, MIN_WITHDRAWAL_DELAY); + delegationManagerImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, permissionController, MIN_WITHDRAWAL_DELAY); strategyManagerImplementation = new StrategyManager(delegationManager, eigenLayerPauserReg); eigenPodManagerImplementation = new EigenPodManager( ethPOSDeposit, @@ -479,7 +492,7 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser { ); // First, deploy the *implementation* contracts, using the *proxy contracts* as inputs - delegationManagerImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, MIN_WITHDRAWAL_DELAY); + delegationManagerImplementation = new DelegationManager(avsDirectory, strategyManager, eigenPodManager, allocationManager, eigenLayerPauserReg, permissionController, MIN_WITHDRAWAL_DELAY); strategyManagerImplementation = new StrategyManager(delegationManager, eigenLayerPauserReg); eigenPodManagerImplementation = new EigenPodManager( ethPOSDeposit, diff --git a/src/test/mocks/PermissionControllerMock.sol b/src/test/mocks/PermissionControllerMock.sol new file mode 100644 index 000000000..3fc6d65c3 --- /dev/null +++ b/src/test/mocks/PermissionControllerMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.9; + +import "forge-std/Test.sol"; +import "../../contracts/interfaces/IPermissionController.sol"; + +contract PermissionControllerMock is Test { + receive() external payable {} + fallback() external payable {} + + bool canCallResult = true; + + // Return true for now + function canCall(address account, address caller, address target, bytes4 selector) external view returns (bool) { + return canCallResult; + } + + function setCanCallResult(bool result) public { + canCallResult = result; + } +} \ No newline at end of file diff --git a/src/test/tree/PermissionControllerUnit.tree b/src/test/tree/PermissionControllerUnit.tree new file mode 100644 index 000000000..bfd961465 --- /dev/null +++ b/src/test/tree/PermissionControllerUnit.tree @@ -0,0 +1,27 @@ +. +└── PermissionController (**** denotes that integration tests are needed to fully validate path) + ├── when setAdmin is called + │ ├── given that the current admin it not set + │ │ └── given that the caller is not the account + │ │ └── it should revert + │ ├── given that the current admin is set + │ │ └── given taht the msg.sender is not the current admin + │ │ └── it should revert + │ ├── given that the new admin is the zero address + │ │ └── it should revert + │ └── given that a valid caller sets a valid admin + │ └── it should update the permissions of the account & emit an AdminSet event + ├── when setDelegate is called + │ ├── given that the caller is not the admin + │ │ └── it should revert + │ ├── given that the delegate already has permissions + │ │ └── it should revert + │ └── given that proper permissions are set + │ └── it should emit a DelegateSet event, and update the `delegatePermissions` and `permissionDelegate` mappings for the account + └── when removeDelegate is called + ├── given that the caller is not the admin + │ └── it should revert + ├── given that the delegate does not have permissions + │ └── it should revert + └── given that proper permissions are set + └── it should emit a DelegateRemoved event, and update the `delegatePermissions` and `permissionDelegate` mappings for the account \ No newline at end of file diff --git a/src/test/unit/AllocationManagerUnit.t.sol b/src/test/unit/AllocationManagerUnit.t.sol index 08162b783..c73d97313 100644 --- a/src/test/unit/AllocationManagerUnit.t.sol +++ b/src/test/unit/AllocationManagerUnit.t.sol @@ -86,6 +86,7 @@ contract AllocationManagerUnitTests is EigenLayerUnitTestSetup, IAllocationManag new AllocationManager( IDelegationManager(address(delegationManagerMock)), _pauserRegistry, + IPermissionController(address(permissionControllerMock)), DEALLOCATION_DELAY, ALLOCATION_CONFIGURATION_DELAY ) @@ -105,7 +106,7 @@ contract AllocationManagerUnitTests is EigenLayerUnitTestSetup, IAllocationManag function _setAllocationDelay(address operator, uint32 delay) internal { cheats.prank(operator); - allocationManager.setAllocationDelay(delay); + allocationManager.setAllocationDelay(operator, delay); cheats.roll(block.number + ALLOCATION_CONFIGURATION_DELAY); } @@ -115,6 +116,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() ); return operatorSet; @@ -128,12 +130,13 @@ contract AllocationManagerUnitTests is EigenLayerUnitTestSetup, IAllocationManag } cheats.prank(operatorSets[0].avs); - allocationManager.createOperatorSets(createSetParams); + allocationManager.createOperatorSets(operatorSets[0].avs, createSetParams); } function _registerForOperatorSet(address operator, OperatorSet memory operatorSet) internal { cheats.prank(operator); allocationManager.registerForOperatorSets( + operator, RegisterParams({avs: operatorSet.avs, operatorSetIds: operatorSet.id.toArrayU32(), data: ""}) ); } @@ -142,6 +145,7 @@ contract AllocationManagerUnitTests is EigenLayerUnitTestSetup, IAllocationManag cheats.startPrank(operator); for (uint256 i; i < operatorSets.length; ++i) { allocationManager.registerForOperatorSets( + operator, RegisterParams({avs: operatorSets[i].avs, operatorSetIds: operatorSets[i].id.toArrayU32(), data: ""}) ); } @@ -384,7 +388,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests function test_revert_paused() public { allocationManager.pause(2 ** PAUSED_OPERATOR_SLASHING); cheats.expectRevert(IPausable.CurrentlyPaused.selector); - allocationManager.slashOperator(_randSlashingParams(defaultOperator, 0)); + allocationManager.slashOperator(defaultAVS, _randSlashingParams(defaultOperator, 0)); } function test_revert_slashZero() public { @@ -393,7 +397,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests cheats.prank(defaultAVS); cheats.expectRevert(InvalidWadToSlash.selector); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); } function test_revert_slashGreaterThanWAD() public { @@ -402,23 +406,24 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests cheats.prank(defaultAVS); cheats.expectRevert(InvalidWadToSlash.selector); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); } function test_revert_NotMemberOfSet() public { cheats.prank(defaultAVS); cheats.expectRevert(NotMemberOfSet.selector); - allocationManager.slashOperator(_randSlashingParams(random().Address(), 0)); + allocationManager.slashOperator(defaultAVS, _randSlashingParams(random().Address(), 0)); } function test_revert_operatorAllocated_notActive() public { AllocateParams[] memory allocateParams = _newAllocateParams(defaultOperatorSet, WAD); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.prank(defaultAVS); allocationManager.slashOperator( + defaultAVS, SlashingParams({ operator: defaultOperator, operatorSetId: allocateParams[0].operatorSet.id, @@ -459,7 +464,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests AllocateParams[] memory allocateParams = _newAllocateParams(defaultOperatorSet, WAD); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); _checkSlashEvents(defaultOperator, defaultOperatorSet, defaultStrategies, uint256(25e16).toArrayU256(), "test"); @@ -467,6 +472,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash operator for 25% cheats.prank(defaultAVS); allocationManager.slashOperator( + defaultAVS, SlashingParams({ operator: defaultOperator, operatorSetId: defaultOperatorSet.id, @@ -505,7 +511,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Allocate magnitude and roll forward to completable block cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); SlashingParams memory slashingParams = _randSlashingParams(defaultOperator, defaultOperatorSet.id); @@ -525,7 +531,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash Operator cheats.prank(defaultAVS); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); // Check storage assertEq( @@ -560,13 +566,13 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Generate allocation for `strategyMock`, we allocate half AllocateParams[] memory allocateParams = _newAllocateParams(defaultOperatorSet, 5e17); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); // Allocate the other half AllocateParams[] memory allocateParams2 = _newAllocateParams(defaultOperatorSet, WAD); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams2); + allocationManager.modifyAllocations(defaultOperator, allocateParams2); uint32 secondAllocEffectBlock = uint32(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); // Slash operator for 50% @@ -591,7 +597,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash Operator cheats.prank(defaultAVS); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); // Check storage assertEq( @@ -646,7 +652,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests AllocateParams[] memory allocateParams = _newAllocateParams(defaultOperatorSet, WAD); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); // 1. Slash operator for 99% in opSet 0 bringing their magnitude to 1e16 @@ -671,7 +677,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash Operator cheats.prank(defaultAVS); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); // Check storage assertEq( @@ -708,7 +714,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests }); cheats.prank(defaultAVS); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); // Check storage assertEq( @@ -747,7 +753,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash Operator cheats.prank(defaultAVS); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); // Check storage assertEq( @@ -780,13 +786,13 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Generate allocation for `strategyMock`, we allocate half AllocateParams[] memory allocateParams = _newAllocateParams(defaultOperatorSet, initialMagnitude); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); // Deallocate half AllocateParams[] memory deallocateParams = _newAllocateParams(defaultOperatorSet, initialMagnitude / 2); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(deallocateParams); + allocationManager.modifyAllocations(defaultOperator, deallocateParams); uint32 deallocationEffectBlock = uint32(block.number + DEALLOCATION_DELAY); // Slash operator for 25% @@ -813,7 +819,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash Operator // First event is emitted because of deallocation cheats.prank(defaultAVS); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); // Check storage post slash assertEq( @@ -861,7 +867,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Allocate all magnitude AllocateParams[] memory allocateParams = _newAllocateParams(defaultOperatorSet, WAD); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); _checkSlashEvents({ @@ -875,6 +881,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash operator for 100% cheats.prank(defaultAVS); allocationManager.slashOperator( + defaultAVS, SlashingParams({ operator: defaultOperator, operatorSetId: allocateParams[0].operatorSet.id, @@ -890,7 +897,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Attempt to allocate cheats.prank(defaultOperator); cheats.expectRevert(InsufficientMagnitude.selector); - allocationManager.modifyAllocations(allocateParams2); + allocationManager.modifyAllocations(defaultOperator, allocateParams2); } /** @@ -902,12 +909,12 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests function test_allocateAll_deallocateAll() public { // Allocate all magnitude cheats.prank(defaultOperator); - allocationManager.modifyAllocations(_newAllocateParams(defaultOperatorSet, WAD)); + allocationManager.modifyAllocations(defaultOperator, _newAllocateParams(defaultOperatorSet, WAD)); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); // Deallocate all cheats.prank(defaultOperator); - allocationManager.modifyAllocations(_newAllocateParams(defaultOperatorSet, 0)); + allocationManager.modifyAllocations(defaultOperator, _newAllocateParams(defaultOperatorSet, 0)); _checkSlashEvents({ operator: defaultOperator, @@ -920,6 +927,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash operator for 100% cheats.prank(defaultAVS); allocationManager.slashOperator( + defaultAVS, SlashingParams({ operator: defaultOperator, operatorSetId: defaultOperatorSet.id, @@ -952,13 +960,13 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Allocate all magnitude AllocateParams[] memory allocateParams = _newAllocateParams(defaultOperatorSet, WAD); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); // Deallocate half AllocateParams[] memory deallocateParams = _newAllocateParams(defaultOperatorSet, 5e17); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(deallocateParams); + allocationManager.modifyAllocations(defaultOperator, deallocateParams); uint32 deallocationEffectBlock = uint32(block.number + DEALLOCATION_DELAY); // Check storage post deallocation @@ -994,7 +1002,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash Operator, only emit events assuming that there is no deallocation cheats.prank(defaultAVS); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); // Check storage post slash assertEq( @@ -1055,7 +1063,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests _registerForOperatorSet(defaultOperator, operatorSet2); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); // Get slashable shares for each operatorSet @@ -1092,7 +1100,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash Operator cheats.prank(defaultAVS); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); // Operator should now have 80e18 shares, since half of 40e18 was slashed delegationManagerMock.setOperatorShares(defaultOperator, strategyMock, 80e18); @@ -1160,7 +1168,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests allocateParams.newMagnitudes[1] = strategy2Magnitude; cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams.toArray()); + allocationManager.modifyAllocations(defaultOperator, allocateParams.toArray()); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); // Slash operator on both strategies for 60% @@ -1193,7 +1201,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Slash Operator cheats.prank(defaultAVS); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); // Check storage for (uint256 i; i < strategies.length; ++i) { @@ -1227,15 +1235,15 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests IStrategy strategy = allocateParams[0].strategies[0]; cheats.prank(defaultAVS); - allocationManager.createOperatorSets(createSetParams); + allocationManager.createOperatorSets(defaultAVS, createSetParams); _registerForOperatorSet(defaultOperator, operatorSet); // Allocate some magnitude, then deallocate some. cheats.startPrank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); - allocationManager.modifyAllocations(deallocateParams); + allocationManager.modifyAllocations(defaultOperator, deallocateParams); cheats.roll(block.number + DEALLOCATION_DELAY); cheats.stopPrank(); @@ -1261,7 +1269,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests }); cheats.prank(defaultAVS); - allocationManager.slashOperator(slashingParams); + allocationManager.slashOperator(defaultAVS, slashingParams); assertEq( currentMagnitude, @@ -1294,7 +1302,7 @@ contract AllocationManagerUnitTests_SlashOperator is AllocationManagerUnitTests // Allocate up to max magnitude AllocateParams[] memory allocateParams2 = _newAllocateParams(operatorSet, uint64(maxMagnitude)); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams2); + allocationManager.modifyAllocations(defaultOperator, allocateParams2); // Assert that encumbered is expectedMaxMagnitude assertEq( @@ -1310,14 +1318,14 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe function test_revert_paused() public { allocationManager.pause(2 ** PAUSED_MODIFY_ALLOCATIONS); cheats.expectRevert(IPausable.CurrentlyPaused.selector); - allocationManager.modifyAllocations(new AllocateParams[](0)); + allocationManager.modifyAllocations(address(this), new AllocateParams[](0)); } function test_revert_allocationDelayNotSet() public { address invalidOperator = address(0x2); cheats.prank(invalidOperator); cheats.expectRevert(UninitializedAllocationDelay.selector); - allocationManager.modifyAllocations(new AllocateParams[](0)); + allocationManager.modifyAllocations(invalidOperator, new AllocateParams[](0)); } function test_revert_allocationDelayNotInEffect() public { @@ -1325,11 +1333,11 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe _registerOperator(operator); cheats.startPrank(operator); - allocationManager.setAllocationDelay(5); + allocationManager.setAllocationDelay(operator, 5); // even though the operator has an allocation delay set, it is not in effect // and modifyAllocations should still be blocked cheats.expectRevert(UninitializedAllocationDelay.selector); - allocationManager.modifyAllocations(new AllocateParams[](0)); + allocationManager.modifyAllocations(operator, new AllocateParams[](0)); } function test_revert_lengthMismatch() public { @@ -1338,7 +1346,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe cheats.expectRevert(InputArrayLengthMismatch.selector); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); } function test_revert_invalidOperatorSet() public { @@ -1350,21 +1358,21 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe cheats.expectRevert(InvalidOperatorSet.selector); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); } function test_revert_multiAlloc_modificationAlreadyPending_diffTx() public { // Allocate magnitude AllocateParams[] memory allocateParams = _randAllocateParams_DefaultOpSet(); cheats.startPrank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Warp to just before allocation complete block cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY - 1); // Attempt to allocate magnitude again cheats.expectRevert(ModificationAlreadyPending.selector); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); cheats.stopPrank(); } @@ -1376,7 +1384,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe cheats.expectRevert(ModificationAlreadyPending.selector); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); } function test_revert_allocateZeroMagnitude() public { @@ -1386,14 +1394,14 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe cheats.expectRevert(SameMagnitude.selector); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); } function test_revert_allocateSameMagnitude() public { // Allocate nonzero magnitude AllocateParams[] memory allocateParams = _randAllocateParams_DefaultOpSet(); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Warp to allocation complete block cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); @@ -1401,7 +1409,26 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe // Attempt to allocate no magnitude (ie. same magnitude) cheats.expectRevert(SameMagnitude.selector); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); + } + + function testFuzz_revert_insufficientAllocatableMagnitude( + Randomness r + ) public rand(r) { + // Allocate some magnitude + AllocateParams[] memory allocateParams = _randAllocateParams_DefaultOpSet(); + cheats.prank(defaultOperator); + allocationManager.modifyAllocations(defaultOperator, allocateParams); + + // Warp to allocation complete block + cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); + + // Attempt to allocate more magnitude than the operator has + // uint64 allocatedMag = allocateParams[0].newMagnitudes[0]; + allocateParams[0].newMagnitudes[0] = WAD + 1; + cheats.expectRevert(InsufficientMagnitude.selector); + cheats.prank(defaultOperator); + allocationManager.modifyAllocations(defaultOperator, allocateParams); } function testFuzz_revert_overAllocate( @@ -1421,27 +1448,60 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe // Overallocate cheats.expectRevert(InsufficientMagnitude.selector); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); - } + allocationManager.modifyAllocations(defaultOperator, allocateParams); + } + + // TODO: yash + // function test_allocateMaxToMultipleStrategies( + // Randomness r + // ) public rand(r) { + // // Create a handful of operator sets under the same AVS, each with a unique strategy + // OperatorSet[] memory operatorSets = _newOperatorSets_SingleUniqueStrategy(defaultAVS, r.Uint256(2, 10)); + + // // Register for each operator set + // _registerForOperatorSets(defaultOperator, operatorSets); + + // // Allocate max to each operator set + // AllocateParams[] memory allocateParams = new AllocateParams[](operatorSets.length); + // for (uint256 i = 0; i < operatorSets.length; i++) { + // allocateParams[i] = AllocateParams({ + // operatorSet: operatorSets[i], + // strategies: allocationManager.getStrategiesInOperatorSet(operatorSets[i]), + // newMagnitudes: WAD.toArrayU64() + // }); + // } + + // cheats.prank(defaultOperator); + // allocationManager.modifyAllocations(defaultOperator, allocateParams); + + // // Ensure encumbered magnitude is updated for each strategy + // for (uint256 i = 0; i < allocateParams.length; i++) { + // assertEq( + // WAD, + // allocationManager.encumberedMagnitude(defaultOperator, allocateParams[i].strategies[0]), + // "encumberedMagnitude not max" + // ); + // } + // } function test_revert_allocateDeallocate_modificationPending() public { // Allocate AllocateParams[] memory allocateParams = _randAllocateParams_DefaultOpSet(); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Deallocate allocateParams[0].newMagnitudes[0] -= 1; cheats.expectRevert(ModificationAlreadyPending.selector); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); } function test_revert_deallocateTwice_modificationPending() public { // Allocate AllocateParams[] memory allocateParams = _randAllocateParams_DefaultOpSet(); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Warp past allocation complete timestsamp cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); @@ -1449,12 +1509,12 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe // Deallocate allocateParams[0].newMagnitudes[0] -= 1; cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Deallocate again -> expect revert cheats.expectRevert(ModificationAlreadyPending.selector); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); } /// @dev Set allocation delay > ALLOCATION_CONFIGURATION_DELAY, allocate, @@ -1470,17 +1530,17 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe uint64 half = 0.5 ether; cheats.prank(defaultAVS); - allocationManager.createOperatorSets(CreateSetParams(1, defaultStrategies).toArray()); + allocationManager.createOperatorSets(defaultAVS, CreateSetParams(1, defaultStrategies).toArray()); cheats.startPrank(defaultOperator); - allocationManager.setAllocationDelay(firstDelay); + allocationManager.setAllocationDelay(defaultOperator, firstDelay); - allocationManager.modifyAllocations(_newAllocateParams(defaultOperatorSet, half)); + allocationManager.modifyAllocations(defaultOperator, _newAllocateParams(defaultOperatorSet, half)); - allocationManager.setAllocationDelay(secondDelay); + allocationManager.setAllocationDelay(defaultOperator, secondDelay); cheats.roll(block.number + secondDelay); - allocationManager.modifyAllocations(_newAllocateParams(OperatorSet(defaultAVS, 1), half)); + allocationManager.modifyAllocations(defaultOperator, _newAllocateParams(OperatorSet(defaultAVS, 1), half)); cheats.stopPrank(); } @@ -1513,7 +1573,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe // Allocate magnitude cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Check storage @@ -1589,7 +1649,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe } cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Check storage assertEq( @@ -1657,7 +1717,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe // Allocate magnitude AllocateParams[] memory allocateParams = _newAllocateParams(defaultOperatorSet, firstAlloc); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Warp to allocation complete block cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); @@ -1675,7 +1735,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe }); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Check storage assertEq( @@ -1716,6 +1776,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe cheats.prank(defaultOperator); allocationManager.modifyAllocations( + defaultOperator, AllocateParams({operatorSet: operatorSet, strategies: strategies, newMagnitudes: WAD.toArrayU64(numStrats)}) .toArray() ); @@ -1756,7 +1817,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe }); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Warp to allocation complete block cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); @@ -1774,7 +1835,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe }); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Check storage after dealloc assertEq( @@ -1847,7 +1908,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe }); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); assertEq( firstMod, @@ -1881,7 +1942,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe }); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Check storage after dealloc assertEq( @@ -1939,7 +2000,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe } cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); assertEq( allocationManager.getAllocatableMagnitude(defaultOperator, strategyMock), @@ -1965,7 +2026,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe } cheats.prank(defaultOperator); - allocationManager.modifyAllocations(deallocateParams); + allocationManager.modifyAllocations(defaultOperator, deallocateParams); assertEq( allocationManager.getAllocatableMagnitude(defaultOperator, strategyMock), @@ -2000,7 +2061,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe }); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(finalAllocParams); + allocationManager.modifyAllocations(defaultOperator, finalAllocParams); // Check that all magnitude will be allocated to the new set, and each prior set // has a zeroed-out allocation @@ -2045,7 +2106,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe }); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Warp to allocation complete block cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); @@ -2063,7 +2124,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe }); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Warp to completion and clear deallocation queue cheats.roll(block.number + DEALLOCATION_DELAY); @@ -2109,7 +2170,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe } cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); uint64 encumberedMagnitudeAfterAllocation = allocationManager.encumberedMagnitude(defaultOperator, strategyMock); // Warp to allocation complete block @@ -2131,7 +2192,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe } cheats.prank(defaultOperator); - allocationManager.modifyAllocations(deallocateParams); + allocationManager.modifyAllocations(defaultOperator, deallocateParams); // Check storage after dealloc assertEq( @@ -2182,7 +2243,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe CreateSetParams[] memory createSetParams = r.CreateSetParams(allocateParams); cheats.prank(defaultAVS); - allocationManager.createOperatorSets(createSetParams); + allocationManager.createOperatorSets(defaultAVS, createSetParams); for (uint256 i; i < allocateParams.length; ++i) { for (uint256 j; j < allocateParams[i].strategies.length; ++j) { @@ -2198,7 +2259,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe } cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); for (uint256 i; i < allocateParams.length; ++i) { for (uint256 j = 0; j < allocateParams[i].strategies.length; j++) { @@ -2229,7 +2290,7 @@ contract AllocationManagerUnitTests_ModifyAllocations is AllocationManagerUnitTe } cheats.prank(defaultOperator); - allocationManager.modifyAllocations(deallocateParams); + allocationManager.modifyAllocations(defaultOperator, deallocateParams); // Deallocations are immediate if the operator's allocation is not slashable. for (uint256 i; i < allocateParams.length; ++i) { @@ -2281,7 +2342,7 @@ contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerU // Allocate magnitude cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Attempt to clear queue, assert no events emitted allocationManager.clearDeallocationQueue(defaultOperator, defaultStrategies, _maxNumToClear()); @@ -2321,14 +2382,14 @@ contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerU // Allocate cheats.prank(defaultOperator); - allocationManager.modifyAllocations(allocateParams); + allocationManager.modifyAllocations(defaultOperator, allocateParams); // Roll to allocation complete block cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); // Deallocate cheats.prank(defaultOperator); - allocationManager.modifyAllocations(deallocateParams); + allocationManager.modifyAllocations(defaultOperator, deallocateParams); // Clear queue - since we have not rolled forward, this should be a no-op allocationManager.clearDeallocationQueue(defaultOperator, defaultStrategies, _maxNumToClear()); @@ -2375,14 +2436,14 @@ contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerU // Allocate half of mag to default operator set AllocateParams[] memory firstAllocation = _newAllocateParams(defaultOperatorSet, 5e17); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(firstAllocation); + allocationManager.modifyAllocations(defaultOperator, firstAllocation); cheats.roll(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); // Deallocate half from default operator set uint32 deallocationEffectBlock = uint32(block.number + DEALLOCATION_DELAY); AllocateParams[] memory firstDeallocation = _newAllocateParams(defaultOperatorSet, 25e16); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(firstDeallocation); + allocationManager.modifyAllocations(defaultOperator, firstDeallocation); Allocation memory allocation = allocationManager.getAllocation(defaultOperator, defaultOperatorSet, strategyMock); assertEq(deallocationEffectBlock, allocation.effectBlock, "effect block not correct"); @@ -2396,7 +2457,7 @@ contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerU uint32 allocationEffectBlock = uint32(block.number + DEFAULT_OPERATOR_ALLOCATION_DELAY); AllocateParams[] memory secondAllocation = _newAllocateParams(newOperatorSet, 33e16); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(secondAllocation); + allocationManager.modifyAllocations(defaultOperator, secondAllocation); allocation = allocationManager.getAllocation(defaultOperator, newOperatorSet, strategyMock); assertEq(allocationEffectBlock, allocation.effectBlock, "effect block not correct"); @@ -2411,7 +2472,7 @@ contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerU // Validate that we can allocate again for opset2. This should not revert AllocateParams[] memory thirdAllocation = _newAllocateParams(newOperatorSet, 10e16); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(thirdAllocation); + allocationManager.modifyAllocations(defaultOperator, thirdAllocation); } /** @@ -2428,7 +2489,7 @@ contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerU // Set allocation delay to be longer than the deallocation delay uint32 allocationDelay = DEALLOCATION_DELAY * 2; cheats.prank(defaultOperator); - allocationManager.setAllocationDelay(allocationDelay); + allocationManager.setAllocationDelay(defaultOperator, allocationDelay); cheats.roll(block.number + ALLOCATION_CONFIGURATION_DELAY); (, uint32 storedDelay) = allocationManager.getAllocationDelay(defaultOperator); assertEq(allocationDelay, storedDelay, "allocation delay not valid"); @@ -2436,7 +2497,7 @@ contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerU // Allocate half of mag to default operator set AllocateParams[] memory firstAllocation = _newAllocateParams(defaultOperatorSet, 5e17); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(firstAllocation); + allocationManager.modifyAllocations(defaultOperator, firstAllocation); cheats.roll(block.number + allocationDelay); // Create and register for a second operator set @@ -2447,7 +2508,7 @@ contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerU // Allocate half of mag to opset2 AllocateParams[] memory secondAllocation = _newAllocateParams(newOperatorSet, 5e17); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(secondAllocation); + allocationManager.modifyAllocations(defaultOperator, secondAllocation); uint32 allocationEffectBlock = uint32(block.number + allocationDelay); Allocation memory allocation = allocationManager.getAllocation(defaultOperator, newOperatorSet, strategyMock); @@ -2457,7 +2518,7 @@ contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerU uint32 deallocationEffectBlock = uint32(block.number + DEALLOCATION_DELAY); AllocateParams[] memory firstDeallocation = _newAllocateParams(defaultOperatorSet, 0); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(firstDeallocation); + allocationManager.modifyAllocations(defaultOperator, firstDeallocation); allocation = allocationManager.getAllocation(defaultOperator, defaultOperatorSet, strategyMock); assertEq(deallocationEffectBlock, allocation.effectBlock, "effect block not correct"); @@ -2473,7 +2534,7 @@ contract AllocationManagerUnitTests_ClearDeallocationQueue is AllocationManagerU ); AllocateParams[] memory thirdAllocation = _newAllocateParams(defaultOperatorSet, 5e17); cheats.prank(defaultOperator); - allocationManager.modifyAllocations(thirdAllocation); + allocationManager.modifyAllocations(defaultOperator, thirdAllocation); } } @@ -2493,7 +2554,7 @@ contract AllocationManagerUnitTests_SetAllocationDelay is AllocationManagerUnitT delegationManagerMock.setIsOperator(operatorToSet, false); cheats.prank(operatorToSet); cheats.expectRevert(OperatorNotRegistered.selector); - allocationManager.setAllocationDelay(1); + allocationManager.setAllocationDelay(operatorToSet, 1); } function testFuzz_setDelay( @@ -2505,7 +2566,7 @@ contract AllocationManagerUnitTests_SetAllocationDelay is AllocationManagerUnitT cheats.expectEmit(true, true, true, true, address(allocationManager)); emit AllocationDelaySet(operatorToSet, delay, uint32(block.number + ALLOCATION_CONFIGURATION_DELAY)); cheats.prank(operatorToSet); - allocationManager.setAllocationDelay(delay); + allocationManager.setAllocationDelay(operatorToSet, delay); // Check values after set (bool isSet, uint32 returnedDelay) = allocationManager.getAllocationDelay(operatorToSet); @@ -2529,7 +2590,7 @@ contract AllocationManagerUnitTests_SetAllocationDelay is AllocationManagerUnitT // Set delay cheats.prank(operatorToSet); - allocationManager.setAllocationDelay(firstDelay); + allocationManager.setAllocationDelay(operatorToSet, firstDelay); // Warp just before effect block cheats.roll(block.number + ALLOCATION_CONFIGURATION_DELAY - 1); @@ -2538,7 +2599,7 @@ contract AllocationManagerUnitTests_SetAllocationDelay is AllocationManagerUnitT cheats.expectEmit(true, true, true, true, address(allocationManager)); emit AllocationDelaySet(operatorToSet, secondDelay, uint32(block.number + ALLOCATION_CONFIGURATION_DELAY)); cheats.prank(operatorToSet); - allocationManager.setAllocationDelay(secondDelay); + allocationManager.setAllocationDelay(operatorToSet, secondDelay); // Warp to effect block of first delay cheats.roll(block.number + 1); @@ -2563,14 +2624,14 @@ contract AllocationManagerUnitTests_SetAllocationDelay is AllocationManagerUnitT // Set delay cheats.prank(operatorToSet); - allocationManager.setAllocationDelay(firstDelay); + allocationManager.setAllocationDelay(operatorToSet, firstDelay); // Warp to effect block of first delay cheats.roll(block.number + ALLOCATION_CONFIGURATION_DELAY); // Set delay again cheats.prank(operatorToSet); - allocationManager.setAllocationDelay(secondDelay); + allocationManager.setAllocationDelay(operatorToSet, secondDelay); // Assert that first delay is set (bool isSet, uint32 returnedDelay) = allocationManager.getAllocationDelay(operatorToSet); @@ -2615,7 +2676,7 @@ contract AllocationManagerUnitTests_registerForOperatorSets is AllocationManager function test_registerForOperatorSets_Paused() public { allocationManager.pause(2 ** PAUSED_OPERATOR_SET_REGISTRATION_AND_DEREGISTRATION); cheats.expectRevert(IPausable.CurrentlyPaused.selector); - allocationManager.registerForOperatorSets(defaultRegisterParams); + allocationManager.registerForOperatorSets(defaultOperator, defaultRegisterParams); } function testFuzz_registerForOperatorSets_InvalidOperator( @@ -2623,7 +2684,7 @@ contract AllocationManagerUnitTests_registerForOperatorSets is AllocationManager ) public rand(r) { cheats.prank(r.Address()); cheats.expectRevert(InvalidOperator.selector); - allocationManager.registerForOperatorSets(defaultRegisterParams); + allocationManager.registerForOperatorSets(r.Address(), defaultRegisterParams); } function testFuzz_registerForOperatorSets_InvalidOperatorSet( @@ -2632,7 +2693,7 @@ contract AllocationManagerUnitTests_registerForOperatorSets is AllocationManager cheats.prank(defaultOperator); cheats.expectRevert(InvalidOperatorSet.selector); defaultRegisterParams.operatorSetIds[0] = 1; // invalid id - allocationManager.registerForOperatorSets(defaultRegisterParams); // invalid id + allocationManager.registerForOperatorSets(defaultOperator, defaultRegisterParams); // invalid id } function testFuzz_registerForOperatorSets_AlreadyMemberOfSet( @@ -2640,7 +2701,7 @@ contract AllocationManagerUnitTests_registerForOperatorSets is AllocationManager ) public rand(r) { cheats.prank(defaultOperator); cheats.expectRevert(AlreadyMemberOfSet.selector); - allocationManager.registerForOperatorSets(defaultRegisterParams); + allocationManager.registerForOperatorSets(defaultOperator, defaultRegisterParams); } function testFuzz_registerForOperatorSets_Correctness( @@ -2660,7 +2721,7 @@ contract AllocationManagerUnitTests_registerForOperatorSets is AllocationManager } cheats.prank(defaultAVS); - allocationManager.createOperatorSets(createSetParams); + allocationManager.createOperatorSets(defaultAVS, createSetParams); for (uint256 j; j < numOpSets; ++j) { cheats.expectEmit(true, true, false, false, address(allocationManager)); @@ -2672,7 +2733,7 @@ contract AllocationManagerUnitTests_registerForOperatorSets is AllocationManager ); cheats.prank(operator); - allocationManager.registerForOperatorSets(RegisterParams(defaultAVS, operatorSetIds, "")); + allocationManager.registerForOperatorSets(operator, RegisterParams(defaultAVS, operatorSetIds, "")); assertEq(allocationManager.getRegisteredSets(operator).length, numOpSets, "should be registered for all sets"); @@ -2702,14 +2763,6 @@ contract AllocationManagerUnitTests_deregisterFromOperatorSets is AllocationMana allocationManager.deregisterFromOperatorSets(defaultDeregisterParams); } - function testFuzz_deregisterFromOperatorSets_InvalidCaller( - Randomness r - ) public rand(r) { - cheats.prank(r.Address()); - cheats.expectRevert(InvalidCaller.selector); - allocationManager.deregisterFromOperatorSets(defaultDeregisterParams); - } - function testFuzz_deregisterFromOperatorSets_InvalidOperatorSet( Randomness r ) public rand(r) { @@ -2742,13 +2795,13 @@ contract AllocationManagerUnitTests_deregisterFromOperatorSets is AllocationMana } cheats.prank(defaultAVS); - allocationManager.createOperatorSets(createSetParams); + allocationManager.createOperatorSets(defaultAVS, createSetParams); address operator = r.Address(); _registerOperator(operator); cheats.prank(operator); - allocationManager.registerForOperatorSets(RegisterParams(defaultAVS, operatorSetIds, "")); + allocationManager.registerForOperatorSets(operator, RegisterParams(defaultAVS, operatorSetIds, "")); for (uint256 j; j < numOpSets; ++j) { cheats.expectEmit(true, true, false, false, address(allocationManager)); @@ -2778,13 +2831,13 @@ contract AllocationManagerUnitTests_addStrategiesToOperatorSet is AllocationMana function test_addStrategiesToOperatorSet_InvalidOperatorSet() public { cheats.prank(defaultAVS); cheats.expectRevert(InvalidOperatorSet.selector); - allocationManager.addStrategiesToOperatorSet(1, defaultStrategies); + allocationManager.addStrategiesToOperatorSet(defaultAVS, 1, defaultStrategies); } function test_addStrategiesToOperatorSet_StrategyAlreadyInOperatorSet() public { cheats.prank(defaultAVS); cheats.expectRevert(StrategyAlreadyInOperatorSet.selector); - allocationManager.addStrategiesToOperatorSet(defaultOperatorSet.id, defaultStrategies); + allocationManager.addStrategiesToOperatorSet(defaultAVS, defaultOperatorSet.id, defaultStrategies); } function testFuzz_addStrategiesToOperatorSet_Correctness( @@ -2801,7 +2854,7 @@ contract AllocationManagerUnitTests_addStrategiesToOperatorSet is AllocationMana } cheats.prank(defaultAVS); - allocationManager.addStrategiesToOperatorSet(defaultOperatorSet.id, strategies); + allocationManager.addStrategiesToOperatorSet(defaultAVS, defaultOperatorSet.id, strategies); IStrategy[] memory strategiesInSet = allocationManager.getStrategiesInOperatorSet(defaultOperatorSet); @@ -2817,14 +2870,14 @@ contract AllocationManagerUnitTests_removeStrategiesFromOperatorSet is Allocatio function test_removeStrategiesFromOperatorSet_InvalidOperatorSet() public { cheats.prank(defaultAVS); cheats.expectRevert(InvalidOperatorSet.selector); - allocationManager.removeStrategiesFromOperatorSet(1, defaultStrategies); + allocationManager.removeStrategiesFromOperatorSet(defaultAVS, 1, defaultStrategies); } function test_removeStrategiesFromOperatorSet_StrategyNotInOperatorSet() public { cheats.prank(defaultAVS); cheats.expectRevert(StrategyNotInOperatorSet.selector); allocationManager.removeStrategiesFromOperatorSet( - defaultOperatorSet.id, IStrategy(random().Address()).toArray() + defaultAVS, defaultOperatorSet.id, IStrategy(random().Address()).toArray() ); } @@ -2835,7 +2888,7 @@ contract AllocationManagerUnitTests_removeStrategiesFromOperatorSet is Allocatio IStrategy[] memory strategies = r.StrategyArray(numStrategies); cheats.prank(defaultAVS); - allocationManager.addStrategiesToOperatorSet(defaultOperatorSet.id, strategies); + allocationManager.addStrategiesToOperatorSet(defaultAVS, defaultOperatorSet.id, strategies); for (uint256 i; i < numStrategies; ++i) { cheats.expectEmit(true, false, false, false, address(allocationManager)); @@ -2847,7 +2900,7 @@ contract AllocationManagerUnitTests_removeStrategiesFromOperatorSet is Allocatio ); cheats.prank(defaultAVS); - allocationManager.removeStrategiesFromOperatorSet(defaultOperatorSet.id, strategies); + allocationManager.removeStrategiesFromOperatorSet(defaultAVS, defaultOperatorSet.id, strategies); // The orginal strategy should still be in the operator set. assertEq( @@ -2862,7 +2915,7 @@ contract AllocationManagerUnitTests_createOperatorSets is AllocationManagerUnitT function test_createOperatorSets_InvalidOperatorSet() public { cheats.prank(defaultAVS); cheats.expectRevert(InvalidOperatorSet.selector); - allocationManager.createOperatorSets(CreateSetParams(defaultOperatorSet.id, defaultStrategies).toArray()); + allocationManager.createOperatorSets(defaultAVS, CreateSetParams(defaultOperatorSet.id, defaultStrategies).toArray()); } function testFuzz_createOperatorSets_Correctness( @@ -2888,7 +2941,7 @@ contract AllocationManagerUnitTests_createOperatorSets is AllocationManagerUnitT } cheats.prank(avs); - allocationManager.createOperatorSets(createSetParams); + allocationManager.createOperatorSets(avs, createSetParams); for (uint256 k; k < numOpSets; ++k) { OperatorSet memory opSet = OperatorSet(avs, createSetParams[k].operatorSetId); @@ -2916,7 +2969,7 @@ contract AllocationManagerUnitTests_setAVSRegistrar is AllocationManagerUnitTest emit AVSRegistrarSet(avs, avsRegistrar); cheats.prank(avs); - allocationManager.setAVSRegistrar(avsRegistrar); + allocationManager.setAVSRegistrar(avs, avsRegistrar); assertTrue(avsRegistrar == allocationManager.getAVSRegistrar(avs), "should be set"); } -} +} \ No newline at end of file diff --git a/src/test/unit/DelegationUnit.t.sol b/src/test/unit/DelegationUnit.t.sol index ee54ba2ae..f07ffc84a 100644 --- a/src/test/unit/DelegationUnit.t.sol +++ b/src/test/unit/DelegationUnit.t.sol @@ -95,6 +95,7 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag IEigenPodManager(address(eigenPodManagerMock)), IAllocationManager(address(allocationManagerMock)), pauserRegistry, + IPermissionController(address(permissionControllerMock)), MIN_WITHDRAWAL_DELAY_BLOCKS ); @@ -620,7 +621,7 @@ contract DelegationManagerUnitTests_RegisterModifyOperator is DelegationManagerU cheats.expectEmit(true, true, true, true, address(delegationManager)); emit OperatorDetailsModified(defaultOperator, modifiedOperatorDetails); - delegationManager.modifyOperatorDetails(modifiedOperatorDetails); + delegationManager.modifyOperatorDetails(defaultOperator, modifiedOperatorDetails); assertEq( modifiedOperatorDetails.delegationApprover, @@ -639,7 +640,7 @@ contract DelegationManagerUnitTests_RegisterModifyOperator is DelegationManagerU cheats.prank(defaultOperator); cheats.expectRevert(OperatorNotRegistered.selector); - delegationManager.updateOperatorMetadataURI(emptyStringForMetadataURI); + delegationManager.updateOperatorMetadataURI(defaultOperator, emptyStringForMetadataURI); } /** @@ -651,7 +652,7 @@ contract DelegationManagerUnitTests_RegisterModifyOperator is DelegationManagerU OperatorDetails memory operatorDetails ) public { cheats.expectRevert(OperatorNotRegistered.selector); - delegationManager.modifyOperatorDetails(operatorDetails); + delegationManager.modifyOperatorDetails(defaultOperator, operatorDetails); } // @notice Tests that an operator who calls `updateOperatorMetadataURI` will correctly see an `OperatorMetadataURIUpdated` event emitted with their input @@ -663,7 +664,7 @@ contract DelegationManagerUnitTests_RegisterModifyOperator is DelegationManagerU cheats.prank(defaultOperator); cheats.expectEmit(true, true, true, true, address(delegationManager)); emit OperatorMetadataURIUpdated(defaultOperator, metadataURI); - delegationManager.updateOperatorMetadataURI(metadataURI); + delegationManager.updateOperatorMetadataURI(defaultOperator, metadataURI); } } @@ -2515,6 +2516,8 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { function testFuzz_undelegate_revert_invalidCaller( address invalidCaller ) public filterFuzzedAddressInputs(invalidCaller) { + // Invalidate canCall on permissionController + permissionControllerMock.setCanCallResult(false); address staker = address(0x123); // filter out addresses that are actually allowed to call the function cheats.assume(invalidCaller != staker); diff --git a/src/test/unit/PermissionControllerUnit.t.sol b/src/test/unit/PermissionControllerUnit.t.sol new file mode 100644 index 000000000..8ce512b7c --- /dev/null +++ b/src/test/unit/PermissionControllerUnit.t.sol @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "forge-std/Test.sol"; + +import "src/contracts/permissions/PermissionController.sol"; +import "src/contracts/interfaces/IPermissionController.sol"; + +import "src/test/utils/EigenLayerUnitTestSetup.sol"; + +contract PermissionControllerUnitTests is EigenLayerUnitTestSetup, IPermissionControllerEvents, IPermissionControllerErrors { + + PermissionController permissionController; + PermissionController permissionControllerImplementation; + + address account = address(0x1); + address admin = address(0x2); + address admin2 = address(0x3); + address delegate1 = address(0x4); + address delegate2 = address(0x5); + + address target1; + address target2; + bytes4 selector1 = IDelegationManager.updateOperatorMetadataURI.selector; + bytes4 selector2 = IAllocationManager.modifyAllocations.selector; + + function setUp() virtual public override { + // Setup + EigenLayerUnitTestSetup.setUp(); + + // Deploy PermissionController + permissionControllerImplementation = new PermissionController(); + permissionController = PermissionController(address(new TransparentUpgradeableProxy( + address(permissionControllerImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector( + PermissionController.initialize.selector + ) + ))); + + // Set targets + target1 = address(delegationManagerMock); + target2 = address(allocationManagerMock); + } +} + +contract PermissionControllerUnitTests_SetAdmin is PermissionControllerUnitTests { + modifier initializeAdmin() { + cheats.prank(account); + permissionController.setAdmin(account, admin); + _; + } + + function testFuzz_getAdmin_notSet(address account) public view filterFuzzedAddressInputs(account) { + address[] memory admins = permissionController.getAdmins(account); + assertEq(admins[0], account, "Account is not admin"); + assertTrue(permissionController.isAdmin(account, account), "Account is not admin"); + } + + /// @notice Tests the setAdmin function when it has not been initialized + function test_revert_caller_not_account() public { + cheats.expectRevert(IPermissionControllerErrors.NotAdmin.selector); + permissionController.setAdmin(account, admin); + } + + function test_setAdmin_initialSet() public { + // Expect emit + cheats.expectEmit(true, true, true, true); + emit AdminSet(account, admin); + + cheats.prank(account); + permissionController.setAdmin(account, admin); + + // Check storage + address[] memory admins = permissionController.getAdmins(account); + assertEq(admins[0], admin, "Admin not set correctly"); + assertTrue(permissionController.isAdmin(account, admin), "Admin not set correctly"); + assertTrue(permissionController.canCall(account, admin, address(0), bytes4(0)), "Admin cannot call"); + } + + function test_revert_invalidAdmin_alreadySet() public initializeAdmin { + cheats.prank(admin); + cheats.expectRevert(IPermissionControllerErrors.AdminAlreadySet.selector); + permissionController.setAdmin(account, admin); + } + + function test_revert_caller_not_admin() public initializeAdmin { + cheats.prank(account); + cheats.expectRevert(IPermissionControllerErrors.NotAdmin.selector); + permissionController.setAdmin(account, admin2); + } + + function test_addAdditionalAdmin() public initializeAdmin { + // Expect emit + cheats.expectEmit(true, true, true, true); + emit AdminSet(account, admin2); + + cheats.prank(admin); + permissionController.setAdmin(account, admin2); + + // Check storage + address[] memory admins = permissionController.getAdmins(account); + assertEq(admins.length, 2, "Additional admin not added"); + assertTrue(permissionController.isAdmin(account, admin), "Old admin should still be admin"); + assertTrue(permissionController.isAdmin(account, admin2), "New admin not set correctly"); + assertTrue(permissionController.canCall(account, admin2, address(0), bytes4(0)), "Admin cannot call"); + } +} + +contract PermissionControllerUnitTests_RemoveAdmin is PermissionControllerUnitTests { + function setUp() virtual public override { + // Setup + PermissionControllerUnitTests.setUp(); + + // Set admin + cheats.prank(account); + permissionController.setAdmin(account, admin); + } + + function test_revert_notAdmin() public { + cheats.prank(admin2); + cheats.expectRevert(IPermissionControllerErrors.NotAdmin.selector); + permissionController.removeAdmin(account, admin); + } + + function test_revert_adminNotSet() public { + cheats.prank(admin); + cheats.expectRevert(IPermissionControllerErrors.AdminNotSet.selector); + permissionController.removeAdmin(account, admin2); + } + + function test_removeAdmin() public { + // Expect emit + cheats.expectEmit(true, true, true, true); + emit AdminRemoved(account, admin); + + cheats.prank(admin); + permissionController.removeAdmin(account, admin); + + // Check storage + assertFalse(permissionController.isAdmin(account, admin), "Admin not removed"); + assertFalse(permissionController.canCall(account, admin, address(0), bytes4(0)), "Admin can still call"); + + // Assert that the account is now the admin + address[] memory admins = permissionController.getAdmins(account); + assertEq(admins[0], account, "Account is not admin"); + assertTrue(permissionController.isAdmin(account, account), "Account is not admin"); + } +} + +contract PermissionControllerUnitTests_SetDelegate is PermissionControllerUnitTests { + function setUp() virtual public override { + // Setup + PermissionControllerUnitTests.setUp(); + + // Set admin + cheats.prank(account); + permissionController.setAdmin(account, admin); + } + + function test_revert_notAdmin() public { + cheats.expectRevert(IPermissionControllerErrors.NotAdmin.selector); + permissionController.setDelegate(account, delegate1, address(0), bytes4(0)); + } + + function test_setDelegate() public { + // Expect emit + cheats.expectEmit(true, true, true, true); + emit DelegateSet(account, delegate1, target1, selector1); + + cheats.prank(admin); + permissionController.setDelegate(account, delegate1, target1, selector1); + + // Validate Permissions + _validateSetDelegate(account, delegate1, target1, selector1); + } + + function test_revert_delegateAlreadySet() public { + // Set delegate + cheats.startPrank(admin); + permissionController.setDelegate(account, delegate1, target1, selector1); + + cheats.expectRevert(IPermissionControllerErrors.DelegateAlreadySet.selector); + permissionController.setDelegate(account, delegate1, target1, selector1); + cheats.stopPrank(); + } + + function test_setMultipleDelegates() public { + // Set delegates + cheats.startPrank(admin); + permissionController.setDelegate(account, delegate1, target1, selector1); + permissionController.setDelegate(account, delegate1, target2, selector2); + permissionController.setDelegate(account, delegate2, target1, selector1); + cheats.stopPrank(); + + // Validate Permissions + _validateSetDelegate(account, delegate1, target1, selector1); + _validateSetDelegate(account, delegate1, target2, selector2); + _validateSetDelegate(account, delegate2, target1, selector1); + } + + function _validateSetDelegate(address accountToCheck, address delegate, address target, bytes4 selector) internal view { + assertTrue(permissionController.canCall(accountToCheck, delegate, target, selector)); + _validateDelegatePermissions(accountToCheck, delegate, target, selector); + _validateGetDelegates(accountToCheck, delegate, target, selector); + } + + function _validateDelegatePermissions(address accountToCheck, address delegate, address target, bytes4 selector) internal view { + (address[] memory targets, bytes4[] memory selectors) = permissionController.getDelegatePermissions(accountToCheck, delegate); + bool foundTargetSelector = false; + for (uint256 i = 0; i < targets.length; ++i) { + if (targets[i] == target) { + assertEq(selectors[i], selector, "Selector does not match target"); + foundTargetSelector = true; + break; + } + } + assertTrue(foundTargetSelector, "Delegate does not have permission for given target and selector"); + } + + function _validateGetDelegates(address accountToCheck, address delegate, address target, bytes4 selector) internal view { + (address[] memory delegates) = permissionController.getDelegates(accountToCheck, target, selector); + bool foundDelegate = false; + for (uint256 i = 0; i < delegates.length; ++i) { + if (delegates[i] == delegate) { + foundDelegate = true; + break; + } + } + assertTrue(foundDelegate, "Delegate not in list of delegates for given target and selector"); + } +} + +contract PermissionControllerUnitTests_RemoveDelegate is PermissionControllerUnitTests { + function setUp() virtual public override { + // Setup + PermissionControllerUnitTests.setUp(); + + // Set admin + cheats.prank(account); + permissionController.setAdmin(account, admin); + + // Set delegates + cheats.startPrank(admin); + permissionController.setDelegate(account, delegate1, target1, selector1); + permissionController.setDelegate(account, delegate1, target2, selector2); + permissionController.setDelegate(account, delegate2, target1, selector1); + cheats.stopPrank(); + } + + function test_revert_notAdmin() public { + cheats.expectRevert(IPermissionControllerErrors.NotAdmin.selector); + permissionController.removeDelegate(account, delegate1, target1, selector1); + } + + function test_removeDelegate() public { + // Expect emit + cheats.expectEmit(true, true, true, true); + emit DelegateRemoved(account, delegate1, target1, selector1); + + cheats.prank(admin); + permissionController.removeDelegate(account, delegate1, target1, selector1); + + // Validate Permissions + _validateRemoveDelegate(account, delegate1, target1, selector1); + } + + function test_revert_delegateNotSet() public { + cheats.expectRevert(IPermissionControllerErrors.DelegateNotSet.selector); + cheats.prank(admin); + permissionController.removeDelegate(account, delegate2, target2, selector2); + } + + function test_removeMultipleDelegates() public { + // Remove delegates + cheats.startPrank(admin); + permissionController.removeDelegate(account, delegate1, target1, selector1); + permissionController.removeDelegate(account, delegate1, target2, selector2); + permissionController.removeDelegate(account, delegate2, target1, selector1); + cheats.stopPrank(); + + // Validate Permissions + _validateRemoveDelegate(account, delegate1, target1, selector1); + _validateRemoveDelegate(account, delegate1, target2, selector2); + _validateRemoveDelegate(account, delegate2, target1, selector1); + } + + + function _validateRemoveDelegate(address accountToCheck, address delegate, address target, bytes4 selector) internal view { + assertFalse(permissionController.canCall(accountToCheck, delegate, target, selector)); + _validateDelegatePermissionsRemoved(accountToCheck, delegate, target, selector); + _validateGetDelegatesRemoved(accountToCheck, delegate, target, selector); + } + + function _validateDelegatePermissionsRemoved(address accountToCheck, address delegate, address target, bytes4 selector) internal view { + (address[] memory targets, bytes4[] memory selectors) = permissionController.getDelegatePermissions(accountToCheck, delegate); + bool foundTargetSelector = false; + for (uint256 i = 0; i < targets.length; ++i) { + if (targets[i] == target && selectors[i] == selector) { + foundTargetSelector = true; + break; + } + } + assertFalse(foundTargetSelector, "Delegate still has permission for given target and selector"); + } + + function _validateGetDelegatesRemoved(address accountToCheck, address delegate, address target, bytes4 selector) internal view { + (address[] memory delegates) = permissionController.getDelegates(accountToCheck, target, selector); + bool foundDelegate = false; + for (uint256 i = 0; i < delegates.length; ++i) { + if (delegates[i] == delegate) { + foundDelegate = true; + break; + } + } + assertFalse(foundDelegate, "Delegate still in list of delegates for given target and selector"); + } +} \ No newline at end of file diff --git a/src/test/unit/RewardsCoordinatorUnit.t.sol b/src/test/unit/RewardsCoordinatorUnit.t.sol index 0e7b15b73..219ace27c 100644 --- a/src/test/unit/RewardsCoordinatorUnit.t.sol +++ b/src/test/unit/RewardsCoordinatorUnit.t.sol @@ -93,6 +93,7 @@ contract RewardsCoordinatorUnitTests is EigenLayerUnitTestSetup, IRewardsCoordin IDelegationManager(address(delegationManagerMock)), IStrategyManager(address(strategyManagerMock)), pauserRegistry, + IPermissionController(address(permissionControllerMock)), CALCULATION_INTERVAL_SECONDS, MAX_REWARDS_DURATION, MAX_RETROACTIVE_LENGTH, @@ -387,7 +388,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi cheats.expectRevert(IPausable.CurrentlyPaused.selector); IRewardsCoordinatorTypes.RewardsSubmission[] memory rewardsSubmissions; - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(defaultAVS, rewardsSubmissions); } // Revert from reentrancy @@ -415,7 +416,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi reenterer.prepare(targetToUse, msgValueToUse, calldataToUse, bytes("ReentrancyGuard: reentrant call")); cheats.expectRevert(); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(defaultAVS, rewardsSubmissions); } // Revert with 0 length strats and multipliers @@ -456,7 +457,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi // 3. call createAVSRewardsSubmission() with expected revert cheats.prank(avs); cheats.expectRevert(IRewardsCoordinatorErrors.InputArrayLengthZero.selector); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(avs, rewardsSubmissions); } // Revert when amount > 1e38-1 @@ -493,7 +494,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi // 3. Call createAVSRewardsSubmission() with expected revert cheats.prank(avs); cheats.expectRevert(IRewardsCoordinatorErrors.AmountExceedsMax.selector); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(avs, rewardsSubmissions); } function testFuzz_Revert_WhenDuplicateStrategies( @@ -535,7 +536,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi cheats.expectRevert( IRewardsCoordinatorErrors.StrategiesNotInAscendingOrder.selector ); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(avs, rewardsSubmissions); } // Revert with exceeding max duration @@ -574,7 +575,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi // 3. call createAVSRewardsSubmission() with expected revert cheats.prank(avs); cheats.expectRevert(IRewardsCoordinatorErrors.DurationExceedsMax.selector); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(avs, rewardsSubmissions); } // Revert with invalid interval seconds @@ -614,7 +615,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi // 3. call createAVSRewardsSubmission() with expected revert cheats.prank(avs); cheats.expectRevert(IRewardsCoordinatorErrors.InvalidDurationRemainder.selector); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(avs, rewardsSubmissions); } // Revert with retroactive rewards enabled and set too far in past @@ -658,7 +659,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi // 3. call createAVSRewardsSubmission() with expected revert cheats.prank(avs); cheats.expectRevert(IRewardsCoordinatorErrors.StartTimestampTooFarInPast.selector); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(avs, rewardsSubmissions); } // Revert with start timestamp past max future length @@ -696,7 +697,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi // 3. call createAVSRewardsSubmission() with expected revert cheats.prank(avs); cheats.expectRevert(IRewardsCoordinatorErrors.StartTimestampTooFarInFuture.selector); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(avs, rewardsSubmissions); } // Revert with non whitelisted strategy @@ -737,7 +738,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi // 3. call createAVSRewardsSubmission() with expected event emitted cheats.prank(avs); cheats.expectRevert(IRewardsCoordinatorErrors.StrategyNotWhitelisted.selector); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(avs, rewardsSubmissions); } /** @@ -791,7 +792,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi cheats.expectEmit(true, true, true, true, address(rewardsCoordinator)); emit AVSRewardsSubmissionCreated(avs, currSubmissionNonce, rewardsSubmissionHash, rewardsSubmissions[0]); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(avs, rewardsSubmissions); cheats.stopPrank(); assertTrue(rewardsCoordinator.isAVSRewardsSubmissionHash(avs, rewardsSubmissionHash), "rewards submission hash not submitted"); @@ -869,7 +870,7 @@ contract RewardsCoordinatorUnitTests_createAVSRewardsSubmission is RewardsCoordi // 4. call createAVSRewardsSubmission() cheats.prank(param.avs); - rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + rewardsCoordinator.createAVSRewardsSubmission(param.avs, rewardsSubmissions); // 5. Check for submissionNonce() and rewardsSubmissionHashes being set assertEq( diff --git a/src/test/utils/BeaconChainProofsWrapper.sol b/src/test/utils/BeaconChainProofsWrapper.sol new file mode 100644 index 000000000..d8e4aff10 --- /dev/null +++ b/src/test/utils/BeaconChainProofsWrapper.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "src/contracts/libraries/BeaconChainProofs.sol"; + +/// @notice This contract is used to test offchain proof generation +contract BeaconChainProofsWrapper { + + function verifyStateRoot( + bytes32 beaconBlockRoot, + BeaconChainProofs.StateRootProof calldata proof + ) external view { + BeaconChainProofs.verifyStateRoot(beaconBlockRoot, proof); + } + + function verifyValidatorFields( + bytes32 beaconStateRoot, + bytes32[] calldata validatorFields, + bytes calldata validatorFieldsProof, + uint40 validatorIndex + ) external view { + BeaconChainProofs.verifyValidatorFields(beaconStateRoot, validatorFields, validatorFieldsProof, validatorIndex); + } + + function verifyBalanceContainer( + bytes32 beaconBlockRoot, + BeaconChainProofs.BalanceContainerProof calldata proof + ) external view { + BeaconChainProofs.verifyBalanceContainer(beaconBlockRoot, proof); + } + + function verifyValidatorBalance( + bytes32 balanceContainerRoot, + uint40 validatorIndex, + BeaconChainProofs.BalanceProof calldata proof + ) external view { + BeaconChainProofs.verifyValidatorBalance(balanceContainerRoot, validatorIndex, proof); + } + + +} \ No newline at end of file diff --git a/src/test/utils/EigenLayerUnitTestSetup.sol b/src/test/utils/EigenLayerUnitTestSetup.sol index 82183e8c0..1b8d64e0e 100644 --- a/src/test/utils/EigenLayerUnitTestSetup.sol +++ b/src/test/utils/EigenLayerUnitTestSetup.sol @@ -8,6 +8,7 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; import "src/contracts/permissions/PauserRegistry.sol"; +import "src/contracts/permissions/PermissionController.sol"; import "src/contracts/strategies/StrategyBase.sol"; import "src/test/mocks/AVSDirectoryMock.sol"; @@ -19,6 +20,9 @@ import "src/test/mocks/EmptyContract.sol"; import "src/test/utils/SingleItemArrayLib.sol"; import "src/test/utils/Random.sol"; +import "src/test/mocks/PermissionControllerMock.sol"; + +import "src/test/utils/SingleItemArrayLib.sol"; abstract contract EigenLayerUnitTestSetup is Test { using SingleItemArrayLib for *; @@ -33,6 +37,7 @@ abstract contract EigenLayerUnitTestSetup is Test { PauserRegistry pauserRegistry; ProxyAdmin eigenLayerProxyAdmin; + PermissionControllerMock permissionControllerMock; AVSDirectoryMock avsDirectoryMock; AllocationManagerMock allocationManagerMock; StrategyManagerMock strategyManagerMock; @@ -64,13 +69,15 @@ abstract contract EigenLayerUnitTestSetup is Test { pauserRegistry = new PauserRegistry(pausers, unpauser); eigenLayerProxyAdmin = new ProxyAdmin(); - + + avsDirectoryMock = AVSDirectoryMock(payable(address(new AVSDirectoryMock()))); allocationManagerMock = AllocationManagerMock(payable(address(new AllocationManagerMock()))); strategyManagerMock = StrategyManagerMock(payable(address(new StrategyManagerMock(IDelegationManager(address(delegationManagerMock)))))); delegationManagerMock = DelegationManagerMock(payable(address(new DelegationManagerMock()))); eigenPodManagerMock = EigenPodManagerMock(payable(address(new EigenPodManagerMock(pauserRegistry)))); emptyContract = new EmptyContract(); + permissionControllerMock = PermissionControllerMock(payable(address(new PermissionControllerMock()))); isExcludedFuzzAddress[address(0)] = true; isExcludedFuzzAddress[address(pauserRegistry)] = true; @@ -80,5 +87,6 @@ abstract contract EigenLayerUnitTestSetup is Test { isExcludedFuzzAddress[address(strategyManagerMock)] = true; isExcludedFuzzAddress[address(delegationManagerMock)] = true; isExcludedFuzzAddress[address(eigenPodManagerMock)] = true; + isExcludedFuzzAddress[address(permissionControllerMock)] = true; } } \ No newline at end of file diff --git a/src/test/utils/Operators.sol b/src/test/utils/Operators.sol new file mode 100644 index 000000000..1bc8acd50 --- /dev/null +++ b/src/test/utils/Operators.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "forge-std/Test.sol"; +import "forge-std/Script.sol"; +import "forge-std/StdJson.sol"; + +contract Operators is Test { + string internal operatorConfigJson; + + constructor() { + operatorConfigJson = vm.readFile("./src/test/test-data/operators.json"); + } + + function operatorPrefix(uint256 index) public pure returns(string memory) { + return string.concat(".operators[", string.concat(vm.toString(index), "].")); + } + + function getNumOperators() public view returns(uint256) { + return stdJson.readUint(operatorConfigJson, ".numOperators"); + } + + function getOperatorAddress(uint256 index) public view returns (address) { + return stdJson.readAddress(operatorConfigJson, string.concat(operatorPrefix(index), "Address")); + } + + function getOperatorSecretKey(uint256 index) public view returns (uint256) { + return readUint(operatorConfigJson, index, "SecretKey"); + } + + function readUint(string memory json, uint256 index, string memory key) public pure returns (uint256) { + return stringToUint(stdJson.readString(json, string.concat(operatorPrefix(index), key))); + } + + function stringToUint(string memory s) public pure returns (uint256) { + bytes memory b = bytes(s); + uint256 result = 0; + for (uint256 i = 0; i < b.length; i++) { + if (uint256(uint8(b[i])) >= 48 && uint256(uint8(b[i])) <= 57) { + result = result * 10 + (uint256(uint8(b[i])) - 48); + } + } + return result; + } + function setOperatorJsonFilePath(string memory filepath) public { + operatorConfigJson = vm.readFile(filepath); + } +} diff --git a/src/test/utils/Owners.sol b/src/test/utils/Owners.sol new file mode 100644 index 000000000..6b55c70bf --- /dev/null +++ b/src/test/utils/Owners.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "forge-std/Test.sol"; +import "forge-std/Script.sol"; +import "forge-std/StdJson.sol"; + +contract Owners is Test { + string internal ownersConfigJson; + address[] addresses; + + constructor() { + ownersConfigJson = vm.readFile("./src/test/test-data/owners.json"); + } + + function ownerPrefix(uint256 index) public pure returns(string memory) { + return string.concat(".owners[", string.concat(vm.toString(index), "].")); + } + + function getNumOperators() public view returns(uint256) { + return stdJson.readUint(ownersConfigJson, ".numOwners"); + } + + function getOwnerAddress(uint256 index) public view returns(address) { + return stdJson.readAddress(ownersConfigJson, string.concat(ownerPrefix(index), "Address")); + } + + function getOwnerAddresses() public returns(address[] memory) { + for (uint256 i = 0; i < getNumOperators(); i++) { + addresses.push(getOwnerAddress(i)); + } + return addresses; + } + + function getReputedOwnerAddresses() public returns(address[] memory) { + resetOwnersConfigJson("reputedOwners.json"); + for (uint256 i = 0; i < getNumOperators(); i++) { + addresses.push(getOwnerAddress(i)); + } + return addresses; + } + + function resetOwnersConfigJson(string memory newConfig) public { + ownersConfigJson = vm.readFile(newConfig); + } + +} diff --git a/src/test/utils/SignatureCompaction.sol b/src/test/utils/SignatureCompaction.sol new file mode 100644 index 000000000..0bb98a1c0 --- /dev/null +++ b/src/test/utils/SignatureCompaction.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + +//small library for dealing with efficiently-packed signatures, where parameters v,r,s are packed into vs and r (64 bytes instead of 65) +library SignatureCompaction { + bytes32 internal constant HALF_CURVE_ORDER = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0; + + function ecrecoverPacked(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { + (address recovered, ECDSA.RecoverError err) = ECDSA.tryRecover(hash, r, vs); + require(err == ECDSA.RecoverError.NoError, "error in ecrecoverPacked"); + return recovered; + } + + function packSignature(bytes32 r, bytes32 s, uint8 v) internal pure returns (bytes32, bytes32) { + require(s <= HALF_CURVE_ORDER, "malleable signature, s too high"); + //v parity is a single bit, encoded as either v = 27 or v = 28 -- in order to recover the bit we subtract 27 + bytes32 vs = bytes32(uint256(bytes32(uint256(v) - 27) << 255) | uint256(s)); + return (r, vs); + } + + //same as above, except doesn't take 'r' as argument since it is unneeded + function packVS(bytes32 s, uint8 v) internal pure returns (bytes32) { + require(s <= HALF_CURVE_ORDER, "malleable signature, s too high"); + //v parity is a single bit, encoded as either v = 27 or v = 28 -- in order to recover the bit we subtract 27 + return bytes32(uint256(bytes32(uint256(v) - 27) << 255) | uint256(s)); + } +} diff --git a/src/test/utils/Utils.sol b/src/test/utils/Utils.sol new file mode 100644 index 000000000..505328983 --- /dev/null +++ b/src/test/utils/Utils.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "src/contracts/strategies/StrategyBase.sol"; + +contract Utils { + address constant dummyAdmin = address(uint160(uint256(keccak256("DummyAdmin")))); + + function deployNewStrategy(IERC20 token, IStrategyManager strategyManager, IPauserRegistry pauserRegistry, address admin) public returns (StrategyBase) { + StrategyBase newStrategy = new StrategyBase(strategyManager, pauserRegistry); + newStrategy = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(newStrategy), + address(admin), + "" + ) + ) + ); + newStrategy.initialize(token); + return newStrategy; + } +}