datahaven/contracts/script/deploy/DeployLocal.s.sol
Ahmad Kaouk eaf55fb414
feat: implement weighted top-32 validator selection (#443)
## Overview

Implements deterministic weighted-stake-based validator selection in
`DataHavenServiceManager`, building on the era-targeting submitter model
from PR #433. Previously, `buildNewValidatorSetMessage()` forwarded all
registered operators in arbitrary membership order with no stake-based
ranking, meaning high-stake operators could be displaced by lower-stake
ones when downstream caps applied. This PR fixes that by computing a
weighted stake score per operator and selecting the top-32 candidates
before bridging the set to DataHaven.

Spec: `specs/validator-set-selection/validator-set-selection.md`

## Contract Changes (`DataHavenServiceManager.sol`)

**New state:**
- `MAX_ACTIVE_VALIDATORS = 32` — cap on the outbound validator set
- `mapping(IStrategy => uint96) public strategiesAndMultipliers` —
per-strategy weight used in the selection formula

**Updated `buildNewValidatorSetMessage()`:**
1. Fetches allocated stake for all operators × strategies from
`AllocationManager`
2. Computes `weightedStake(op) = Σ(allocatedStake[op][j] ×
multiplier[j])` across all strategies
3. Filters operators with no solochain address mapping or zero weighted
stake
4. Runs a partial selection sort to pick the top `min(candidateCount,
32)` by descending weighted stake; ties broken by lower operator address
(deterministic)
5. Reverts with `EmptyValidatorSet()` if no eligible candidates remain

**Admin API changes:**
- `addStrategiesToValidatorsSupportedStrategies()` signature changed
from `IStrategy[]` to `IRewardsCoordinatorTypes.StrategyAndMultiplier[]`
— strategy and multiplier are stored atomically in one call, eliminating
the risk of a strategy being registered without a multiplier
- New `setStrategiesAndMultipliers(StrategyAndMultiplier[])` — updates
multiplier weights for existing strategies without touching the
EigenLayer strategy set
- New `getStrategiesAndMultipliers()` — returns all strategies with
their current multipliers
- `removeStrategiesFromValidatorsSupportedStrategies()` now cleans up
multiplier entries on removal

**New error / event:**
- `EmptyValidatorSet()` — reverts when no eligible candidates exist
- `StrategiesAndMultipliersSet(StrategyAndMultiplier[])` — emitted on
add or update of multipliers

## Tests (`ValidatorSetSelection.t.sol`)

New 552-line Foundry test suite covering all cases from the spec:

| Case |
|------|
| `addStrategies` stores multiplier atomically |
| `removeStrategies` deletes multiplier |
| `setStrategiesAndMultipliers` updates without touching the strategy
set |
| `getStrategiesAndMultipliers` returns correct pairs |
| Weighted stake computed correctly across multiple strategies |
| Operators with zero weighted stake are excluded |
| Unset multiplier treated as 0 |
| Top-32 selection when candidate count > 32 |
| All candidates included when count < 32 |
| Tie-breaking by lower operator address |
| `EmptyValidatorSet` revert when no eligible operators |

## Deploy Scripts

- **`DeployBase.s.sol`**: Sets a default multiplier of `1` for all
configured validator strategies after AVS registration via
`setStrategiesAndMultipliers`
- **New `AllocateOperatorStake.s.sol`**: Forge script that allocates
full magnitude (`1e18`) to the validator operator set for a given
operator. Must be run at least one block after `SignUpValidator` to
respect EigenLayer's allocation configuration delay.

## E2E Framework

- **`validators.ts` — `registerOperator()`**: Extended to deposit tokens
into each deployed strategy and allocate full magnitude to the DataHaven
operator set after registration. Previously operators registered without
staking, producing zero weighted stake and getting filtered out by the
new selection logic.
- **`setup-validators.ts`**: Added a stake allocation pass after the
registration loop, invoking `AllocateOperatorStake.s.sol` per validator.
- **`validator-set-update.test.ts`**: Added debug logging for
transaction receipts and the `OutboundMessageAccepted` /
`ExternalValidatorsSet` events.
- **`generated.ts`**: Regenerated contract bindings to include new
functions, events, and the `EmptyValidatorSet` error.

## ⚠️ Breaking Changes ⚠️

- `addStrategiesToValidatorsSupportedStrategies(IStrategy[])` →
`addStrategiesToValidatorsSupportedStrategies(StrategyAndMultiplier[])`:
callers must supply multipliers alongside strategies.
- Operators with zero weighted stake are no longer included in the
bridged validator set.

## Rollout Notes

1. PR #433 (era-targeting + submitter role) must be deployed first
2. Deploy this `ServiceManager` upgrade
3. Confirm `strategiesAndMultipliers` is set for all active strategies
(default multiplier `1` applied automatically by `DeployBase`)
4. Deploy the runtime cap-enforcement changes (spec section 10.2)
5. Submitter daemon requires no changes — continues submitting
`targetEra = ActiveEra + 1`
2026-02-24 09:23:57 +01:00

676 lines
28 KiB
Solidity

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.27;
import {DeployBase, StrategyInfo, ServiceManagerInitParams} from "./DeployBase.s.sol";
// Snowbridge imports for function signatures
import {BeefyClient} from "snowbridge/src/BeefyClient.sol";
import {AgentExecutor} from "snowbridge/src/AgentExecutor.sol";
import {IGatewayV2} from "snowbridge/src/v2/IGateway.sol";
// Logging import
import {Logging} from "../utils/Logging.sol";
// Additional imports specific to local deployment
import {
ERC20PresetFixedSupply
} from "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import {
ITransparentUpgradeableProxy
} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {
TransparentUpgradeableProxy
} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
// EigenLayer core contract imports for implementation declarations
import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol";
import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol";
import {DelegationManager} from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol";
import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol";
import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol";
import {
PermissionController
} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol";
import {IETHPOSDeposit} from "eigenlayer-contracts/src/contracts/interfaces/IETHPOSDeposit.sol";
import {
IRewardsCoordinatorTypes
} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {EigenStrategy} from "eigenlayer-contracts/src/contracts/strategies/EigenStrategy.sol";
import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol";
import {EigenPod} from "eigenlayer-contracts/src/contracts/pods/EigenPod.sol";
import {EigenPodManager} from "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol";
import {
StrategyBaseTVLLimits
} from "eigenlayer-contracts/src/contracts/strategies/StrategyBaseTVLLimits.sol";
import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol";
import {DataHavenServiceManager} from "../../src/DataHavenServiceManager.sol";
/**
* @title DeployLocal
* @notice Deployment script for local development (anvil) - deploys full EigenLayer infrastructure
*/
contract DeployLocal is DeployBase {
using SafeERC20 for IERC20;
// Local-specific EigenLayer Contract declarations
EmptyContract public emptyContract;
RewardsCoordinator public rewardsCoordinatorImplementation;
PermissionController public permissionControllerImplementation;
AllocationManager public allocationManagerImplementation;
DelegationManager public delegationImplementation;
StrategyManager public strategyManagerImplementation;
AVSDirectory public avsDirectoryImplementation;
EigenPodManager public eigenPodManagerImplementation;
UpgradeableBeacon public eigenPodBeacon;
EigenPod public eigenPodImplementation;
StrategyBaseTVLLimits public baseStrategyImplementation;
StrategyInfo[] public deployedStrategies;
IStrategy public eigenStrategy;
// EigenLayer required semver
string public constant SEMVER = "v1.0.0";
function run() public {
totalSteps = 4; // Total major deployment steps for local
_executeSharedDeployment();
// Fund ServiceManager with tokens for rewards distribution (local only)
_fundServiceManagerWithTokens();
}
/**
* @notice Fund the ServiceManager with tokens for rewards distribution
* @dev Only for local deployments - transfers tokens from operator to ServiceManager
*/
function _fundServiceManagerWithTokens() internal {
if (deployedStrategies.length == 0) {
Logging.logInfo("No strategies deployed, skipping ServiceManager funding");
return;
}
// Read ServiceManager address from deployments file
string memory network = _getNetworkName();
string memory deploymentPath =
string.concat(vm.projectRoot(), "/deployments/", network, ".json");
string memory json = vm.readFile(deploymentPath);
address serviceManager = vm.parseJsonAddress(json, ".ServiceManager");
// Get token address from deployed strategies
address tokenAddress = deployedStrategies[0].underlyingToken;
// Transfer 500,000 tokens (half of minted supply) to ServiceManager
uint256 fundAmount = 500000 * 1e18;
Logging.logSection("Funding ServiceManager with Reward Tokens");
vm.broadcast(_operatorPrivateKey);
IERC20(tokenAddress).safeTransfer(serviceManager, fundAmount);
Logging.logStep(
string.concat("Transferred ", vm.toString(fundAmount), " tokens to ServiceManager")
);
}
// Implementation of abstract functions from DeployBase
function _getNetworkName() internal pure override returns (string memory) {
return "anvil";
}
function _getDeploymentMode() internal pure override returns (string memory) {
return "LOCAL";
}
function _setupEigenLayerContracts(
EigenLayerConfig memory eigenLayerConfig
) internal override returns (ProxyAdmin) {
Logging.logHeader("EIGENLAYER CORE CONTRACTS DEPLOYMENT");
Logging.logInfo("Deploying core infrastructure contracts");
// Deploy proxy admin for ability to upgrade proxy contracts
vm.broadcast(_deployerPrivateKey);
ProxyAdmin proxyAdmin = new ProxyAdmin();
Logging.logContractDeployed("ProxyAdmin", address(proxyAdmin));
// Deploy pauser registry
PauserRegistry pauserRegistry = _deployPauserRegistry(eigenLayerConfig);
Logging.logContractDeployed("PauserRegistry", address(pauserRegistry));
// Deploy empty contract to use as initial implementation for proxies
vm.broadcast(_deployerPrivateKey);
emptyContract = new EmptyContract();
Logging.logContractDeployed("EmptyContract", address(emptyContract));
// Deploy proxies that will point to implementations
Logging.logSection("Deploying Proxy Contracts");
_deployProxies(proxyAdmin);
Logging.logStep("Initial proxies deployed successfully");
vm.broadcast(_deployerPrivateKey);
eigenStrategy =
IStrategy(address(new EigenStrategy(strategyManager, pauserRegistry, SEMVER)));
Logging.logContractDeployed("EigenStrategy", address(eigenStrategy));
// Setup ETH2 deposit contract for EigenPod functionality
ethPOSDeposit = IETHPOSDeposit(getETHPOSDepositAddress());
Logging.logContractDeployed("ETHPOSDeposit", address(ethPOSDeposit));
// Deploy EigenPod implementation and beacon
vm.broadcast(_deployerPrivateKey);
eigenPodImplementation = new EigenPod(ethPOSDeposit, eigenPodManager, SEMVER);
vm.broadcast(_deployerPrivateKey);
eigenPodBeacon = new UpgradeableBeacon(address(eigenPodImplementation));
Logging.logContractDeployed("EigenPod Implementation", address(eigenPodImplementation));
Logging.logContractDeployed("EigenPod Beacon", address(eigenPodBeacon));
// Deploy implementation contracts
Logging.logSection("Deploying Implementation Contracts");
_deployImplementations(eigenLayerConfig, pauserRegistry);
Logging.logStep("Implementation contracts deployed successfully");
// Upgrade proxies to point to implementations and initialize
Logging.logSection("Initializing Contracts");
_upgradeAndInitializeProxies(eigenLayerConfig, proxyAdmin);
Logging.logStep("Proxies upgraded and initialized successfully");
// Deploy strategy implementation and create strategy proxies
Logging.logSection("Deploying Strategy Contracts");
_deployStrategies(pauserRegistry, proxyAdmin);
Logging.logStep("Strategy contracts deployed successfully");
// Transfer ownership of core contracts
vm.broadcast(_deployerPrivateKey);
proxyAdmin.transferOwnership(eigenLayerConfig.executorMultisig);
vm.broadcast(_deployerPrivateKey);
eigenPodBeacon.transferOwnership(eigenLayerConfig.executorMultisig);
Logging.logStep("Ownership transferred to multisig");
Logging.logFooter();
return proxyAdmin;
}
function _createServiceManagerProxy(
DataHavenServiceManager implementation,
ProxyAdmin proxyAdmin,
ServiceManagerInitParams memory params
) internal override returns (DataHavenServiceManager) {
// Prepare strategies for service manager (local deployment has deployed strategies)
_prepareStrategiesForServiceManager(params);
vm.broadcast(_deployerPrivateKey);
bytes memory initData = abi.encodeWithSelector(
DataHavenServiceManager.initialize.selector,
params.avsOwner,
params.rewardsInitiator,
params.validatorsStrategiesAndMultipliers,
params.gateway,
params.validatorSetSubmitter
);
TransparentUpgradeableProxy proxy =
new TransparentUpgradeableProxy(address(implementation), address(proxyAdmin), initData);
return DataHavenServiceManager(address(proxy));
}
function _outputDeployedAddresses(
BeefyClient beefyClient,
AgentExecutor agentExecutor,
IGatewayV2 gateway,
DataHavenServiceManager serviceManager,
DataHavenServiceManager serviceManagerImplementation,
address rewardsAgent
) internal override {
Logging.logHeader("DEPLOYMENT SUMMARY");
Logging.logSection("Snowbridge Contracts + Rewards Agent");
Logging.logContractDeployed("BeefyClient", address(beefyClient));
Logging.logContractDeployed("AgentExecutor", address(agentExecutor));
Logging.logContractDeployed("Gateway", address(gateway));
Logging.logContractDeployed("RewardsAgent", rewardsAgent);
Logging.logSection("DataHaven Contracts");
Logging.logContractDeployed("ServiceManager", address(serviceManager));
Logging.logSection("EigenLayer Core Contracts");
Logging.logContractDeployed("DelegationManager", address(delegation));
Logging.logContractDeployed("StrategyManager", address(strategyManager));
Logging.logContractDeployed("AVSDirectory", address(avsDirectory));
Logging.logContractDeployed("EigenPodManager", address(eigenPodManager));
Logging.logContractDeployed("EigenPodBeacon", address(eigenPodBeacon));
Logging.logContractDeployed("RewardsCoordinator", address(rewardsCoordinator));
Logging.logContractDeployed("AllocationManager", address(allocationManager));
Logging.logContractDeployed("EigenStrategy", address(eigenStrategy));
Logging.logContractDeployed("PermissionController", address(permissionController));
Logging.logContractDeployed("ETHPOSDeposit", address(ethPOSDeposit));
Logging.logSection("Strategy Contracts");
Logging.logContractDeployed(
"BaseStrategyImplementation", address(baseStrategyImplementation)
);
for (uint256 i = 0; i < deployedStrategies.length; i++) {
Logging.logContractDeployed(
string.concat("DeployedStrategy", vm.toString(i)), deployedStrategies[i].address_
);
}
Logging.logFooter();
// Write to deployment file for future reference
string memory network = _getNetworkName();
string memory deploymentPath =
string.concat(vm.projectRoot(), "/deployments/", network, ".json");
// Create directory if it doesn't exist
vm.createDir(string.concat(vm.projectRoot(), "/deployments"), true);
// Create JSON with deployed addresses
string memory json = "{";
json = string.concat(json, '"network": "', network, '",');
// Snowbridge contracts
json = string.concat(json, '"BeefyClient": "', vm.toString(address(beefyClient)), '",');
json = string.concat(json, '"AgentExecutor": "', vm.toString(address(agentExecutor)), '",');
json = string.concat(json, '"Gateway": "', vm.toString(address(gateway)), '",');
json =
string.concat(json, '"ServiceManager": "', vm.toString(address(serviceManager)), '",');
json = string.concat(
json,
'"ServiceManagerImplementation": "',
vm.toString(address(serviceManagerImplementation)),
'",'
);
json = string.concat(json, '"RewardsAgent": "', vm.toString(rewardsAgent), '",');
// EigenLayer contracts
json = string.concat(json, '"DelegationManager": "', vm.toString(address(delegation)), '",');
json = string.concat(
json, '"StrategyManager": "', vm.toString(address(strategyManager)), '",'
);
json = string.concat(json, '"AVSDirectory": "', vm.toString(address(avsDirectory)), '",');
json = string.concat(
json, '"EigenPodManager": "', vm.toString(address(eigenPodManager)), '",'
);
json =
string.concat(json, '"EigenPodBeacon": "', vm.toString(address(eigenPodBeacon)), '",');
json = string.concat(
json, '"RewardsCoordinator": "', vm.toString(address(rewardsCoordinator)), '",'
);
json = string.concat(
json, '"AllocationManager": "', vm.toString(address(allocationManager)), '",'
);
json = string.concat(
json, '"PermissionController": "', vm.toString(address(permissionController)), '",'
);
json = string.concat(json, '"ETHPOSDeposit": "', vm.toString(address(ethPOSDeposit)), '",');
json = string.concat(
json,
'"BaseStrategyImplementation": "',
vm.toString(address(baseStrategyImplementation)),
'"'
);
// Add strategies with token information
if (deployedStrategies.length > 0) {
json = string.concat(json, ",");
json = string.concat(json, '"DeployedStrategies": [');
for (uint256 i = 0; i < deployedStrategies.length; i++) {
json = string.concat(json, "{");
json = string.concat(
json, '"address": "', vm.toString(deployedStrategies[i].address_), '",'
);
json = string.concat(
json,
'"underlyingToken": "',
vm.toString(deployedStrategies[i].underlyingToken),
'",'
);
json = string.concat(
json, '"tokenCreator": "', vm.toString(deployedStrategies[i].tokenCreator), '"'
);
json = string.concat(json, "}");
// Add comma if not the last element
if (i < deployedStrategies.length - 1) {
json = string.concat(json, ",");
}
}
json = string.concat(json, "]");
}
json = string.concat(json, "}");
// Write to file
vm.writeFile(deploymentPath, json);
Logging.logInfo(string.concat("Deployment info saved to: ", deploymentPath));
}
// LOCAL-SPECIFIC FUNCTIONS
function _prepareStrategiesForServiceManager(
ServiceManagerInitParams memory params
) internal view {
if (params.validatorsStrategiesAndMultipliers.length == 0) {
IRewardsCoordinatorTypes.StrategyAndMultiplier[] memory sm =
new IRewardsCoordinatorTypes.StrategyAndMultiplier[](deployedStrategies.length);
for (uint256 i = 0; i < deployedStrategies.length; i++) {
sm[i] = IRewardsCoordinatorTypes.StrategyAndMultiplier({
strategy: IStrategy(deployedStrategies[i].address_), multiplier: 1
});
}
params.validatorsStrategiesAndMultipliers = sm;
}
}
function _deployProxies(
ProxyAdmin proxyAdmin
) internal {
// Deploy proxies with empty implementation initially
vm.broadcast(_deployerPrivateKey);
delegation = DelegationManager(
address(
new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")
)
);
Logging.logContractDeployed("DelegationManager Proxy", address(delegation));
vm.broadcast(_deployerPrivateKey);
strategyManager = StrategyManager(
address(
new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")
)
);
Logging.logContractDeployed("StrategyManager Proxy", address(strategyManager));
vm.broadcast(_deployerPrivateKey);
avsDirectory = AVSDirectory(
address(
new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")
)
);
Logging.logContractDeployed("AVSDirectory Proxy", address(avsDirectory));
vm.broadcast(_deployerPrivateKey);
eigenPodManager = EigenPodManager(
address(
new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")
)
);
Logging.logContractDeployed("EigenPodManager Proxy", address(eigenPodManager));
vm.broadcast(_deployerPrivateKey);
rewardsCoordinator = RewardsCoordinator(
address(
new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")
)
);
Logging.logContractDeployed("RewardsCoordinator Proxy", address(rewardsCoordinator));
vm.broadcast(_deployerPrivateKey);
allocationManager = AllocationManager(
address(
new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")
)
);
Logging.logContractDeployed("AllocationManager Proxy", address(allocationManager));
vm.broadcast(_deployerPrivateKey);
permissionController = PermissionController(
address(
new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")
)
);
Logging.logContractDeployed("PermissionController Proxy", address(permissionController));
}
function _deployImplementations(
EigenLayerConfig memory config,
PauserRegistry pauserRegistry
) internal {
// Deploy implementation contracts
vm.broadcast(_deployerPrivateKey);
delegationImplementation = new DelegationManager(
strategyManager,
eigenPodManager,
allocationManager,
pauserRegistry,
permissionController,
config.minWithdrawalDelayBlocks,
SEMVER
);
Logging.logContractDeployed(
"DelegationManager Implementation", address(delegationImplementation)
);
vm.broadcast(_deployerPrivateKey);
strategyManagerImplementation =
new StrategyManager(allocationManager, delegation, pauserRegistry, SEMVER);
Logging.logContractDeployed(
"StrategyManager Implementation", address(strategyManagerImplementation)
);
vm.broadcast(_deployerPrivateKey);
avsDirectoryImplementation = new AVSDirectory(delegation, pauserRegistry, SEMVER);
Logging.logContractDeployed(
"AVSDirectory Implementation", address(avsDirectoryImplementation)
);
vm.broadcast(_deployerPrivateKey);
eigenPodManagerImplementation =
new EigenPodManager(ethPOSDeposit, eigenPodBeacon, delegation, pauserRegistry, SEMVER);
Logging.logContractDeployed(
"EigenPodManager Implementation", address(eigenPodManagerImplementation)
);
vm.broadcast(_deployerPrivateKey);
rewardsCoordinatorImplementation = new RewardsCoordinator(
IRewardsCoordinatorTypes.RewardsCoordinatorConstructorParams(
delegation,
strategyManager,
allocationManager,
pauserRegistry,
permissionController,
config.calculationIntervalSeconds,
config.maxRewardsDuration,
config.maxRetroactiveLength,
config.maxFutureLength,
config.genesisRewardsTimestamp,
SEMVER
)
);
Logging.logContractDeployed(
"RewardsCoordinator Implementation", address(rewardsCoordinatorImplementation)
);
vm.broadcast(_deployerPrivateKey);
allocationManagerImplementation = new AllocationManager(
delegation,
eigenStrategy,
pauserRegistry,
permissionController,
config.deallocationDelay,
config.allocationConfigurationDelay,
SEMVER
);
Logging.logContractDeployed(
"AllocationManager Implementation", address(allocationManagerImplementation)
);
vm.broadcast(_deployerPrivateKey);
permissionControllerImplementation = new PermissionController(SEMVER);
Logging.logContractDeployed(
"PermissionController Implementation", address(permissionControllerImplementation)
);
}
function _upgradeAndInitializeProxies(
EigenLayerConfig memory config,
ProxyAdmin proxyAdmin
) internal {
// Initialize DelegationManager
vm.broadcast(_deployerPrivateKey);
proxyAdmin.upgradeAndCall(
ITransparentUpgradeableProxy(payable(address(delegation))),
address(delegationImplementation),
abi.encodeWithSelector(
DelegationManager.initialize.selector, config.delegationInitPausedStatus
)
);
Logging.logStep("DelegationManager initialized");
// Initialize StrategyManager
vm.broadcast(_deployerPrivateKey);
proxyAdmin.upgradeAndCall(
ITransparentUpgradeableProxy(payable(address(strategyManager))),
address(strategyManagerImplementation),
abi.encodeWithSelector(
StrategyManager.initialize.selector,
config.executorMultisig,
config.operationsMultisig,
config.strategyManagerInitPausedStatus
)
);
Logging.logStep("StrategyManager initialized");
// Initialize AVSDirectory
vm.broadcast(_deployerPrivateKey);
proxyAdmin.upgradeAndCall(
ITransparentUpgradeableProxy(payable(address(avsDirectory))),
address(avsDirectoryImplementation),
abi.encodeWithSelector(
AVSDirectory.initialize.selector,
config.executorMultisig,
0 // Initial paused status
)
);
Logging.logStep("AVSDirectory initialized");
// Initialize EigenPodManager
vm.broadcast(_deployerPrivateKey);
proxyAdmin.upgradeAndCall(
ITransparentUpgradeableProxy(payable(address(eigenPodManager))),
address(eigenPodManagerImplementation),
abi.encodeWithSelector(
EigenPodManager.initialize.selector,
config.executorMultisig,
config.eigenPodManagerInitPausedStatus
)
);
Logging.logStep("EigenPodManager initialized");
// Initialize RewardsCoordinator
vm.broadcast(_deployerPrivateKey);
proxyAdmin.upgradeAndCall(
ITransparentUpgradeableProxy(payable(address(rewardsCoordinator))),
address(rewardsCoordinatorImplementation),
abi.encodeWithSelector(
RewardsCoordinator.initialize.selector,
config.executorMultisig,
config.rewardsCoordinatorInitPausedStatus,
config.rewardsUpdater,
config.activationDelay,
config.globalCommissionBips
)
);
Logging.logStep("RewardsCoordinator initialized");
// Initialize AllocationManager
vm.broadcast(_deployerPrivateKey);
proxyAdmin.upgradeAndCall(
ITransparentUpgradeableProxy(payable(address(allocationManager))),
address(allocationManagerImplementation),
abi.encodeWithSelector(
AllocationManager.initialize.selector, config.allocationManagerInitPausedStatus
)
);
Logging.logStep("AllocationManager initialized");
// Initialize PermissionController (no initialization function)
vm.broadcast(_deployerPrivateKey);
proxyAdmin.upgrade(
ITransparentUpgradeableProxy(payable(address(permissionController))),
address(permissionControllerImplementation)
);
Logging.logStep("PermissionController upgraded");
}
function _deployStrategies(
PauserRegistry pauserRegistry,
ProxyAdmin proxyAdmin
) internal {
// Deploy base strategy implementation
vm.broadcast(_deployerPrivateKey);
baseStrategyImplementation =
new StrategyBaseTVLLimits(strategyManager, pauserRegistry, SEMVER);
Logging.logContractDeployed("Strategy Implementation", address(baseStrategyImplementation));
// Create default test token and strategy if needed
// In a production environment, this would be replaced with actual token addresses.
if (block.chainid != 1) {
// We mint tokens to the operator account so that it then has a balance to deposit as stake.
vm.broadcast(_deployerPrivateKey);
address testToken =
address(new ERC20PresetFixedSupply("TestToken", "TEST", 1000000 ether, _operator));
Logging.logContractDeployed("TestToken", testToken);
// Create strategy for test token
vm.broadcast(_deployerPrivateKey);
StrategyBaseTVLLimits strategy = StrategyBaseTVLLimits(
address(
new TransparentUpgradeableProxy(
address(baseStrategyImplementation),
address(proxyAdmin),
abi.encodeWithSelector(
StrategyBaseTVLLimits.initialize.selector,
1000000 ether, // maxPerDeposit
10000000 ether, // maxDeposits
IERC20(testToken)
)
)
)
);
// Store the strategy with its token information
deployedStrategies.push(
StrategyInfo({
address_: address(strategy), underlyingToken: testToken, tokenCreator: _operator
})
);
Logging.logContractDeployed("Test Strategy", address(strategy));
}
// Whitelist strategies in the strategy manager
IStrategy[] memory strategies = new IStrategy[](deployedStrategies.length);
for (uint256 i = 0; i < deployedStrategies.length; i++) {
strategies[i] = IStrategy(deployedStrategies[i].address_);
}
vm.broadcast(_operationsMultisigPrivateKey);
strategyManager.addStrategiesToDepositWhitelist(strategies);
}
function _deployPauserRegistry(
EigenLayerConfig memory config
) internal returns (PauserRegistry) {
// Use the array of pauser addresses directly from the config
vm.broadcast(_deployerPrivateKey);
return new PauserRegistry(config.pauserAddresses, config.unpauserAddress);
}
function _prepareStrategiesForServiceManager(
AVSConfig memory config,
StrategyInfo[] memory strategies
) internal pure {
if (config.validatorsStrategies.length == 0) {
config.validatorsStrategies = new address[](strategies.length);
for (uint256 i = 0; i < strategies.length; i++) {
config.validatorsStrategies[i] = strategies[i].address_;
}
}
}
}