Skip to content

Commit 15302b2

Browse files
committed
feat: prooftra upgrade script
1 parent 7c9e05a commit 15302b2

File tree

4 files changed

+291
-0
lines changed

4 files changed

+291
-0
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.12;
3+
4+
import {EOADeployer} from "zeus-templates/templates/EOADeployer.sol";
5+
import "../Env.sol";
6+
7+
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
8+
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
9+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
10+
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
11+
import "src/contracts/libraries/BeaconChainProofs.sol";
12+
13+
contract Deploy is EOADeployer {
14+
using Env for *;
15+
16+
function _runAsEOA() internal override {
17+
vm.startBroadcast();
18+
19+
// Deploy EigenPodBeacon Impl
20+
deployImpl({
21+
name: type(EigenPod).name,
22+
deployedTo: address(new EigenPod({
23+
_ethPOS: Env.ethPOS(),
24+
_eigenPodManager: Env.proxy.eigenPodManager(),
25+
_GENESIS_TIME: Env.EIGENPOD_GENESIS_TIME()
26+
}))
27+
});
28+
29+
vm.stopBroadcast();
30+
}
31+
32+
function testDeploy() public virtual {
33+
_runAsEOA();
34+
_validateNewImplAddresses(false);
35+
_validateImplConstructors();
36+
}
37+
38+
/// @dev Validate that the `Env.impl` addresses are updated to be distinct from what the proxy
39+
/// admin reports as the current implementation address.
40+
///
41+
/// Note: The upgrade script can call this with `areMatching == true` to check that these impl
42+
/// addresses _are_ matches.
43+
function _validateNewImplAddresses(bool areMatching) internal view {
44+
function(address, address, string memory)
45+
internal
46+
pure assertion = areMatching ? _assertMatch : _assertNotMatch;
47+
48+
assertion(
49+
Env.beacon.eigenPod().implementation(),
50+
address(Env.impl.eigenPod()),
51+
"eigenPod impl failed"
52+
);
53+
}
54+
55+
/// @dev Validate the immutables set in the new implementation constructors
56+
function _validateImplConstructors() internal view {
57+
/// pods/
58+
EigenPod eigenPod = Env.impl.eigenPod();
59+
assertTrue(eigenPod.ethPOS() == Env.ethPOS(), "ep.ethPOS invalid");
60+
assertTrue(eigenPod.eigenPodManager() == Env.proxy.eigenPodManager(), "ep.epm invalid");
61+
assertTrue(eigenPod.GENESIS_TIME() == Env.EIGENPOD_GENESIS_TIME(), "ep.genesis invalid");
62+
if (block.chainid == 5) {
63+
assertEq(BeaconChainProofs.PECTRA_FORK_TIMESTAMP, 1_739_352_768);
64+
}
65+
66+
// TODO: set this to 0 for mainnet, till the fork timestamp is provided
67+
if (block.chainid == 1) {
68+
assertEq(BeaconChainProofs.PECTRA_FORK_TIMESTAMP, 0);
69+
}
70+
}
71+
72+
/// @dev Query and return `proxyAdmin.getProxyImplementation(proxy)`
73+
function _getProxyImpl(address proxy) internal view returns (address) {
74+
return
75+
ProxyAdmin(Env.proxyAdmin()).getProxyImplementation(
76+
ITransparentUpgradeableProxy(proxy)
77+
);
78+
}
79+
80+
/// @dev Query and return `proxyAdmin.getProxyAdmin(proxy)`
81+
function _getProxyAdmin(address proxy) internal view returns (address) {
82+
return
83+
ProxyAdmin(Env.proxyAdmin()).getProxyAdmin(
84+
ITransparentUpgradeableProxy(proxy)
85+
);
86+
}
87+
88+
function _assertMatch(
89+
address a,
90+
address b,
91+
string memory err
92+
) private pure {
93+
assertEq(a, b, err);
94+
}
95+
96+
function _assertNotMatch(
97+
address a,
98+
address b,
99+
string memory err
100+
) private pure {
101+
assertNotEq(a, b, err);
102+
}
103+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.12;
3+
4+
import {Deploy} from "./1-eoa.s.sol";
5+
import "../Env.sol";
6+
7+
import {MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol";
8+
import "zeus-templates/utils/Encode.sol";
9+
10+
import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";
11+
12+
contract Queue is MultisigBuilder, Deploy {
13+
using Env for *;
14+
using Encode for *;
15+
16+
function _runAsMultisig()
17+
internal
18+
virtual
19+
override
20+
prank(Env.opsMultisig())
21+
{
22+
bytes memory calldata_to_executor = _getCalldataToExecutor();
23+
24+
TimelockController timelock = Env.timelockController();
25+
timelock.schedule({
26+
target: Env.executorMultisig(),
27+
value: 0,
28+
data: calldata_to_executor,
29+
predecessor: 0,
30+
salt: 0,
31+
delay: timelock.getMinDelay()
32+
});
33+
}
34+
35+
/// @dev Get the calldata to be sent from the timelock to the executor
36+
function _getCalldataToExecutor() internal returns (bytes memory) {
37+
MultisigCall[] storage executorCalls = Encode
38+
.newMultisigCalls()
39+
.append({
40+
to: address(Env.beacon.eigenPod()),
41+
data: Encode.upgradeableBeacon.upgradeTo({
42+
newImpl: address(Env.impl.eigenPod())
43+
})
44+
});
45+
46+
return
47+
Encode.gnosisSafe.execTransaction({
48+
from: address(Env.timelockController()),
49+
to: address(Env.multiSendCallOnly()),
50+
op: Encode.Operation.DelegateCall,
51+
data: Encode.multiSend(executorCalls)
52+
});
53+
}
54+
55+
function testScript() public virtual {
56+
runAsEOA();
57+
58+
TimelockController timelock = Env.timelockController();
59+
bytes memory calldata_to_executor = _getCalldataToExecutor();
60+
bytes32 txHash = timelock.hashOperation({
61+
target: Env.executorMultisig(),
62+
value: 0,
63+
data: calldata_to_executor,
64+
predecessor: 0,
65+
salt: 0
66+
});
67+
68+
// Check that the upgrade does not exist in the timelock
69+
assertFalse(
70+
timelock.isOperationPending(txHash),
71+
"Transaction should NOT be queued."
72+
);
73+
74+
execute();
75+
76+
// Check that the upgrade has been added to the timelock
77+
assertTrue(
78+
timelock.isOperationPending(txHash),
79+
"Transaction should be queued."
80+
);
81+
}
82+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.12;
3+
4+
import "../Env.sol";
5+
import {Queue} from "./2-multisig.s.sol";
6+
7+
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
8+
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
9+
10+
contract Execute is Queue {
11+
using Env for *;
12+
13+
function _runAsMultisig()
14+
internal
15+
override(Queue)
16+
prank(Env.protocolCouncilMultisig())
17+
{
18+
bytes memory calldata_to_executor = _getCalldataToExecutor();
19+
20+
TimelockController timelock = Env.timelockController();
21+
timelock.execute({
22+
target: Env.executorMultisig(),
23+
value: 0,
24+
payload: calldata_to_executor,
25+
predecessor: 0,
26+
salt: 0
27+
});
28+
}
29+
30+
function testScript() public virtual override(Queue) {
31+
// 0. Deploy Impls
32+
runAsEOA();
33+
34+
TimelockController timelock = Env.timelockController();
35+
bytes memory calldata_to_executor = _getCalldataToExecutor();
36+
bytes32 txHash = timelock.hashOperation({
37+
target: Env.executorMultisig(),
38+
value: 0,
39+
data: calldata_to_executor,
40+
predecessor: 0,
41+
salt: 0
42+
});
43+
assertFalse(
44+
timelock.isOperationPending(txHash),
45+
"Transaction should NOT be queued."
46+
);
47+
48+
// 1. Queue Upgrade
49+
Queue._runAsMultisig();
50+
_unsafeResetHasPranked(); // reset hasPranked so we can use it again
51+
52+
// 2. Warp past delay
53+
vm.warp(block.timestamp + timelock.getMinDelay()); // 1 tick after ETA
54+
assertEq(
55+
timelock.isOperationReady(txHash),
56+
true,
57+
"Transaction should be executable."
58+
);
59+
60+
// 3- execute
61+
execute();
62+
63+
assertTrue(
64+
timelock.isOperationDone(txHash),
65+
"Transaction should be complete."
66+
);
67+
68+
// 4. Validate
69+
_validateNewImplAddresses(true);
70+
_validateProxyConstructors();
71+
}
72+
73+
function _validateProxyConstructors() internal view {
74+
/// pods/
75+
UpgradeableBeacon eigenPodBeacon = Env.beacon.eigenPod();
76+
assertTrue(eigenPodBeacon.implementation() == address(Env.impl.eigenPod()), "eigenPodBeacon.impl invalid");
77+
78+
if (block.chainid == 5) {
79+
assertEq(BeaconChainProofs.PECTRA_FORK_TIMESTAMP, 1_739_352_768);
80+
}
81+
82+
// TODO: set this to 0 for mainnet, till the fork timestamp is provided
83+
if (block.chainid == 1) {
84+
assertEq(BeaconChainProofs.PECTRA_FORK_TIMESTAMP, 0);
85+
}
86+
}
87+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "slashing-prooftra",
3+
"from": "1.1.0",
4+
"to": "1.2.0",
5+
"phases": [
6+
{
7+
"type": "eoa",
8+
"filename": "1-eoa.s.sol"
9+
},
10+
{
11+
"type": "multisig",
12+
"filename": "2-multisig.s.sol"
13+
},
14+
{
15+
"type": "multisig",
16+
"filename": "3-execute.s.sol"
17+
}
18+
]
19+
}

0 commit comments

Comments
 (0)