Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/bindings/AVSDirectory/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/AllocationManager/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/BeaconChainProofs/binding.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/bindings/DelegationManager/binding.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pkg/bindings/EigenPod/binding.go

Large diffs are not rendered by default.

376 changes: 374 additions & 2 deletions pkg/bindings/EigenPodManager/binding.go

Large diffs are not rendered by default.

374 changes: 373 additions & 1 deletion pkg/bindings/EigenPodManagerStorage/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/EigenPodStorage/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/EigenStrategy/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/IEigenPod/binding.go

Large diffs are not rendered by default.

343 changes: 342 additions & 1 deletion pkg/bindings/IEigenPodManager/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/IRewardsCoordinator/binding.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pkg/bindings/RewardsCoordinator/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/RewardsCoordinatorStorage/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/StrategyBase/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/StrategyBaseTVLLimits/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/StrategyFactory/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/StrategyManager/binding.go

Large diffs are not rendered by default.

137 changes: 137 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,137 @@
// 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(),
_version: "v1.2.0"
})
)
});

// 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(),
_version: "v1.2.0"
})
)
});

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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// 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
});
}
}
90 changes: 90 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,90 @@
// 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
});
}
}
54 changes: 54 additions & 0 deletions script/releases/v1.2.0-slashing/4-pause.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import "../Env.sol";

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

/**
* Purpose: Enqueue a transaction which immediately sets
* `EigenPodManager.PAUSED_START_CHECKPOINT=true` and
* `EigenPodManager.PAUSED_EIGENPODS_VERIFY_CREDENTIALS=true`
*/
contract Pause is MultisigBuilder, EigenPodPausingConstants {
using Env for *;

function _runAsMultisig() internal virtual override prank(Env.pauserMultisig()) {
uint256 mask = 2 ** PAUSED_START_CHECKPOINT | 2 ** PAUSED_EIGENPODS_VERIFY_CREDENTIALS
| 2 ** PAUSED_VERIFY_STALE_BALANCE | 2 ** PAUSED_EIGENPODS_VERIFY_CHECKPOINT_PROOFS;

Env.proxy.eigenPodManager().pause(mask);
}

function testScript() public virtual {
execute();

assertTrue(Env.proxy.eigenPodManager().paused(PAUSED_START_CHECKPOINT), "Not paused!");
assertTrue(Env.proxy.eigenPodManager().paused(PAUSED_EIGENPODS_VERIFY_CREDENTIALS), "Not paused!");
assertTrue(Env.proxy.eigenPodManager().paused(PAUSED_VERIFY_STALE_BALANCE), "Not paused!");
assertTrue(Env.proxy.eigenPodManager().paused(PAUSED_EIGENPODS_VERIFY_CHECKPOINT_PROOFS), "Not paused!");

// Create a new pod and try to verify credentials + start checkpoint
EigenPod pod = EigenPod(payable(Env.proxy.eigenPodManager().createPod()));

// Revert verifying withdrawal credentials
vm.expectRevert(IEigenPodErrors.CurrentlyPaused.selector);
BeaconChainProofs.StateRootProof memory emptyProof;
pod.verifyWithdrawalCredentials(0, emptyProof, new uint40[](0), new bytes[](0), new bytes32[][](0));

// Revert starting checkpoint
vm.expectRevert(IEigenPodErrors.CurrentlyPaused.selector);
pod.startCheckpoint(false);

// Revert verifying stale balance
BeaconChainProofs.ValidatorProof memory validatorProof;
vm.expectRevert(IEigenPodErrors.CurrentlyPaused.selector);
pod.verifyStaleBalance(0, emptyProof, validatorProof);

// Revert completing checkpoint
BeaconChainProofs.BalanceContainerProof memory balanceContainerProof;
BeaconChainProofs.BalanceProof[] memory proofs;
vm.expectRevert(IEigenPodErrors.CurrentlyPaused.selector);
pod.verifyCheckpointProofs(balanceContainerProof, proofs);
}
}
Loading
Loading