Skip to content

Commit 40c24f7

Browse files
committed
feat: add step 1
1 parent 1512167 commit 40c24f7

File tree

4 files changed

+345
-0
lines changed

4 files changed

+345
-0
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
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+
12+
// Just upgrade StrategyManager
13+
contract Deploy is EOADeployer {
14+
using Env for *;
15+
16+
function _runAsEOA() internal override {
17+
vm.startBroadcast();
18+
19+
// Deploy DM
20+
deployImpl({
21+
name: type(DelegationManager).name,
22+
deployedTo: address(new DelegationManager({
23+
_strategyManager: Env.proxy.strategyManager(),
24+
_eigenPodManager: Env.proxy.eigenPodManager(),
25+
_allocationManager: Env.proxy.allocationManager(),
26+
_pauserRegistry: Env.impl.pauserRegistry(),
27+
_permissionController: Env.proxy.permissionController(),
28+
_MIN_WITHDRAWAL_DELAY: Env.MIN_WITHDRAWAL_DELAY()
29+
}))
30+
});
31+
32+
// Deploy AVSD
33+
deployImpl({
34+
name: type(AVSDirectory).name,
35+
deployedTo: address(new AVSDirectory({
36+
_delegation: Env.proxy.delegationManager(),
37+
_pauserRegistry: Env.impl.pauserRegistry()
38+
}))
39+
});
40+
41+
// Deploy SM
42+
deployImpl({
43+
name: type(StrategyManager).name,
44+
deployedTo: address(new StrategyManager({
45+
_delegation: Env.proxy.delegationManager(),
46+
_pauserRegistry: Env.impl.pauserRegistry()
47+
}))
48+
});
49+
50+
// Deploy RC
51+
deployImpl({
52+
name: type(RewardsCoordinator).name,
53+
deployedTo: address(new RewardsCoordinator({
54+
_delegationManager: Env.proxy.delegationManager(),
55+
_strategyManager: Env.proxy.strategyManager(),
56+
_allocationManager: Env.proxy.allocationManager(),
57+
_pauserRegistry: Env.impl.pauserRegistry(),
58+
_permissionController: Env.proxy.permissionController(),
59+
_CALCULATION_INTERVAL_SECONDS: Env.CALCULATION_INTERVAL_SECONDS(),
60+
_MAX_REWARDS_DURATION: Env.MAX_REWARDS_DURATION(),
61+
_MAX_RETROACTIVE_LENGTH: Env.MAX_RETROACTIVE_LENGTH(),
62+
_MAX_FUTURE_LENGTH: Env.MAX_FUTURE_LENGTH(),
63+
_GENESIS_REWARDS_TIMESTAMP: Env.GENESIS_REWARDS_TIMESTAMP()
64+
}))
65+
});
66+
67+
vm.stopBroadcast();
68+
}
69+
70+
function testDeploy() public virtual {
71+
_runAsEOA();
72+
_validateDomainSeparatorNonZero();
73+
_validateNewImplAddresses(false);
74+
_validateImplConstructors();
75+
_validateImplsInitialized();
76+
}
77+
78+
function _validateDomainSeparatorNonZero() internal view {
79+
bytes32 zeroDomainSeparator = bytes32(0);
80+
81+
assertFalse(Env.impl.avsDirectory().domainSeparator() == zeroDomainSeparator, "avsD.domainSeparator is zero");
82+
assertFalse(Env.impl.delegationManager().domainSeparator() == zeroDomainSeparator, "dm.domainSeparator is zero");
83+
assertFalse(Env.impl.strategyManager().domainSeparator() == zeroDomainSeparator, "rc.domainSeparator is zero");
84+
}
85+
86+
87+
/// @dev Validate that the `Env.impl` addresses are updated to be distinct from what the proxy
88+
/// admin reports as the current implementation address.
89+
///
90+
/// Note: The upgrade script can call this with `areMatching == true` to check that these impl
91+
/// addresses _are_ matches.
92+
function _validateNewImplAddresses(bool areMatching) internal view {
93+
function (address, address, string memory) internal pure assertion =
94+
areMatching ? _assertMatch : _assertNotMatch;
95+
96+
97+
assertion(
98+
_getProxyImpl(address(Env.proxy.strategyManager())),
99+
address(Env.impl.strategyManager()),
100+
"strategyManager impl failed"
101+
);
102+
103+
assertion(
104+
_getProxyImpl(address(Env.proxy.delegationManager())),
105+
address(Env.impl.delegationManager()),
106+
"delegationManager impl failed"
107+
);
108+
109+
assertion(
110+
_getProxyImpl(address(Env.proxy.avsDirectory())),
111+
address(Env.impl.avsDirectory()),
112+
"avsdirectory impl failed"
113+
);
114+
115+
assertion(
116+
_getProxyImpl(address(Env.proxy.rewardsCoordinator())),
117+
address(Env.impl.rewardsCoordinator()),
118+
"rewardsCoordinator impl failed"
119+
);
120+
}
121+
122+
/// @dev Validate the immutables set in the new implementation constructors
123+
function _validateImplConstructors() internal view {
124+
AVSDirectory avsDirectory = Env.impl.avsDirectory();
125+
assertTrue(avsDirectory.delegation() == Env.proxy.delegationManager(), "avsD.dm invalid");
126+
assertTrue(avsDirectory.pauserRegistry() == Env.impl.pauserRegistry(), "avsD.pR invalid");
127+
128+
DelegationManager delegation = Env.impl.delegationManager();
129+
assertTrue(delegation.strategyManager() == Env.proxy.strategyManager(), "dm.sm invalid");
130+
assertTrue(delegation.eigenPodManager() == Env.proxy.eigenPodManager(), "dm.epm invalid");
131+
assertTrue(delegation.allocationManager() == Env.proxy.allocationManager(), "dm.alm invalid");
132+
assertTrue(delegation.pauserRegistry() == Env.impl.pauserRegistry(), "dm.pR invalid");
133+
assertTrue(delegation.permissionController() == Env.proxy.permissionController(), "dm.pc invalid");
134+
assertTrue(delegation.minWithdrawalDelayBlocks() == Env.MIN_WITHDRAWAL_DELAY(), "dm.withdrawalDelay invalid");
135+
136+
RewardsCoordinator rewards = Env.impl.rewardsCoordinator();
137+
assertTrue(rewards.delegationManager() == Env.proxy.delegationManager(), "rc.dm invalid");
138+
assertTrue(rewards.strategyManager() == Env.proxy.strategyManager(), "rc.sm invalid");
139+
assertTrue(rewards.allocationManager() == Env.proxy.allocationManager(), "rc.alm invalid");
140+
assertTrue(rewards.pauserRegistry() == Env.impl.pauserRegistry(), "rc.pR invalid");
141+
assertTrue(rewards.permissionController() == Env.proxy.permissionController(), "rc.pc invalid");
142+
assertTrue(rewards.CALCULATION_INTERVAL_SECONDS() == Env.CALCULATION_INTERVAL_SECONDS(), "rc.calcInterval invalid");
143+
assertTrue(rewards.MAX_REWARDS_DURATION() == Env.MAX_REWARDS_DURATION(), "rc.rewardsDuration invalid");
144+
assertTrue(rewards.MAX_RETROACTIVE_LENGTH() == Env.MAX_RETROACTIVE_LENGTH(), "rc.retroLength invalid");
145+
assertTrue(rewards.MAX_FUTURE_LENGTH() == Env.MAX_FUTURE_LENGTH(), "rc.futureLength invalid");
146+
assertTrue(rewards.GENESIS_REWARDS_TIMESTAMP() == Env.GENESIS_REWARDS_TIMESTAMP(), "rc.genesis invalid");
147+
148+
StrategyManager strategyManager = Env.impl.strategyManager();
149+
assertTrue(strategyManager.delegation() == Env.proxy.delegationManager(), "sm.dm invalid");
150+
assertTrue(strategyManager.pauserRegistry() == Env.impl.pauserRegistry(), "sm.pR invalid");
151+
}
152+
153+
/// @dev Call initialize on all deployed implementations to ensure initializers are disabled
154+
function _validateImplsInitialized() internal {
155+
bytes memory errInit = "Initializable: contract is already initialized";
156+
157+
AVSDirectory avsDirectory = Env.impl.avsDirectory();
158+
vm.expectRevert(errInit);
159+
avsDirectory.initialize(address(0), 0);
160+
161+
DelegationManager delegation = Env.impl.delegationManager();
162+
vm.expectRevert(errInit);
163+
delegation.initialize(address(0), 0);
164+
165+
RewardsCoordinator rewards = Env.impl.rewardsCoordinator();
166+
vm.expectRevert(errInit);
167+
rewards.initialize(address(0), 0, address(0), 0, 0);
168+
169+
StrategyManager strategyManager = Env.impl.strategyManager();
170+
vm.expectRevert(errInit);
171+
strategyManager.initialize(address(0), address(0), 0);
172+
}
173+
174+
/// @dev Query and return `proxyAdmin.getProxyImplementation(proxy)`
175+
function _getProxyImpl(address proxy) internal view returns (address) {
176+
return ProxyAdmin(Env.proxyAdmin()).getProxyImplementation(ITransparentUpgradeableProxy(proxy));
177+
}
178+
179+
/// @dev Query and return `proxyAdmin.getProxyAdmin(proxy)`
180+
function _getProxyAdmin(address proxy) internal view returns (address) {
181+
return ProxyAdmin(Env.proxyAdmin()).getProxyAdmin(ITransparentUpgradeableProxy(proxy));
182+
}
183+
184+
function _assertMatch(address a, address b, string memory err) private pure {
185+
assertEq(a, b, err);
186+
}
187+
188+
function _assertNotMatch(address a, address b, string memory err) private pure {
189+
assertNotEq(a, b, err);
190+
}
191+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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() prank(Env.opsMultisig()) internal virtual override {
17+
bytes memory calldata_to_executor = _getCalldataToExecutor();
18+
19+
TimelockController timelock = Env.timelockController();
20+
timelock.schedule({
21+
target: Env.executorMultisig(),
22+
value: 0,
23+
data: calldata_to_executor,
24+
predecessor: 0,
25+
salt: 0,
26+
delay: timelock.getMinDelay()
27+
});
28+
}
29+
30+
/// @dev Get the calldata to be sent from the timelock to the executor
31+
function _getCalldataToExecutor() internal returns (bytes memory) {
32+
MultisigCall[] storage executorCalls = Encode.newMultisigCalls()
33+
/// core/
34+
.append({
35+
to: Env.proxyAdmin(),
36+
data: Encode.proxyAdmin.upgrade({
37+
proxy: address(Env.proxy.strategyManager()),
38+
impl: address(Env.impl.strategyManager())
39+
})
40+
});
41+
42+
return Encode.gnosisSafe.execTransaction({
43+
from: address(Env.timelockController()),
44+
to: address(Env.multiSendCallOnly()),
45+
op: Encode.Operation.DelegateCall,
46+
data: Encode.multiSend(executorCalls)
47+
});
48+
}
49+
50+
function testScript() public virtual {
51+
runAsEOA();
52+
53+
TimelockController timelock = Env.timelockController();
54+
bytes memory calldata_to_executor = _getCalldataToExecutor();
55+
bytes32 txHash = timelock.hashOperation({
56+
target: Env.executorMultisig(),
57+
value: 0,
58+
data: calldata_to_executor,
59+
predecessor: 0,
60+
salt: 0
61+
});
62+
63+
// Check that the upgrade does not exist in the timelock
64+
assertFalse(timelock.isOperationPending(txHash), "Transaction should NOT be queued.");
65+
66+
execute();
67+
68+
// Check that the upgrade has been added to the timelock
69+
assertTrue(timelock.isOperationPending(txHash), "Transaction should be queued.");
70+
}
71+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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() prank(Env.protocolCouncilMultisig()) internal override(Queue) {
14+
bytes memory calldata_to_executor = _getCalldataToExecutor();
15+
16+
TimelockController timelock = Env.timelockController();
17+
timelock.execute({
18+
target: Env.executorMultisig(),
19+
value: 0,
20+
payload: calldata_to_executor,
21+
predecessor: 0,
22+
salt: 0
23+
});
24+
}
25+
26+
function testScript() public virtual override(Queue){
27+
// 0. Deploy Impls
28+
runAsEOA();
29+
30+
TimelockController timelock = Env.timelockController();
31+
bytes memory calldata_to_executor = _getCalldataToExecutor();
32+
bytes32 txHash = timelock.hashOperation({
33+
target: Env.executorMultisig(),
34+
value: 0,
35+
data: calldata_to_executor,
36+
predecessor: 0,
37+
salt: 0
38+
});
39+
assertFalse(timelock.isOperationPending(txHash), "Transaction should NOT be queued.");
40+
41+
// 1. Queue Upgrade
42+
Queue._runAsMultisig();
43+
_unsafeResetHasPranked(); // reset hasPranked so we can use it again
44+
45+
// 2. Warp past delay
46+
vm.warp(block.timestamp + timelock.getMinDelay()); // 1 tick after ETA
47+
assertEq(timelock.isOperationReady(txHash), true, "Transaction should be executable.");
48+
49+
// 3- execute
50+
execute();
51+
52+
assertTrue(timelock.isOperationDone(txHash), "Transaction should be complete.");
53+
54+
// 4. Validate
55+
_validateNewImplAddresses(true);
56+
_validateProxyConstructors();
57+
}
58+
59+
function _validateProxyConstructors() internal view {
60+
StrategyManager strategyManager = Env.proxy.strategyManager();
61+
assertTrue(strategyManager.delegation() == Env.proxy.delegationManager(), "sm.dm invalid");
62+
assertTrue(strategyManager.pauserRegistry() == Env.impl.pauserRegistry(), "sm.pR invalid");
63+
}
64+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "slashing-patch",
3+
"from": "1.0.2",
4+
"to": "1.0.3",
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)