diff --git a/.github/workflows/foundry.yml b/.github/workflows/foundry.yml index 52f868097..2ec5d43b6 100644 --- a/.github/workflows/foundry.yml +++ b/.github/workflows/foundry.yml @@ -13,6 +13,7 @@ env: FOUNDRY_PROFILE: ci RPC_MAINNET: ${{ secrets.RPC_MAINNET }} RPC_HOLESKY: ${{ secrets.RPC_HOLESKY }} + HOLESKY_RPC_URL: ${{ secrets.HOLESKY_RPC_URL }} CHAIN_ID: ${{ secrets.CHAIN_ID }} jobs: diff --git a/script/config/17000-preprod.json b/script/config/17000-preprod.json new file mode 100644 index 000000000..b39a3a40a --- /dev/null +++ b/script/config/17000-preprod.json @@ -0,0 +1 @@ +{"ZEUS_ENV":"preprod","ZEUS_ENV_COMMIT":"26dcafff7bd304b84f81ba36fe70ef44d414a752","ZEUS_TEST":"false","ZEUS_ENV_VERSION":"1.0.0","ZEUS_VERSION":"0.3.0","ZEUS_ENV_communityMultisig":"0x42789cCdE6486b81c7D41Fa3759A8BB74D7909DB","ZEUS_ENV_executorMultisig":"0x84E5e0D6f6c3153057bd5d661Be1ff1766cEac08","ZEUS_ENV_protocolCouncilMultisig":"0x314A6B61B5CBFA79dc7b2c0416e039822E886b54","ZEUS_ENV_operationsMultisig":"0x6d609cD2812bDA02a75dcABa7DaafE4B20Ff5608","ZEUS_ENV_pauserMultisig":"0x546D3b66B27dCb94777d7eFC3825b10675FE5D95","ZEUS_ENV_pauserRegistry":"0x9Ab2FEAf0465f0eD51Fc2b663eF228B418c9Dad1","ZEUS_ENV_proxyAdmin":"0x1BEF05C7303d44e0E2FCD2A19d993eDEd4c51b5B","ZEUS_ENV_timelockController":"0x7c66A1e862E11C4887270aBd649157ACe837A2D0","ZEUS_ENV_timelockController_BEIGEN":"0x68fa0fB9eDe34745C41d85E2766ee32eBc6cEF0e","ZEUS_ENV_beigenExecutorMultisig":"0xFbb2c01A19A85c4C648186a97d879EEA25cE1397","ZEUS_ENV_foundationMultisig":"0x9c36f012585c8bb4247eEe18C01Cdfc5f2e90336","ZEUS_ENV_ethPOS":"0x4242424242424242424242424242424242424242","ZEUS_ENV_MultiSendCallOnly":"0x40A2aCCbd92BCA938b02010E17A5b8929b49130D","ZEUS_ENV_MIN_WITHDRAWAL_DELAY":50,"ZEUS_ENV_ALLOCATION_CONFIGURATION_DELAY":75,"ZEUS_ENV_EIGENPOD_GENESIS_TIME":1695902400,"ZEUS_ENV_REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS":86400,"ZEUS_ENV_REWARDS_COORDINATOR_MAX_REWARDS_DURATION":6048000,"ZEUS_ENV_REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH":7776000,"ZEUS_ENV_REWARDS_COORDINATOR_MAX_FUTURE_LENGTH":2592000,"ZEUS_ENV_REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP":1710979200,"ZEUS_ENV_REWARDS_COORDINATOR_INIT_PAUSED_STATUS":0,"ZEUS_ENV_REWARDS_COORDINATOR_UPDATER":"0x18a0f92Ad9645385E8A8f3db7d0f6CF7aBBb0aD4","ZEUS_ENV_REWARDS_COORDINATOR_ACTIVATION_DELAY":120,"ZEUS_ENV_REWARDS_COORDINATOR_DEFAULT_OPERATOR_SPLIT_BIPS":1000,"ZEUS_ENV_REWARDS_COORDINATOR_PAUSE_STATUS":2,"ZEUS_DEPLOYED_PauserRegistry_Impl":"0x50712285cE831a6B9a11214A430f28999A5b4DAe","ZEUS_DEPLOYED_AVSDirectory_Proxy":"0x141d6995556135D4997b2ff72EB443Be300353bC","ZEUS_DEPLOYED_AVSDirectory_Impl":"0xD5597C5c574BbD4Ce76C3aaF2900525De97aD711","ZEUS_DEPLOYED_EigenPod_Beacon":"0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC","ZEUS_DEPLOYED_EigenPod_Impl":"0xf53F78382a26b62A3aC3a3837a8dED2044a2103D","ZEUS_DEPLOYED_EigenPodManager_Proxy":"0xB8d8952f572e67B11e43bC21250967772fa883Ff","ZEUS_DEPLOYED_EigenPodManager_Impl":"0xe55A9EA92b1134afF44fB81e667FD5f74e6a47e5","ZEUS_DEPLOYED_DelegationManager_Proxy":"0x75dfE5B44C2E530568001400D3f704bC8AE350CC","ZEUS_DEPLOYED_DelegationManager_Impl":"0xa61d360551d05715046491eEc8a8Cb90f6867545","ZEUS_DEPLOYED_RewardsCoordinator_Proxy":"0xb22Ef643e1E067c994019A4C19e403253C05c2B0","ZEUS_DEPLOYED_RewardsCoordinator_Impl":"0x480e9045F62F20b8B8342164011305f223D48B2b","ZEUS_DEPLOYED_EigenStrategy_Proxy":"0x4e0125f8a928Eb1b9dB4BeDd3756BA3c200563C2","ZEUS_DEPLOYED_EigenStrategy_Impl":"0xdB01e5178E4745346B230fdF2d6Da2F483a71EA4","ZEUS_DEPLOYED_Eigen_Proxy":"0xD58f6844f79eB1fbd9f7091d05f7cb30d3363926","ZEUS_DEPLOYED_Eigen_Impl":"0x95a7431400F362F3647a69535C5666cA0133CAA0","ZEUS_DEPLOYED_BackingEigen_Proxy":"0xA72942289a043874249E60469F68f08B8c6ECCe8","ZEUS_DEPLOYED_BackingEigen_Impl":"0xd5FdabDac3d8ACeAB7BFfDDFA18877A4c5D5Aa82","ZEUS_DEPLOYED_StrategyBase_Beacon":"0xf2c2AcA859C685895E60ca7A14274365b64c0c2a","ZEUS_DEPLOYED_StrategyBase_Impl":"0x3Dc582A90b920AA6aa0204e0517d6767C9C8c268","ZEUS_DEPLOYED_StrategyManager_Proxy":"0xF9fbF2e35D8803273E214c99BF15174139f4E67a","ZEUS_DEPLOYED_StrategyManager_Impl":"0x158E6FBFb5FAc98894BF0Da28998efcB3Ba1C58A","ZEUS_DEPLOYED_StrategyFactory_Proxy":"0xad4a89e3ca9b3dc25aabe0aa7d72e61d2ec66052","ZEUS_DEPLOYED_StrategyFactory_Impl":"0x203654Ea1e39d00983A9cE0457B2d2efC188fC40","ZEUS_DEPLOYED_StrategyBaseTVLLimits_Impl":"0x98bD5748dc964e85400B3A5D0152c444201C577a","ZEUS_DEPLOYED_PermissionController_Impl":"0x259597c0AEc3c9978D24e892225f2F4Ac142d885","ZEUS_DEPLOYED_PermissionController_Proxy":"0xa2348c77802238Db39f0CefAa500B62D3FDD682b","ZEUS_DEPLOYED_AllocationManager_Impl":"0xc7618DC8607503C15f4393782068B79655104A24","ZEUS_DEPLOYED_AllocationManager_Proxy":"0xFdD5749e11977D60850E06bF5B13221Ad95eb6B4","ZEUS_DEPLOYED_StrategyBaseTVLLimits_0":"0x6e5d5060b33ca2090a78e9cb74fe207453b30e49","ZEUS_DEPLOYED_StrategyBaseTVLLimits_1":"0xf6a09ae03d7760aecf1626ce7df0f113bec2d9bd","ZEUS_DEPLOYED_StrategyBaseTVLLimits_2":"0x7fa77c321bf66e42eabc9b10129304f7f90c5585","ZEUS_DEPLOYED_StrategyBaseTVLLimits_3":"0x24da526f9e465c4fb6bae41e226df8aa5b34eac7","ZEUS_DEPLOYED_StrategyBaseTVLLimits_4":"0x6dc6ce589f852f96ac86cb160ab0b15b9f56dedd","ZEUS_DEPLOYED_StrategyBaseTVLLimits_5":"0x3c28437e610fb099cc3d6de4d9c707dfacd308ae","ZEUS_DEPLOYED_StrategyBaseTVLLimits_6":"0x7b6257f5caf7311b36f7416133a8499c68a83c3a","ZEUS_DEPLOYED_StrategyBaseTVLLimits_7":"0xc86382179500e8ed3e686fc4a99ed9ec72df3f56","ZEUS_DEPLOYED_StrategyBaseTVLLimits_8":"0x3cb1fd19cfb178c1098f2fc1e11090a0642b2314","ZEUS_DEPLOYED_StrategyBaseTVLLimits_9":"0x87f6c7d24b109919eb38295e3f8298425e6331d9","ZEUS_DEPLOYED_StrategyBaseTVLLimits_10":"0x5c8b55722f421556a2aafb7a3ea63d4c3e514312","ZEUS_DEPLOYED_StrategyBaseTVLLimits_11":"0xd523267698c81a372191136e477fdebfa33d9fb4"} \ No newline at end of file diff --git a/test/End2End.t.sol b/test/End2End.t.sol new file mode 100644 index 000000000..523434e18 --- /dev/null +++ b/test/End2End.t.sol @@ -0,0 +1,537 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import {Vm} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {Test, console2 as console} from "forge-std/Test.sol"; +import {OperatorLib} from "./utils/OperatorLib.sol"; +import {UpgradeableProxyLib} from "./unit/UpgradeableProxyLib.sol"; +import {MiddlewareDeployLib} from "./utils/MiddlewareDeployLib.sol"; +import {BN254} from "../src/libraries/BN254.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAllocationManagerTypes} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IServiceManager} from "../src/interfaces/IServiceManager.sol"; +import {IStakeRegistry, IStakeRegistryTypes} from "../src/interfaces/IStakeRegistry.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {RegistryCoordinator} from "../src/RegistryCoordinator.sol"; +import {IRegistryCoordinator} from "../src/interfaces/IRegistryCoordinator.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; +import {PermissionController} from + "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; +import {ServiceManagerMock} from "./mocks/ServiceManagerMock.sol"; +import { + ISlashingRegistryCoordinator, + ISlashingRegistryCoordinatorTypes +} from "../src/interfaces/ISlashingRegistryCoordinator.sol"; +import {ERC20Mock} from "./mocks/ERC20Mock.sol"; +import {IStrategyFactory} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyFactory.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract End2EndForkTest is Test { + using stdJson for string; + using OperatorLib for *; + + struct DeploymentData { + address delegationManager; + address avsDirectory; + address allocationManager; + address strategyManager; + address eigenPodManager; + address rewardsCoordinator; + address eigenPodBeacon; + address pauserRegistry; + address strategyFactory; + address strategyBeacon; + address eigenStrategy; + address eigen; + address backingEigen; + address permissionController; + } + + struct ConfigData { + address admin; + address token; + address strategy; + uint256 numQuorums; + uint256[] operatorParams; + address[][] operators; + address proxyAdmin; + } + + function testCreateOperator() public { + OperatorLib.Operator memory operator = OperatorLib.createOperator("operator-1"); + + assertTrue(operator.key.addr != address(0), "VM wallet address should be non-zero"); + + assertTrue(operator.signingKey.privateKey != 0, "BLS private key should be non-zero"); + + assertTrue( + operator.signingKey.publicKeyG1.X != 0 || operator.signingKey.publicKeyG1.X != 0, + "BLS public key G1 X should be non-zero" + ); + assertTrue( + operator.signingKey.publicKeyG1.Y != 0 || operator.signingKey.publicKeyG1.Y != 0, + "BLS public key G1 Y should be non-zero" + ); + + assertTrue( + operator.signingKey.publicKeyG2.X[0] != 0 || operator.signingKey.publicKeyG2.X[1] != 0, + "BLS public key G2 X should be non-zero" + ); + assertTrue( + operator.signingKey.publicKeyG2.Y[0] != 0 || operator.signingKey.publicKeyG2.Y[1] != 0, + "BLS public key G2 Y should be non-zero" + ); + } + + function testSignAndVerifyMessage() public { + OperatorLib.Operator memory operator = OperatorLib.createOperator("operator-1"); + + bytes32 messageHash = keccak256(abi.encodePacked("Test message")); + BN254.G1Point memory signature = OperatorLib.signMessageWithOperator(operator, messageHash); + BN254.G1Point memory messagePoint = BN254.hashToG1(messageHash); + + bool isValid = BN254.pairing( + BN254.negate(signature), + BN254.generatorG2(), + messagePoint, + operator.signingKey.publicKeyG2 + ); + assertTrue(isValid, "Signature should be valid"); + } + + function testEndToEndSetup_M2Migration() public { + ( + OperatorLib.Operator[] memory operators, + DeploymentData memory coreDeployment, + MiddlewareDeployLib.MiddlewareDeployData memory middlewareDeployment, + ConfigData memory middlewareConfig + ) = _setupInitialState(); + + _setupOperatorsAndTokens(operators, coreDeployment, middlewareConfig); + + _setupFirstQuorumAndOperatorSet( + operators, middlewareConfig, coreDeployment, middlewareDeployment + ); + + _setupSecondQuorumAndOperatorSet( + operators, middlewareConfig, coreDeployment, middlewareDeployment + ); + + _executeSlashing(operators, middlewareConfig, middlewareDeployment); + } + + function _deployMiddlewareWithCore( + address proxyAdmin, + address owner + ) + internal + returns ( + MiddlewareDeployLib.MiddlewareDeployData memory middleware, + DeploymentData memory core + ) + { + string memory rpcUrl = vm.envString("HOLESKY_RPC_URL"); + vm.createSelectFork(rpcUrl); + + // Read core deployment data from json + core = _readCoreDeploymentJson("./script/config", 17000, "preprod"); + + // Deploy proxies + middleware = MiddlewareDeployLib.deployEmptyProxies(proxyAdmin); + + // Deploy pauser registry + middleware.pauserRegistry = MiddlewareDeployLib.deployPauserRegistry(proxyAdmin); + + // Upgrade the proxies + MiddlewareDeployLib.upgradeRegistriesM2Coordinator( + core.delegationManager, core.avsDirectory, core.allocationManager, middleware + ); + MiddlewareDeployLib.ugpradeServiceManager( + core.avsDirectory, core.rewardsCoordinator, core.allocationManager, middleware, owner + ); + MiddlewareDeployLib.upgradeM2Coordinator(core.allocationManager, middleware, owner); + + return (middleware, core); + } + + function _setupInitialState() + internal + returns ( + OperatorLib.Operator[] memory operators, + DeploymentData memory coreDeployment, + MiddlewareDeployLib.MiddlewareDeployData memory middlewareDeployment, + ConfigData memory middlewareConfig + ) + { + middlewareConfig.proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); + middlewareConfig.admin = address(this); + + // Deploy middleware with core + (middlewareDeployment, coreDeployment) = + _deployMiddlewareWithCore(middlewareConfig.proxyAdmin, middlewareConfig.admin); + + // Create 5 operators using helper function + operators = _createOperators(5, 100); + + // Deploy token and strategy + (address token, address strategy) = _deployTokenAndStrategy(coreDeployment.strategyFactory); + + // Setup middleware deployment data + middlewareConfig.numQuorums = 1; + middlewareConfig.operatorParams = new uint256[](3); + middlewareConfig.operatorParams[0] = 10; + middlewareConfig.operatorParams[1] = 100; + middlewareConfig.operatorParams[2] = 100; + middlewareConfig.strategy = strategy; + middlewareConfig.token = token; + middlewareConfig.operators = _getAndSortOperators(operators); + + // Set the metadata URI, AVS Registrar and UAM apointee for operator set creation + vm.startPrank(middlewareDeployment.serviceManager); + AllocationManager(coreDeployment.allocationManager).updateAVSMetadataURI( + middlewareDeployment.serviceManager, "metadata" + ); + AllocationManager(coreDeployment.allocationManager).setAVSRegistrar( + middlewareDeployment.serviceManager, + IAVSRegistrar(middlewareDeployment.registryCoordinator) + ); + PermissionController(coreDeployment.permissionController).setAppointee( + address(middlewareDeployment.serviceManager), + address(middlewareDeployment.registryCoordinator), + coreDeployment.allocationManager, + AllocationManager.createOperatorSets.selector + ); + vm.stopPrank(); + } + + function _deployTokenAndStrategy( + address strategyFactory + ) private returns (address token, address strategy) { + ERC20Mock tokenContract = new ERC20Mock(); + token = address(tokenContract); + strategy = address(IStrategyFactory(strategyFactory).deployNewStrategy(IERC20(token))); + } + + function _createOperators( + uint256 numOperators, + uint256 startIndex + ) internal returns (OperatorLib.Operator[] memory) { + OperatorLib.Operator[] memory operators = new OperatorLib.Operator[](numOperators); + for (uint256 i = 0; i < numOperators; i++) { + operators[i] = + OperatorLib.createOperator(string(abi.encodePacked("operator-", i + startIndex))); + } + return operators; + } + + function _registerOperatorsAsEigenLayerOperators( + OperatorLib.Operator[] memory operators, + address delegationManager + ) internal { + for (uint256 i = 0; i < operators.length; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerAsOperator(operators[i], delegationManager); + vm.stopPrank(); + } + } + + function _setupOperatorsAndTokens( + OperatorLib.Operator[] memory operators, + DeploymentData memory coreDeployment, + ConfigData memory middlewareConfig + ) internal { + // Verify and register operators + for (uint256 i = 0; i < 5; i++) { + bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator( + operators[i].key.addr + ); + assertFalse(isRegistered, "Operator should not be registered"); + } + + _registerOperatorsAsEigenLayerOperators(operators, coreDeployment.delegationManager); + + for (uint256 i = 0; i < 5; i++) { + bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator( + operators[i].key.addr + ); + assertTrue(isRegistered, "Operator should be registered"); + } + + // Setup tokens and verify balances + uint256 mintAmount = 1000 * 1e18; + for (uint256 i = 0; i < 5; i++) { + OperatorLib.mintMockTokens(operators[i], middlewareConfig.token, mintAmount); + uint256 balance = IERC20(middlewareConfig.token).balanceOf(operators[i].key.addr); + assertEq(balance, mintAmount, "Operator should have correct token balance"); + } + + // Handle deposits + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + uint256 shares = OperatorLib.depositTokenIntoStrategy( + operators[i], + coreDeployment.strategyManager, + middlewareConfig.strategy, + middlewareConfig.token, + mintAmount + ); + assertTrue(shares > 0, "Should have received shares for deposit"); + vm.stopPrank(); + + shares = IStrategy(middlewareConfig.strategy).shares(operators[i].key.addr); + assertEq(shares, mintAmount, "Operator shares should equal deposit amount"); + } + } + + function _setupFirstQuorumAndOperatorSet( + OperatorLib.Operator[] memory operators, + ConfigData memory middlewareConfig, + DeploymentData memory coreDeployment, + MiddlewareDeployLib.MiddlewareDeployData memory middlewareDeployment + ) internal { + vm.startPrank(middlewareConfig.admin); + + // Create first quorum + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 100, + kickBIPsOfTotalStake: 100 + }); + + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams({ + strategy: IStrategy(middlewareConfig.strategy), + multiplier: 1 ether + }); + + RegistryCoordinator(middlewareDeployment.registryCoordinator) + .createTotalDelegatedStakeQuorum(operatorSetParams, 100, strategyParams); + vm.stopPrank(); + + // Register operators + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerOperatorFromAVS_OpSet( + operators[i], + coreDeployment.allocationManager, + middlewareDeployment.registryCoordinator, + middlewareDeployment.serviceManager, + operatorSetIds + ); + vm.stopPrank(); + } + + vm.roll(block.number + 10); + + // Update operators for quorum + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(0)); + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + middlewareConfig.operators, quorumNumbers + ); + } + + function _setupSecondQuorumAndOperatorSet( + OperatorLib.Operator[] memory operators, + ConfigData memory middlewareConfig, + DeploymentData memory coreDeployment, + MiddlewareDeployLib.MiddlewareDeployData memory middlewareDeployment + ) internal { + // Create second quorum + vm.startPrank(middlewareConfig.admin); + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams({ + strategy: IStrategy(middlewareConfig.strategy), + multiplier: 1 ether + }); + + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + + RegistryCoordinator(middlewareDeployment.registryCoordinator).createSlashableStakeQuorum( + operatorSetParams, 100, strategyParams, 10 + ); + vm.stopPrank(); + + _setupOperatorAllocations( + operators, coreDeployment, middlewareDeployment, middlewareConfig.strategy + ); + + // Register and update operators for second quorum + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 1; + + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerOperatorFromAVS_OpSet( + operators[i], + coreDeployment.allocationManager, + middlewareDeployment.registryCoordinator, + middlewareDeployment.serviceManager, + operatorSetIds + ); + vm.stopPrank(); + } + + vm.roll(block.number + 10); + + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(1)); + + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + middlewareConfig.operators, quorumNumbers + ); + } + + function _setupOperatorAllocations( + OperatorLib.Operator[] memory operators, + DeploymentData memory coreDeployment, + MiddlewareDeployLib.MiddlewareDeployData memory middlewareDeployment, + address strategy + ) internal { + uint32 minDelay = 1; + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.setAllocationDelay( + operators[i], address(coreDeployment.allocationManager), minDelay + ); + vm.stopPrank(); + } + + vm.roll(block.number + 100); + + IStrategy[] memory allocStrategies = new IStrategy[](1); + allocStrategies[0] = IStrategy(strategy); + + uint64[] memory magnitudes = new uint64[](1); + magnitudes[0] = uint64(1 ether); + + OperatorSet memory operatorSet = + OperatorSet({avs: address(middlewareDeployment.serviceManager), id: 1}); + + IAllocationManagerTypes.AllocateParams[] memory allocParams = + new IAllocationManagerTypes.AllocateParams[](1); + allocParams[0] = IAllocationManagerTypes.AllocateParams({ + operatorSet: operatorSet, + strategies: allocStrategies, + newMagnitudes: magnitudes + }); + + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.modifyOperatorAllocations( + operators[i], address(coreDeployment.allocationManager), allocParams + ); + vm.stopPrank(); + } + + vm.roll(block.number + 100); + } + + function _executeSlashing( + OperatorLib.Operator[] memory operators, + ConfigData memory middlewareConfig, + MiddlewareDeployLib.MiddlewareDeployData memory middlewareDeployment + ) internal { + IAllocationManagerTypes.SlashingParams memory slashingParams = IAllocationManagerTypes + .SlashingParams({ + operator: operators[0].key.addr, + operatorSetId: 1, + strategies: new IStrategy[](1), + wadsToSlash: new uint256[](1), + description: "Test slashing" + }); + + slashingParams.strategies[0] = IStrategy(middlewareConfig.strategy); + slashingParams.wadsToSlash[0] = 0.5e18; + + ServiceManagerMock(middlewareDeployment.serviceManager).slashOperator(slashingParams); + } + + function _getAndSortOperators( + OperatorLib.Operator[] memory operators + ) internal pure returns (address[][] memory) { + address[][] memory registeredOperators = new address[][](1); + registeredOperators[0] = new address[](5); + for (uint256 i = 0; i < 5; i++) { + registeredOperators[0][i] = operators[i].key.addr; + } + + // Sort operator addresses + for (uint256 i = 0; i < registeredOperators[0].length - 1; i++) { + for (uint256 j = 0; j < registeredOperators[0].length - i - 1; j++) { + if (registeredOperators[0][j] > registeredOperators[0][j + 1]) { + address temp = registeredOperators[0][j]; + registeredOperators[0][j] = registeredOperators[0][j + 1]; + registeredOperators[0][j + 1] = temp; + } + } + } + + return registeredOperators; + } + + function _readCoreDeploymentJson( + string memory path, + uint256 chainId + ) internal returns (DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), ".json")); + return _parseZeusJson(filePath); + } + + function _readCoreDeploymentJson( + string memory path, + uint256 chainId, + string memory environment + ) internal returns (DeploymentData memory) { + string memory filePath = + string(abi.encodePacked(path, "/", vm.toString(chainId), "-", environment, ".json")); + return _parseZeusJson(filePath); + } + + function _parseZeusJson( + string memory filePath + ) internal returns (DeploymentData memory) { + string memory json = vm.readFile(filePath); + require(vm.exists(filePath), "Deployment file does not exist"); + DeploymentData memory deploymentData; + + deploymentData.delegationManager = + json.readAddress(".ZEUS_DEPLOYED_DelegationManager_Proxy"); + deploymentData.avsDirectory = json.readAddress(".ZEUS_DEPLOYED_AVSDirectory_Proxy"); + deploymentData.strategyManager = json.readAddress(".ZEUS_DEPLOYED_StrategyManager_Proxy"); + deploymentData.allocationManager = + json.readAddress(".ZEUS_DEPLOYED_AllocationManager_Proxy"); + deploymentData.eigenPodManager = json.readAddress(".ZEUS_DEPLOYED_EigenPodManager_Proxy"); + deploymentData.rewardsCoordinator = + json.readAddress(".ZEUS_DEPLOYED_RewardsCoordinator_Proxy"); + deploymentData.eigenPodBeacon = json.readAddress(".ZEUS_DEPLOYED_EigenPod_Beacon"); + deploymentData.pauserRegistry = json.readAddress(".ZEUS_DEPLOYED_PauserRegistry_Impl"); + deploymentData.strategyFactory = json.readAddress(".ZEUS_DEPLOYED_StrategyFactory_Proxy"); + deploymentData.strategyBeacon = json.readAddress(".ZEUS_DEPLOYED_StrategyBase_Beacon"); + deploymentData.eigenStrategy = json.readAddress(".ZEUS_DEPLOYED_EigenStrategy_Proxy"); + deploymentData.eigen = json.readAddress(".ZEUS_DEPLOYED_Eigen_Proxy"); + deploymentData.backingEigen = json.readAddress(".ZEUS_DEPLOYED_BackingEigen_Proxy"); + deploymentData.permissionController = + json.readAddress(".ZEUS_DEPLOYED_PermissionController_Proxy"); + + return deploymentData; + } +} diff --git a/test/mocks/ERC20Mock.sol b/test/mocks/ERC20Mock.sol new file mode 100644 index 000000000..f4c95bcc8 --- /dev/null +++ b/test/mocks/ERC20Mock.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract ERC20Mock is ERC20 { + constructor() ERC20("", "") {} + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } +} diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index b33df5030..61e93d9b8 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -28,4 +28,10 @@ contract ServiceManagerMock is ServiceManagerBase { ) public virtual initializer { __ServiceManagerBase_init(initialOwner, rewardsInitiator); } + + function slashOperator( + IAllocationManager.SlashingParams memory _params + ) public { + _allocationManager.slashOperator({avs: address(this), params: _params}); + } } diff --git a/test/unit/InstantSlasher.t.sol b/test/unit/InstantSlasher.t.sol index 6275422f3..d05cb81b0 100644 --- a/test/unit/InstantSlasher.t.sol +++ b/test/unit/InstantSlasher.t.sol @@ -34,7 +34,7 @@ import {ISlashingRegistryCoordinatorTypes} from import {IBLSApkRegistry, IBLSApkRegistryTypes} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; -import {CoreDeploymentLib} from "../utils/CoreDeployLib.sol"; +import {CoreDeployLib} from "../utils/CoreDeployLib.sol"; import { OperatorWalletLib, Operator, @@ -57,7 +57,7 @@ contract InstantSlasherTest is Test { ProxyAdmin public proxyAdmin; EmptyContract public emptyContract; SlashingRegistryCoordinator public slashingRegistryCoordinator; - CoreDeploymentLib.DeploymentData public coreDeployment; + CoreDeployLib.DeploymentData public coreDeployment; PauserRegistry public pauserRegistry; ERC20Mock public mockToken; StrategyFactory public strategyFactory; @@ -96,7 +96,7 @@ contract InstantSlasherTest is Test { pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - CoreDeploymentLib.DeploymentConfigData memory configData; + CoreDeployLib.DeploymentConfigData memory configData; configData.strategyManager.initialOwner = proxyAdminOwner; configData.strategyManager.initialStrategyWhitelister = proxyAdminOwner; configData.strategyManager.initPausedStatus = 0; @@ -133,7 +133,7 @@ contract InstantSlasherTest is Test { configData.ethPOSDeposit.ethPOSDepositAddress = address(0x123); - coreDeployment = CoreDeploymentLib.deployContracts(address(proxyAdmin), configData); + coreDeployment = CoreDeployLib.deployContracts(address(proxyAdmin), configData); address strategyManagerOwner = Ownable(coreDeployment.strategyManager).owner(); vm.stopPrank(); diff --git a/test/unit/SlashingRegistryCoordinatorUnit.t.sol b/test/unit/SlashingRegistryCoordinatorUnit.t.sol index 680fdac29..12e5106e2 100644 --- a/test/unit/SlashingRegistryCoordinatorUnit.t.sol +++ b/test/unit/SlashingRegistryCoordinatorUnit.t.sol @@ -43,7 +43,7 @@ import {IBLSApkRegistry, IBLSApkRegistryTypes} from "../../src/interfaces/IBLSAp import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; -import {CoreDeploymentLib} from "../utils/CoreDeployLib.sol"; +import {CoreDeployLib} from "../utils/CoreDeployLib.sol"; import { OperatorWalletLib, Operator, @@ -82,7 +82,7 @@ contract SlashingRegistryCoordinatorUnitTestSetup is ProxyAdmin internal proxyAdmin; EmptyContract internal emptyContract; SlashingRegistryCoordinator internal slashingRegistryCoordinator; - CoreDeploymentLib.DeploymentData internal coreDeployment; + CoreDeployLib.DeploymentData internal coreDeployment; PauserRegistry internal pauserRegistry; ERC20Mock internal mockToken; StrategyFactory internal strategyFactory; @@ -173,7 +173,7 @@ contract SlashingRegistryCoordinatorUnitTestSetup is pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - CoreDeploymentLib.DeploymentConfigData memory configData; + CoreDeployLib.DeploymentConfigData memory configData; configData.strategyManager.initialOwner = proxyAdminOwner; configData.strategyManager.initialStrategyWhitelister = proxyAdminOwner; configData.strategyManager.initPausedStatus = 0; @@ -210,7 +210,7 @@ contract SlashingRegistryCoordinatorUnitTestSetup is configData.ethPOSDeposit.ethPOSDepositAddress = address(0x123); - coreDeployment = CoreDeploymentLib.deployContracts(address(proxyAdmin), configData); + coreDeployment = CoreDeployLib.deployContracts(address(proxyAdmin), configData); address strategyManagerOwner = Ownable(coreDeployment.strategyManager).owner(); vm.stopPrank(); diff --git a/test/unit/VetoableSlasher.t.sol b/test/unit/VetoableSlasher.t.sol index 78a89dc7d..9ce1dcbd2 100644 --- a/test/unit/VetoableSlasher.t.sol +++ b/test/unit/VetoableSlasher.t.sol @@ -35,7 +35,7 @@ import {ISlashingRegistryCoordinatorTypes} from import {IBLSApkRegistry, IBLSApkRegistryTypes} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; -import {CoreDeploymentLib} from "../utils/CoreDeployLib.sol"; +import {CoreDeployLib} from "../utils/CoreDeployLib.sol"; import { OperatorWalletLib, Operator, @@ -61,7 +61,7 @@ contract VetoableSlasherTest is Test { VetoableSlasher public vetoableSlasherImplementation; ProxyAdmin public proxyAdmin; EmptyContract public emptyContract; - CoreDeploymentLib.DeploymentData public coreDeployment; + CoreDeployLib.DeploymentData public coreDeployment; PauserRegistry public pauserRegistry; ERC20Mock public mockToken; StrategyFactory public strategyFactory; @@ -103,7 +103,7 @@ contract VetoableSlasherTest is Test { pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - CoreDeploymentLib.DeploymentConfigData memory configData; + CoreDeployLib.DeploymentConfigData memory configData; configData.strategyManager.initialOwner = proxyAdminOwner; configData.strategyManager.initialStrategyWhitelister = proxyAdminOwner; configData.strategyManager.initPausedStatus = 0; @@ -140,7 +140,7 @@ contract VetoableSlasherTest is Test { configData.ethPOSDeposit.ethPOSDepositAddress = address(0x123); - coreDeployment = CoreDeploymentLib.deployContracts(address(proxyAdmin), configData); + coreDeployment = CoreDeployLib.deployContracts(address(proxyAdmin), configData); address strategyManagerOwner = Ownable(coreDeployment.strategyManager).owner(); vm.stopPrank(); diff --git a/test/utils/CoreDeployLib.sol b/test/utils/CoreDeployLib.sol index c0f474e79..5dc7d286e 100644 --- a/test/utils/CoreDeployLib.sol +++ b/test/utils/CoreDeployLib.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; +pragma solidity ^0.8.12; +import {Vm} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/StdJson.sol"; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; @@ -37,9 +39,12 @@ import {PermissionController} from import {UpgradeableProxyLib} from "../unit/UpgradeableProxyLib.sol"; -library CoreDeploymentLib { +library CoreDeployLib { + using stdJson for string; using UpgradeableProxyLib for address; + Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + struct StrategyManagerConfig { uint256 initPausedStatus; address initialOwner; diff --git a/test/utils/MiddlewareDeployLib.sol b/test/utils/MiddlewareDeployLib.sol index 643701ef4..7b4f55d54 100644 --- a/test/utils/MiddlewareDeployLib.sol +++ b/test/utils/MiddlewareDeployLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; +pragma solidity ^0.8.12; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from @@ -11,7 +11,8 @@ import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPa import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; - +import {PermissionController} from + "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import {InstantSlasher} from "../../src/slashers/InstantSlasher.sol"; import {SlashingRegistryCoordinator} from "../../src/SlashingRegistryCoordinator.sol"; import {SocketRegistry} from "../../src/SocketRegistry.sol"; @@ -25,6 +26,19 @@ import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; import {UpgradeableProxyLib} from "../unit/UpgradeableProxyLib.sol"; +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import { + PauserRegistry, + IPauserRegistry +} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; +import {CoreDeployLib} from "./CoreDeployLib.sol"; +import {RegistryCoordinator, IRegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IPermissionController} from + "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; library MiddlewareDeployLib { using UpgradeableProxyLib for address; @@ -81,6 +95,11 @@ library MiddlewareDeployLib { address indexRegistry; address stakeRegistry; address blsApkRegistry; + address operatorStateRetriever; + address registryCoordinator; + address serviceManager; + address pauserRegistry; + address permissionController; } function deployMiddleware( @@ -105,10 +124,12 @@ library MiddlewareDeployLib { ) internal returns (MiddlewareDeployData memory proxies) { proxies.instantSlasher = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); proxies.slashingRegistryCoordinator = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.registryCoordinator = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); proxies.socketRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); proxies.indexRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); proxies.stakeRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); proxies.blsApkRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.serviceManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); return proxies; } @@ -149,6 +170,109 @@ library MiddlewareDeployLib { UpgradeableProxyLib.upgrade(deployments.stakeRegistry, stakeRegistryImpl); } + function upgradeRegistriesM2Coordinator( + address delegationManager, + address avsDirectory, + address allocationManager, + MiddlewareDeployData memory deployments + ) internal { + address stakeRegistryImpl = address( + new StakeRegistry( + IRegistryCoordinator(deployments.registryCoordinator), + IDelegationManager(delegationManager), + IAVSDirectory(avsDirectory), + IAllocationManager(allocationManager) + ) + ); + UpgradeableProxyLib.upgrade(deployments.stakeRegistry, stakeRegistryImpl); + + address blsApkRegistryImpl = + address(new BLSApkRegistry(IRegistryCoordinator(deployments.registryCoordinator))); + UpgradeableProxyLib.upgrade(deployments.blsApkRegistry, blsApkRegistryImpl); + + address indexRegistryImpl = + address(new IndexRegistry(IRegistryCoordinator(deployments.registryCoordinator))); + UpgradeableProxyLib.upgrade(deployments.indexRegistry, indexRegistryImpl); + + address socketRegistryImpl = + address(new SocketRegistry(IRegistryCoordinator(deployments.registryCoordinator))); + UpgradeableProxyLib.upgrade(deployments.socketRegistry, socketRegistryImpl); + } + + function ugpradeServiceManager( + address avsDirectory, + address rewardsCoordinator, + address allocationManager, + MiddlewareDeployData memory deployment, + address admin + ) internal { + address impl = address( + new ServiceManagerMock( + IAVSDirectory(avsDirectory), + IRewardsCoordinator(rewardsCoordinator), + IRegistryCoordinator(deployment.registryCoordinator), + IStakeRegistry(deployment.stakeRegistry), + IPermissionController(deployment.permissionController), + IAllocationManager(allocationManager) + ) + ); + bytes memory serviceManagerUpgradeCall = + abi.encodeCall(ServiceManagerMock.initialize, (admin, admin)); + + UpgradeableProxyLib.upgradeAndCall( + deployment.serviceManager, impl, serviceManagerUpgradeCall + ); + } + + function ugpradeServiceManager( + CoreDeployLib.DeploymentData memory core, + MiddlewareDeployData memory deployment, + address admin + ) internal { + address impl = address( + new ServiceManagerMock( + IAVSDirectory(core.avsDirectory), + IRewardsCoordinator(core.rewardsCoordinator), + IRegistryCoordinator(deployment.registryCoordinator), + IStakeRegistry(deployment.stakeRegistry), + IPermissionController(deployment.permissionController), + IAllocationManager(core.allocationManager) + ) + ); + bytes memory serviceManagerUpgradeCall = + abi.encodeCall(ServiceManagerMock.initialize, (admin, admin)); + + UpgradeableProxyLib.upgradeAndCall( + deployment.serviceManager, impl, serviceManagerUpgradeCall + ); + } + + function upgradeM2Coordinator( + address allocationManager, + MiddlewareDeployData memory deployment, + address admin + ) internal { + address impl = address( + new RegistryCoordinator( + IServiceManager(deployment.serviceManager), + IStakeRegistry(deployment.stakeRegistry), + IBLSApkRegistry(deployment.blsApkRegistry), + IIndexRegistry(deployment.indexRegistry), + ISocketRegistry(deployment.socketRegistry), + IAllocationManager(allocationManager), + IPauserRegistry(deployment.pauserRegistry) + ) + ); + bytes memory registryCoordinatorUpgradeCall = abi.encodeCall( + SlashingRegistryCoordinator.initialize, + (admin, admin, admin, 0, deployment.serviceManager) + ); + + UpgradeableProxyLib.upgradeAndCall( + deployment.registryCoordinator, impl, registryCoordinatorUpgradeCall + ); + } + function upgradeCoordinator( MiddlewareDeployData memory deployments, address allocationManager, @@ -195,4 +319,13 @@ library MiddlewareDeployLib { ); UpgradeableProxyLib.upgrade(deployments.instantSlasher, instantSlasherImpl); } + + function deployPauserRegistry( + address admin + ) internal returns (address) { + address[] memory pausers = new address[](2); + pausers[0] = admin; + pausers[1] = admin; + return address(new PauserRegistry(pausers, admin)); + } } diff --git a/test/utils/OperatorLib.sol b/test/utils/OperatorLib.sol new file mode 100644 index 000000000..73380ce6c --- /dev/null +++ b/test/utils/OperatorLib.sol @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Vm} from "forge-std/Vm.sol"; + +import {console2 as console} from "forge-std/Test.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {ISlashingRegistryCoordinatorTypes} from + "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +import {IRegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import { + IAllocationManager, + IAllocationManagerTypes +} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IBLSApkRegistry, IBLSApkRegistryTypes} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {IStrategyFactory} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyFactory.sol"; +import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {UpgradeableProxyLib} from "../unit/UpgradeableProxyLib.sol"; +import {CoreDeployLib} from "./CoreDeployLib.sol"; +import {ERC20Mock} from "../mocks/ERC20Mock.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {BN256G2} from "./BN256G2.sol"; + +library OperatorLib { + using BN254 for *; + using Strings for uint256; + + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + struct Wallet { + uint256 privateKey; + address addr; + } + + struct BLSWallet { + uint256 privateKey; + BN254.G2Point publicKeyG2; + BN254.G1Point publicKeyG1; + } + + struct Operator { + Wallet key; + BLSWallet signingKey; + } + + function createBLSWallet( + uint256 salt + ) internal returns (BLSWallet memory) { + uint256 privateKey = uint256(keccak256(abi.encodePacked(salt))); + BN254.G1Point memory publicKeyG1 = BN254.generatorG1().scalar_mul(privateKey); + BN254.G2Point memory publicKeyG2 = mul(privateKey); + + return + BLSWallet({privateKey: privateKey, publicKeyG2: publicKeyG2, publicKeyG1: publicKeyG1}); + } + + function createWallet( + uint256 salt + ) internal pure returns (Wallet memory) { + uint256 privateKey = uint256(keccak256(abi.encodePacked(salt))); + address addr = vm.addr(privateKey); + + return Wallet({privateKey: privateKey, addr: addr}); + } + + function createOperator( + string memory name + ) internal returns (Operator memory) { + uint256 salt = uint256(keccak256(abi.encodePacked(name))); + Wallet memory vmWallet = createWallet(salt); + BLSWallet memory blsWallet = createBLSWallet(salt); + + return Operator({key: vmWallet, signingKey: blsWallet}); + } + + function mul( + uint256 x + ) internal returns (BN254.G2Point memory g2Point) { + string[] memory inputs = new string[](5); + inputs[0] = "go"; + inputs[1] = "run"; + inputs[2] = "test/ffi/go/g2mul.go"; + inputs[3] = x.toString(); + + inputs[4] = "1"; + bytes memory res = vm.ffi(inputs); + g2Point.X[1] = abi.decode(res, (uint256)); + + inputs[4] = "2"; + res = vm.ffi(inputs); + g2Point.X[0] = abi.decode(res, (uint256)); + + inputs[4] = "3"; + res = vm.ffi(inputs); + g2Point.Y[1] = abi.decode(res, (uint256)); + + inputs[4] = "4"; + res = vm.ffi(inputs); + g2Point.Y[0] = abi.decode(res, (uint256)); + } + + function signWithOperatorKey( + Operator memory operator, + bytes32 digest + ) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator.key.privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function signWithSigningKey( + Operator memory operator, + bytes32 digest + ) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator.signingKey.privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function aggregate( + BN254.G2Point memory pk1, + BN254.G2Point memory pk2 + ) internal view returns (BN254.G2Point memory apk) { + (apk.X[0], apk.X[1], apk.Y[0], apk.Y[1]) = BN256G2.ECTwistAdd( + pk1.X[0], pk1.X[1], pk1.Y[0], pk1.Y[1], pk2.X[0], pk2.X[1], pk2.Y[0], pk2.Y[1] + ); + } + + function mintMockTokens(Operator memory operator, address token, uint256 amount) internal { + ERC20Mock(token).mint(operator.key.addr, amount); + } + + function depositTokenIntoStrategy( + Operator memory, + address strategyManager, + address strategy, + address token, + uint256 amount + ) internal returns (uint256) { + /// TODO :make sure strategy associated with token + IStrategy strategy = IStrategy(strategy); + require(address(strategy) != address(0), "Strategy was not found"); + IStrategyManager strategyManager = IStrategyManager(strategyManager); + + ERC20Mock(token).approve(address(strategyManager), amount); + uint256 shares = strategyManager.depositIntoStrategy(strategy, IERC20(token), amount); + + return shares; + } + + function registerAsOperator(Operator memory operator, address delegationManager) internal { + IDelegationManager delegationManagerInstance = IDelegationManager(delegationManager); + + delegationManagerInstance.registerAsOperator(operator.key.addr, 0, ""); + } + + function registerOperatorToAVS_M2( + Operator memory operator, + address avsDirectory, + address serviceManager, + address registryCoordinator, + bytes memory quorumNumbers, + string memory socket + ) internal { + IAVSDirectory avsDirectoryInstance = IAVSDirectory(avsDirectory); + RegistryCoordinator registryCoordinatorInstance = RegistryCoordinator(registryCoordinator); + + bytes32 salt = keccak256(abi.encodePacked(block.timestamp, operator.key.addr)); + uint256 expiry = block.timestamp + 1 hours; + + bytes32 operatorRegistrationDigestHash = avsDirectoryInstance + .calculateOperatorAVSRegistrationDigestHash(operator.key.addr, serviceManager, salt, expiry); + + bytes memory signature = signWithOperatorKey(operator, operatorRegistrationDigestHash); + // Get the pubkey registration message hash that needs to be signed + bytes32 pubkeyRegistrationMessageHash = + registryCoordinatorInstance.calculatePubkeyRegistrationMessageHash(operator.key.addr); + + // Sign the pubkey registration message hash + BN254.G1Point memory blsSig = + signMessage(operator.signingKey, pubkeyRegistrationMessageHash); + + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params = IBLSApkRegistryTypes + .PubkeyRegistrationParams({ + pubkeyG1: operator.signingKey.publicKeyG1, + pubkeyG2: operator.signingKey.publicKeyG2, + pubkeyRegistrationSignature: blsSig + }); + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = ISignatureUtils + .SignatureWithSaltAndExpiry({signature: signature, salt: salt, expiry: expiry}); + + // Call the registerOperator function on the registry + registryCoordinatorInstance.registerOperator( + quorumNumbers, socket, params, operatorSignature + ); + } + + function deregisterOperatorFromAVS_M2( + Operator memory operator, + address registryCoordinator + ) internal { + vm.prank(operator.key.addr); + RegistryCoordinator(registryCoordinator).deregisterOperator(""); + } + + function registerOperatorFromAVS_OpSet( + Operator memory operator, + address allocationManager, + address registryCoordinator, + address avs, + uint32[] memory operatorSetIds + ) internal { + bytes memory registrationParamsData; + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + // Get the pubkey registration message hash that needs to be signed + bytes32 pubkeyRegistrationMessageHash = RegistryCoordinator(registryCoordinator) + .calculatePubkeyRegistrationMessageHash(operator.key.addr); + + // Sign the pubkey registration message hash + BN254.G1Point memory signature = + signMessage(operator.signingKey, pubkeyRegistrationMessageHash); + + IBLSApkRegistryTypes.PubkeyRegistrationParams memory blsParams = IBLSApkRegistryTypes + .PubkeyRegistrationParams({ + pubkeyG1: operator.signingKey.publicKeyG1, + pubkeyG2: operator.signingKey.publicKeyG2, + pubkeyRegistrationSignature: signature + }); + + registrationParamsData = abi.encode( + ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, + "test-socket", // Random socket string + blsParams + ); + + IAllocationManagerTypes.RegisterParams memory params = IAllocationManagerTypes + .RegisterParams({avs: avs, operatorSetIds: operatorSetIds, data: registrationParamsData}); + + // Register the operator in the Allocation Manager + allocationManagerInstance.registerForOperatorSets(operator.key.addr, params); + } + + function deregisterOperatorFromAVS_OpSet( + Operator memory operator, + address allocationManager, + address avs, + uint32[] calldata operatorSetIds + ) internal { + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + IAllocationManagerTypes.DeregisterParams memory params = IAllocationManagerTypes + .DeregisterParams({operator: operator.key.addr, avs: avs, operatorSetIds: operatorSetIds}); + + // Deregister the operator in the Allocation Manager + allocationManagerInstance.deregisterFromOperatorSets(params); + } + + function setAllocationDelay( + Operator memory operator, + address allocationManager, + uint32 delay + ) internal { + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + // Set the allocation delay for the operator + allocationManagerInstance.setAllocationDelay(operator.key.addr, delay); + } + + function modifyOperatorAllocations( + Operator memory operator, + address allocationManager, + IAllocationManagerTypes.AllocateParams[] memory params + ) internal { + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + allocationManagerInstance.modifyAllocations(operator.key.addr, params); + } + + function createAndAddOperator( + uint256 salt + ) internal returns (Operator memory) { + Wallet memory operatorKey = createWallet(salt); + BLSWallet memory signingKey = createBLSWallet(salt); + + Operator memory newOperator = Operator({key: operatorKey, signingKey: signingKey}); + + return newOperator; + } + + function signMessage( + BLSWallet memory blsWallet, + bytes32 messageHash + ) internal view returns (BN254.G1Point memory) { + // Hash the message to a point on G1 + BN254.G1Point memory messagePoint = BN254.hashToG1(messageHash); + + // Sign by multiplying the hashed message point with the private key + return messagePoint.scalar_mul(blsWallet.privateKey); + } + + function signMessageWithOperator( + Operator memory operator, + bytes32 messageHash + ) internal view returns (BN254.G1Point memory) { + return signMessage(operator.signingKey, messageHash); + } +}