Skip to content

Commit

Permalink
feat: improve and update slashing upgrade scripts (#955)
Browse files Browse the repository at this point in the history
* feat: add checks for alm and pc impls

* feat: add whitelist checks for strategies

* refactor: make assertions less confusing

* fix: tests reflect rc pause status
  • Loading branch information
wadealexc authored Dec 13, 2024
1 parent 881485b commit acba1a2
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 27 deletions.
8 changes: 8 additions & 0 deletions script/releases/Env.sol
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ library Env {
return _envU32("REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP");
}

function REWARDS_PAUSE_STATUS() internal view returns (uint) {
return _envU256("REWARDS_COORDINATOR_PAUSE_STATUS");
}

/**
* core/
*/
Expand Down Expand Up @@ -301,6 +305,10 @@ library Env {
return ZEnvHelpers.state().envAddress(key);
}

function _envU256(string memory key) private view returns (uint) {
return ZEnvHelpers.state().envU256(key);
}

function _envU64(string memory key) private view returns (uint64) {
return ZEnvHelpers.state().envU64(key);
}
Expand Down
28 changes: 19 additions & 9 deletions script/README.md → script/releases/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
# Release Scripts
# Release Scripting With Zeus

This directory contains the following subdirectories:
This directory is where you will build [Zeus](https://github.com/Layr-Labs/zeus) scripts to manage core protocol releases. Releases are broken up into multiple steps composed of either a `forge` script or a custom shell script. Zeus's role is:
* Provide an environment to your script via environment variables
* Update environment after a release is run to completion
* Track status of releases to ensure steps are run (in order) only once per environment

* `configs`: to store configuration data related to a given network.
* `interfaces`: to store interfaces relevant to your scripts.
* `releases`: to set up more subdirectories corresponding to your release, and the most important `script/` subdirectory.
* `utils`: to define any utility contracts for performing common actions.
**Note about environments:** Zeus scripts are intended to be written once, and run across _any_ environment we use. We currently have 3 live environments (`preprod`, `testnet`, and `mainnet`), and the params/deployment addresses for each live in separate folders in [`layr-labs/eigenlayer-contracts-metadata`](https://github.com/Layr-Labs/eigenlayer-contracts-metadata).

It is intended to be driven by [Zeus](https://github.com/Layr-Labs/zeus), which will run `forge` commands under the hood and track the status of upgrades.
When running or testing a script, _you tell zeus which environment to use,_ and it will fork the corresponding network state and setup environment variables for that environment's params/deployment addresses.

## Using Zeus Templates
##### Getting Started

* Install [Zeus](https://github.com/Layr-Labs/zeus)
* Run `zeus login`

At this point, you should be able to view an environment's config (try `zeus env show preprod`)

---

### Writing a Script

Scripts are broken up into multiple steps TODO

The [zeus-templates](https://github.com/Layr-Labs/zeus-templates) repository provides two base contract classes to facilitate deployment and multisig scripts.

### EOADeployer

Expand Down
54 changes: 37 additions & 17 deletions script/releases/v1.0.0-slashing/1-deployContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "../Env.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/**
* Purpose: use an EOA to deploy all of the new contracts for this upgrade.
Expand Down Expand Up @@ -184,6 +185,7 @@ contract Deploy is EOADeployer {
_validateProxyAdmins();
_validateImplConstructors();
_validateImplsInitialized();
_validateStrategiesAreWhitelisted();
}

/// @dev Validate that the `Env.impl` addresses are updated to be distinct from what the proxy
Expand All @@ -194,29 +196,29 @@ contract Deploy is EOADeployer {
function _validateNewImplAddresses(bool areMatching) internal view {
/// core/ -- can't check AllocationManager as it didn't exist before this deploy

function (bool, string memory) internal pure assertion =
areMatching ? _assertTrue : _assertFalse;
function (address, address, string memory) internal pure assertion =
areMatching ? _assertMatch : _assertNotMatch;

assertion(
_getProxyImpl(address(Env.proxy.avsDirectory())) ==
_getProxyImpl(address(Env.proxy.avsDirectory())),
address(Env.impl.avsDirectory()),
"avsDirectory impl failed"
);

assertion(
_getProxyImpl(address(Env.proxy.delegationManager())) ==
_getProxyImpl(address(Env.proxy.delegationManager())),
address(Env.impl.delegationManager()),
"delegationManager impl failed"
);

assertion(
_getProxyImpl(address(Env.proxy.rewardsCoordinator())) ==
_getProxyImpl(address(Env.proxy.rewardsCoordinator())),
address(Env.impl.rewardsCoordinator()),
"rewardsCoordinator impl failed"
);

assertion(
_getProxyImpl(address(Env.proxy.strategyManager())) ==
_getProxyImpl(address(Env.proxy.strategyManager())),
address(Env.impl.strategyManager()),
"strategyManager impl failed"
);
Expand All @@ -227,43 +229,42 @@ contract Deploy is EOADeployer {
/// pods/

assertion(
Env.beacon.eigenPod().implementation() ==
Env.beacon.eigenPod().implementation(),
address(Env.impl.eigenPod()),
"eigenPod impl failed"
);

assertion(
_getProxyImpl(address(Env.proxy.eigenPodManager())) ==
_getProxyImpl(address(Env.proxy.eigenPodManager())),
address(Env.impl.eigenPodManager()),
"eigenPodManager impl failed"
);

/// strategies/

// FIXME
assertion(
_getProxyImpl(address(Env.proxy.eigenStrategy())) ==
_getProxyImpl(address(Env.proxy.eigenStrategy())),
address(Env.impl.eigenStrategy()),
"eigenStrategy impl failed"
);

assertion(
Env.beacon.strategyBase().implementation() ==
Env.beacon.strategyBase().implementation(),
address(Env.impl.strategyBase()),
"strategyBase impl failed"
);

uint count = Env.instance.strategyBaseTVLLimits_Count();
for (uint i = 0; i < count; i++) {
assertion(
_getProxyImpl(address(Env.instance.strategyBaseTVLLimits(i))) ==
_getProxyImpl(address(Env.instance.strategyBaseTVLLimits(i))),
address(Env.impl.strategyBaseTVLLimits()),
"strategyBaseTVLLimits impl failed"
);
}

assertion(
_getProxyImpl(address(Env.proxy.strategyFactory())) ==
_getProxyImpl(address(Env.proxy.strategyFactory())),
address(Env.impl.strategyFactory()),
"strategyFactory impl failed"
);
Expand Down Expand Up @@ -488,6 +489,25 @@ contract Deploy is EOADeployer {
}
}

/// @dev Iterate over StrategyBaseTVLLimits instances and validate that each is
/// whitelisted for deposit
function _validateStrategiesAreWhitelisted() internal view {
uint count = Env.instance.strategyBaseTVLLimits_Count();
for (uint i = 0; i < count; i++) {
StrategyBaseTVLLimits strategy = Env.instance.strategyBaseTVLLimits(i);

// emit log_named_uint("strategy", i);
// IERC20Metadata underlying = IERC20Metadata(address(strategy.underlyingToken()));
// emit log_named_string("- name", underlying.name());
// emit log_named_string("- symbol", underlying.symbol());
// emit log_named_uint("- totalShares", strategy.totalShares());

bool isWhitelisted = Env.proxy.strategyManager().strategyIsWhitelistedForDeposit(strategy);
// emit log_named_string("- is whitelisted", isWhitelisted ? "true" : "false");
assertTrue(isWhitelisted, "not whitelisted!!");
}
}

/// @dev Query and return `proxyAdmin.getProxyImplementation(proxy)`
function _getProxyImpl(address proxy) internal view returns (address) {
return ProxyAdmin(Env.proxyAdmin()).getProxyImplementation(ITransparentUpgradeableProxy(proxy));
Expand All @@ -498,11 +518,11 @@ contract Deploy is EOADeployer {
return ProxyAdmin(Env.proxyAdmin()).getProxyAdmin(ITransparentUpgradeableProxy(proxy));
}

function _assertTrue(bool b, string memory err) private pure {
assertTrue(b, err);
function _assertMatch(address a, address b, string memory err) private pure {
assertEq(a, b, err);
}

function _assertFalse(bool b, string memory err) private pure {
assertFalse(b, err);
function _assertNotMatch(address a, address b, string memory err) private pure {
assertNotEq(a, b, err);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,28 @@ contract Execute is QueueAndUnpause, Pause {
assertTrue(timelock.isOperationDone(txHash), "Transaction should be complete.");

_validateNewImplAddresses({ areMatching: true });
_validateStrategiesAreWhitelisted();
_validateProxyAdmins();
_validateProxyConstructors();
_validateProxiesInitialized();
}

function _validateNewProxyImplsMatch() internal view {
ProxyAdmin pa = ProxyAdmin(Env.proxyAdmin());

assertTrue(
pa.getProxyImplementation(ITransparentUpgradeableProxy(address(Env.proxy.allocationManager()))) ==
address(Env.impl.allocationManager()),
"allocationManager impl failed"
);

assertTrue(
pa.getProxyImplementation(ITransparentUpgradeableProxy(address(Env.proxy.permissionController()))) ==
address(Env.impl.permissionController()),
"permissionController impl failed"
);
}

/// @dev Mirrors the checks done in 1-deployContracts, but now we check each contract's
/// proxy, as the upgrade should mean that each proxy can see these methods/immutables
function _validateProxyConstructors() internal view {
Expand Down Expand Up @@ -191,7 +208,7 @@ contract Execute is QueueAndUnpause, Pause {
vm.expectRevert(errInit);
rewards.initialize(address(0), 0, address(0), 0, 0);
assertTrue(rewards.owner() == Env.opsMultisig(), "rc.owner invalid");
assertTrue(rewards.paused() == 0, "rc.paused invalid");
assertTrue(rewards.paused() == Env.REWARDS_PAUSE_STATUS(), "rc.paused invalid");
assertTrue(rewards.rewardsUpdater() == Env.REWARDS_UPDATER(), "rc.updater invalid");
assertTrue(rewards.activationDelay() == Env.ACTIVATION_DELAY(), "rc.activationDelay invalid");
assertTrue(rewards.defaultOperatorSplitBips() == Env.DEFAULT_SPLIT_BIPS(), "rc.splitBips invalid");
Expand Down

0 comments on commit acba1a2

Please sign in to comment.