Skip to content

Commit 44ece6d

Browse files
feat: slashing 1.0.3 upgrade script (#995)
* feat: add step 1 * feat: step 1 & 2 complete; pending step 3 sanity * test: add `_validateProxyDomainSeparators` * feat: add rc validation --------- Co-authored-by: clandestine.eth <[email protected]>
1 parent 1512167 commit 44ece6d

File tree

4 files changed

+490
-0
lines changed

4 files changed

+490
-0
lines changed
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
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+
_validateRCValues();
77+
}
78+
79+
function _validateDomainSeparatorNonZero() internal view {
80+
bytes32 zeroDomainSeparator = bytes32(0);
81+
82+
assertFalse(Env.impl.avsDirectory().domainSeparator() == zeroDomainSeparator, "avsD.domainSeparator is zero");
83+
assertFalse(Env.impl.delegationManager().domainSeparator() == zeroDomainSeparator, "dm.domainSeparator is zero");
84+
assertFalse(Env.impl.strategyManager().domainSeparator() == zeroDomainSeparator, "rc.domainSeparator is zero");
85+
}
86+
87+
88+
/// @dev Validate that the `Env.impl` addresses are updated to be distinct from what the proxy
89+
/// admin reports as the current implementation address.
90+
///
91+
/// Note: The upgrade script can call this with `areMatching == true` to check that these impl
92+
/// addresses _are_ matches.
93+
function _validateNewImplAddresses(bool areMatching) internal view {
94+
function (address, address, string memory) internal pure assertion =
95+
areMatching ? _assertMatch : _assertNotMatch;
96+
97+
98+
assertion(
99+
_getProxyImpl(address(Env.proxy.strategyManager())),
100+
address(Env.impl.strategyManager()),
101+
"strategyManager impl failed"
102+
);
103+
104+
assertion(
105+
_getProxyImpl(address(Env.proxy.delegationManager())),
106+
address(Env.impl.delegationManager()),
107+
"delegationManager impl failed"
108+
);
109+
110+
assertion(
111+
_getProxyImpl(address(Env.proxy.avsDirectory())),
112+
address(Env.impl.avsDirectory()),
113+
"avsdirectory impl failed"
114+
);
115+
116+
assertion(
117+
_getProxyImpl(address(Env.proxy.rewardsCoordinator())),
118+
address(Env.impl.rewardsCoordinator()),
119+
"rewardsCoordinator impl failed"
120+
);
121+
}
122+
123+
/// @dev Validate the immutables set in the new implementation constructors
124+
function _validateImplConstructors() internal view {
125+
AVSDirectory avsDirectory = Env.impl.avsDirectory();
126+
assertTrue(avsDirectory.delegation() == Env.proxy.delegationManager(), "avsD.dm invalid");
127+
assertTrue(avsDirectory.pauserRegistry() == Env.impl.pauserRegistry(), "avsD.pR invalid");
128+
129+
DelegationManager delegation = Env.impl.delegationManager();
130+
assertTrue(delegation.strategyManager() == Env.proxy.strategyManager(), "dm.sm invalid");
131+
assertTrue(delegation.eigenPodManager() == Env.proxy.eigenPodManager(), "dm.epm invalid");
132+
assertTrue(delegation.allocationManager() == Env.proxy.allocationManager(), "dm.alm invalid");
133+
assertTrue(delegation.pauserRegistry() == Env.impl.pauserRegistry(), "dm.pR invalid");
134+
assertTrue(delegation.permissionController() == Env.proxy.permissionController(), "dm.pc invalid");
135+
assertTrue(delegation.minWithdrawalDelayBlocks() == Env.MIN_WITHDRAWAL_DELAY(), "dm.withdrawalDelay invalid");
136+
137+
RewardsCoordinator rewards = Env.impl.rewardsCoordinator();
138+
assertTrue(rewards.delegationManager() == Env.proxy.delegationManager(), "rc.dm invalid");
139+
assertTrue(rewards.strategyManager() == Env.proxy.strategyManager(), "rc.sm invalid");
140+
assertTrue(rewards.allocationManager() == Env.proxy.allocationManager(), "rc.alm invalid");
141+
assertTrue(rewards.pauserRegistry() == Env.impl.pauserRegistry(), "rc.pR invalid");
142+
assertTrue(rewards.permissionController() == Env.proxy.permissionController(), "rc.pc invalid");
143+
assertTrue(rewards.CALCULATION_INTERVAL_SECONDS() == Env.CALCULATION_INTERVAL_SECONDS(), "rc.calcInterval invalid");
144+
assertTrue(rewards.MAX_REWARDS_DURATION() == Env.MAX_REWARDS_DURATION(), "rc.rewardsDuration invalid");
145+
assertTrue(rewards.MAX_RETROACTIVE_LENGTH() == Env.MAX_RETROACTIVE_LENGTH(), "rc.retroLength invalid");
146+
assertTrue(rewards.MAX_FUTURE_LENGTH() == Env.MAX_FUTURE_LENGTH(), "rc.futureLength invalid");
147+
assertTrue(rewards.GENESIS_REWARDS_TIMESTAMP() == Env.GENESIS_REWARDS_TIMESTAMP(), "rc.genesis invalid");
148+
149+
StrategyManager strategyManager = Env.impl.strategyManager();
150+
assertTrue(strategyManager.delegation() == Env.proxy.delegationManager(), "sm.dm invalid");
151+
assertTrue(strategyManager.pauserRegistry() == Env.impl.pauserRegistry(), "sm.pR invalid");
152+
}
153+
154+
/// @dev Call initialize on all deployed implementations to ensure initializers are disabled
155+
function _validateImplsInitialized() internal {
156+
bytes memory errInit = "Initializable: contract is already initialized";
157+
158+
AVSDirectory avsDirectory = Env.impl.avsDirectory();
159+
vm.expectRevert(errInit);
160+
avsDirectory.initialize(address(0), 0);
161+
162+
DelegationManager delegation = Env.impl.delegationManager();
163+
vm.expectRevert(errInit);
164+
delegation.initialize(address(0), 0);
165+
166+
RewardsCoordinator rewards = Env.impl.rewardsCoordinator();
167+
vm.expectRevert(errInit);
168+
rewards.initialize(address(0), 0, address(0), 0, 0);
169+
170+
StrategyManager strategyManager = Env.impl.strategyManager();
171+
vm.expectRevert(errInit);
172+
strategyManager.initialize(address(0), address(0), 0);
173+
}
174+
175+
function _validateRCValues() internal view {
176+
177+
RewardsCoordinator rewardsCoordinatorImpl = Env.impl.rewardsCoordinator();
178+
assertEq(
179+
rewardsCoordinatorImpl.CALCULATION_INTERVAL_SECONDS(),
180+
Env.CALCULATION_INTERVAL_SECONDS(),
181+
"expected REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS"
182+
);
183+
assertEq(
184+
rewardsCoordinatorImpl.CALCULATION_INTERVAL_SECONDS(),
185+
1 days,
186+
"expected REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS"
187+
);
188+
assertGt(
189+
rewardsCoordinatorImpl.CALCULATION_INTERVAL_SECONDS(),
190+
0,
191+
"expected non-zero REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS"
192+
);
193+
194+
assertEq(rewardsCoordinatorImpl.MAX_REWARDS_DURATION(), Env.MAX_REWARDS_DURATION());
195+
assertGt(rewardsCoordinatorImpl.MAX_REWARDS_DURATION(), 0);
196+
197+
assertEq(
198+
rewardsCoordinatorImpl.MAX_RETROACTIVE_LENGTH(),
199+
Env.MAX_RETROACTIVE_LENGTH()
200+
);
201+
assertGt(rewardsCoordinatorImpl.MAX_RETROACTIVE_LENGTH(), 0);
202+
203+
assertEq(rewardsCoordinatorImpl.MAX_FUTURE_LENGTH(), Env.MAX_FUTURE_LENGTH());
204+
assertGt(rewardsCoordinatorImpl.MAX_FUTURE_LENGTH(), 0);
205+
206+
assertEq(
207+
rewardsCoordinatorImpl.GENESIS_REWARDS_TIMESTAMP(),
208+
Env.GENESIS_REWARDS_TIMESTAMP()
209+
);
210+
assertGt(rewardsCoordinatorImpl.GENESIS_REWARDS_TIMESTAMP(), 0);
211+
}
212+
213+
/// @dev Query and return `proxyAdmin.getProxyImplementation(proxy)`
214+
function _getProxyImpl(address proxy) internal view returns (address) {
215+
return ProxyAdmin(Env.proxyAdmin()).getProxyImplementation(ITransparentUpgradeableProxy(proxy));
216+
}
217+
218+
/// @dev Query and return `proxyAdmin.getProxyAdmin(proxy)`
219+
function _getProxyAdmin(address proxy) internal view returns (address) {
220+
return ProxyAdmin(Env.proxyAdmin()).getProxyAdmin(ITransparentUpgradeableProxy(proxy));
221+
}
222+
223+
function _assertMatch(address a, address b, string memory err) private pure {
224+
assertEq(a, b, err);
225+
}
226+
227+
function _assertNotMatch(address a, address b, string memory err) private pure {
228+
assertNotEq(a, b, err);
229+
}
230+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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+
.append({
42+
to: Env.proxyAdmin(),
43+
data: Encode.proxyAdmin.upgrade({
44+
proxy: address(Env.proxy.avsDirectory()),
45+
impl: address(Env.impl.avsDirectory())
46+
})
47+
})
48+
.append({
49+
to: Env.proxyAdmin(),
50+
data: Encode.proxyAdmin.upgrade({
51+
proxy: address(Env.proxy.delegationManager()),
52+
impl: address(Env.impl.delegationManager())
53+
})
54+
})
55+
.append({
56+
to: Env.proxyAdmin(),
57+
data: Encode.proxyAdmin.upgrade({
58+
proxy: address(Env.proxy.rewardsCoordinator()),
59+
impl: address(Env.impl.rewardsCoordinator())
60+
})
61+
});
62+
63+
64+
return Encode.gnosisSafe.execTransaction({
65+
from: address(Env.timelockController()),
66+
to: address(Env.multiSendCallOnly()),
67+
op: Encode.Operation.DelegateCall,
68+
data: Encode.multiSend(executorCalls)
69+
});
70+
}
71+
72+
function testScript() public virtual {
73+
runAsEOA();
74+
75+
TimelockController timelock = Env.timelockController();
76+
bytes memory calldata_to_executor = _getCalldataToExecutor();
77+
bytes32 txHash = timelock.hashOperation({
78+
target: Env.executorMultisig(),
79+
value: 0,
80+
data: calldata_to_executor,
81+
predecessor: 0,
82+
salt: 0
83+
});
84+
85+
// Check that the upgrade does not exist in the timelock
86+
assertFalse(timelock.isOperationPending(txHash), "Transaction should NOT be queued.");
87+
88+
execute();
89+
90+
// Check that the upgrade has been added to the timelock
91+
assertTrue(timelock.isOperationPending(txHash), "Transaction should be queued.");
92+
}
93+
}

0 commit comments

Comments
 (0)