Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[DO NOT MERGE]: v1.2.0 on v1.1.1 #1156

Draft
wants to merge 10 commits into
base: feat/v1.1.1
Choose a base branch
from
146 changes: 146 additions & 0 deletions script/releases/v1.2.0-slashing/1-eoa.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import {EOADeployer} from "zeus-templates/templates/EOADeployer.sol";
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";
import "src/contracts/libraries/BeaconChainProofs.sol";

contract Deploy is EOADeployer {
using Env for *;

function _runAsEOA() internal override {
vm.startBroadcast();

// Deploy EigenPodManager Impl
deployImpl({
name: type(EigenPodManager).name,
deployedTo: address(new EigenPodManager({
_ethPOS: Env.ethPOS(),
_eigenPodBeacon: Env.beacon.eigenPod(),
_delegationManager: Env.proxy.delegationManager(),
_pauserRegistry: Env.impl.pauserRegistry()
}))
});

// Deploy EigenPodBeacon Impl
deployImpl({
name: type(EigenPod).name,
deployedTo: address(new EigenPod({
_ethPOS: Env.ethPOS(),
_eigenPodManager: Env.proxy.eigenPodManager(),
_GENESIS_TIME: Env.EIGENPOD_GENESIS_TIME()
}))
});

vm.stopBroadcast();
}

function testDeploy() public virtual {
_runAsEOA();
_validateNewImplAddresses(false);
_validateProxyAdmins();
_validateImplConstructors();
_validateImplsInitialized();
}

/// @dev Validate that the `Env.impl` addresses are updated to be distinct from what the proxy
/// admin reports as the current implementation address.
///
/// Note: The upgrade script can call this with `areMatching == true` to check that these impl
/// addresses _are_ matches.
function _validateNewImplAddresses(bool areMatching) internal view {
function(address, address, string memory)
internal
pure assertion = areMatching ? _assertMatch : _assertNotMatch;

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

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

/// @dev Ensure each deployed TUP/beacon is owned by the proxyAdmin/executorMultisig
function _validateProxyAdmins() internal view {
assertTrue(
Env.beacon.eigenPod().owner() == Env.executorMultisig(),
"eigenPod beacon owner incorrect"
);

assertTrue(
_getProxyAdmin(address(Env.proxy.eigenPodManager())) == Env.proxyAdmin(),
"eigenPodManager proxyAdmin incorrect"
);
}

/// @dev Validate the immutables set in the new implementation constructors
function _validateImplConstructors() internal view {
/// pods/
EigenPod eigenPod = Env.impl.eigenPod();
assertTrue(eigenPod.ethPOS() == Env.ethPOS(), "ep.ethPOS invalid");
assertTrue(eigenPod.eigenPodManager() == Env.proxy.eigenPodManager(), "ep.epm invalid");
assertTrue(eigenPod.GENESIS_TIME() == Env.EIGENPOD_GENESIS_TIME(), "ep.genesis invalid");

/// manager/
EigenPodManager eigenPodManager = Env.impl.eigenPodManager();
assertTrue(eigenPodManager.ethPOS() == Env.ethPOS(), "epm.ethPOS invalid");
assertTrue(eigenPodManager.eigenPodBeacon() == Env.beacon.eigenPod(), "epm.epBeacon invalid");
assertTrue(eigenPodManager.delegationManager() == Env.proxy.delegationManager(), "epm.dm invalid");
assertTrue(eigenPodManager.pauserRegistry() == Env.impl.pauserRegistry(), "epm.pR invalid");
}

function _validateImplsInitialized() internal {
bytes memory errInit = "Initializable: contract is already initialized";

EigenPod eigenPod = Env.impl.eigenPod();
vm.expectRevert(errInit);
eigenPod.initialize(address(0));

EigenPodManager eigenPodManager = Env.impl.eigenPodManager();
vm.expectRevert(errInit);
eigenPodManager.initialize(address(0), 0);
}

/// @dev Query and return `proxyAdmin.getProxyImplementation(proxy)`
function _getProxyImpl(address proxy) internal view returns (address) {
return
ProxyAdmin(Env.proxyAdmin()).getProxyImplementation(
ITransparentUpgradeableProxy(proxy)
);
}

/// @dev Query and return `proxyAdmin.getProxyAdmin(proxy)`
function _getProxyAdmin(address proxy) internal view returns (address) {
return
ProxyAdmin(Env.proxyAdmin()).getProxyAdmin(
ITransparentUpgradeableProxy(proxy)
);
}

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

function _assertNotMatch(
address a,
address b,
string memory err
) private pure {
assertNotEq(a, b, err);
}
}
112 changes: 112 additions & 0 deletions script/releases/v1.2.0-slashing/2-queueUpgradeAndTimestampSetter.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import {Deploy} from "./1-eoa.s.sol";
import "../Env.sol";
import "forge-std/console.sol";

import {MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol";
import "zeus-templates/utils/Encode.sol";

import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";

/**
* Purpose: Queues an upgrade to EP/EPM
* and sets the timestamp submitter to the ops multisig
*/
contract QueueUpgradeAndTimestampSetter is MultisigBuilder, Deploy {
using Env for *;
using Encode for *;

function _runAsMultisig()
internal
virtual
override
prank(Env.opsMultisig())
{
bytes memory calldata_to_executor = _getCalldataToExecutor_queueUpgrade();

TimelockController timelock = Env.timelockController();
timelock.schedule({
target: Env.executorMultisig(),
value: 0,
data: calldata_to_executor,
predecessor: 0,
salt: 0,
delay: timelock.getMinDelay()
});
}

/// @dev Get the calldata to be sent from the timelock to the executor
function _getCalldataToExecutor_queueUpgrade() internal virtual returns (bytes memory) {
MultisigCall[] storage executorCalls = Encode
.newMultisigCalls()
.append({
to: address(Env.beacon.eigenPod()),
data: Encode.upgradeableBeacon.upgradeTo({
newImpl: address(Env.impl.eigenPod())
})
})
.append({
to: Env.proxyAdmin(),
data: Encode.proxyAdmin.upgrade({
proxy: address(Env.proxy.eigenPodManager()),
impl: address(Env.impl.eigenPodManager())
})
});

// Set the timestamp submitter to the ops multisig
executorCalls.append({
to: address(Env.proxy.eigenPodManager()),
data: abi.encodeCall(EigenPodManager.setProofTimestampSetter, (address(Env.opsMultisig())))
});

return
Encode.gnosisSafe.execTransaction({
from: address(Env.timelockController()),
to: address(Env.multiSendCallOnly()),
op: Encode.Operation.DelegateCall,
data: Encode.multiSend(executorCalls)
});
}

function testScript() public virtual {
runAsEOA();

TimelockController timelock = Env.timelockController();
bytes memory calldata_to_executor = _getCalldataToExecutor_queueUpgrade();
bytes32 txHash = timelock.hashOperation({
target: Env.executorMultisig(),
value: 0,
data: calldata_to_executor,
predecessor: 0,
salt: 0
});

// Check that the upgrade does not exist in the timelock
assertFalse(
timelock.isOperationPending(txHash),
"Transaction should NOT be queued."
);

execute();

// Check that the upgrade has been added to the timelock
assertTrue(
timelock.isOperationPending(txHash),
"Transaction should be queued."
);
}

function getTimelockId() public virtual returns (bytes32) {
TimelockController timelock = Env.timelockController();
bytes memory calldata_to_executor = _getCalldataToExecutor_queueUpgrade();
return timelock.hashOperation({
target: Env.executorMultisig(),
value: 0,
data: calldata_to_executor,
predecessor: 0,
salt: 0
});
}
}
104 changes: 104 additions & 0 deletions script/releases/v1.2.0-slashing/3-queueUnpause.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import {QueueUpgradeAndTimestampSetter} from "./2-queueUpgradeAndTimestampSetter.s.sol";
import "../Env.sol";
import "forge-std/console.sol";
import {MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol";
import "zeus-templates/utils/Encode.sol";

import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";

/**
* Purpose: Unpauses the EPM.
* This needs to be done separately from the upgrade, since we must do actions between
* the upgrade and unpause.
*/
contract QueueUnpause is QueueUpgradeAndTimestampSetter {
using Env for *;
using Encode for *;

function _runAsMultisig()
internal
virtual
override(QueueUpgradeAndTimestampSetter)
prank(Env.opsMultisig())
{
bytes memory calldata_to_executor = _getCalldataToExecutor_queueUnpause();

TimelockController timelock = Env.timelockController();
timelock.schedule({
target: Env.executorMultisig(),
value: 0,
data: calldata_to_executor,
predecessor: QueueUpgradeAndTimestampSetter.getTimelockId(),
salt: 0,
delay: timelock.getMinDelay()
});
}

/// @dev Get the calldata to be sent from the timelock to the executor
function _getCalldataToExecutor_queueUnpause() internal virtual returns (bytes memory) {
MultisigCall[] storage executorCalls = Encode
.newMultisigCalls()
.append({
to: address(Env.proxy.eigenPodManager()),
data: abi.encodeCall(Pausable.unpause, 0)
});

return
Encode.gnosisSafe.execTransaction({
from: address(Env.timelockController()),
to: address(Env.multiSendCallOnly()),
op: Encode.Operation.DelegateCall,
data: Encode.multiSend(executorCalls)
});
}

function testScript() public virtual override(QueueUpgradeAndTimestampSetter) {
runAsEOA();

// 1. Run queue upgrade logic
QueueUpgradeAndTimestampSetter._runAsMultisig();
_unsafeResetHasPranked(); // reset hasPranked so we can use it again

// 2. Run queue unpause logic
TimelockController timelock = Env.timelockController();
bytes memory calldata_to_executor = _getCalldataToExecutor_queueUnpause();
console.log("calldata_to_executor");
console.logBytes(calldata_to_executor);
bytes32 txHash = timelock.hashOperation({
target: Env.executorMultisig(),
value: 0,
data: calldata_to_executor,
predecessor: QueueUpgradeAndTimestampSetter.getTimelockId(),
salt: 0
});

// Check that the upgrade does not exist in the timelock
assertFalse(
timelock.isOperationPending(txHash),
"Transaction should NOT be queued."
);

execute();

// Check that the upgrade has been added to the timelock
assertTrue(
timelock.isOperationPending(txHash),
"Transaction should be queued."
);
}

function getTimelockId() public virtual override returns (bytes32) {
TimelockController timelock = Env.timelockController();
bytes memory calldata_to_executor = _getCalldataToExecutor_queueUnpause();
return timelock.hashOperation({
target: Env.executorMultisig(),
value: 0,
data: calldata_to_executor,
predecessor: QueueUpgradeAndTimestampSetter.getTimelockId(),
salt: 0
});
}
}
Loading
Loading