Skip to content

Commit fa50152

Browse files
authored
Merge pull request #1499 from lidofinance/feat/deploy-v3-hoodi
feat: deploy params V3 hoodi
2 parents 5de69c2 + 57dbe5e commit fa50152

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1157
-660
lines changed

.github/workflows/tests-integration-mainnet.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
GAS_PRIORITY_FEE: 1
4242
GAS_MAX_FEE: 100
4343
NETWORK_STATE_FILE: deployed-mainnet-upgrade.json
44-
GENESIS_TIME: 1606824023 # needed only for TW upgrade
44+
UPGRADE_PARAMETERS_FILE: upgrade-parameters-mainnet.toml
4545

4646
- name: Mock Aragon voting
4747
run: yarn upgrade:mock-voting

.github/workflows/tests-integration-upgrade-template.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,3 @@ jobs:
2626
env:
2727
RPC_URL: "${{ secrets.ETH_RPC_URL }}"
2828
UPGRADE_PARAMETERS_FILE: upgrade-parameters-mainnet.json
29-
GENESIS_TIME: 1606824023 # needed only for TW upgrade

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ Please refer to the [Lido Contributor Code of Conduct](/CODE_OF_CONDUCT.md).
6565

6666
## License
6767

68-
2024 Lido <info@lido.fi>
68+
2025 Lido <info@lido.fi>
6969

7070
This program is free software: you can redistribute it and/or modify
7171
it under the terms of the GNU General Public License as published by

contracts/0.8.25/utils/V3TemporaryAdmin.sol

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ interface ICSModule {
5454
function accounting() external view returns (address);
5555
}
5656

57+
interface IVaultsAdapter {
58+
function evmScriptExecutor() external view returns (address);
59+
}
60+
5761
interface ILidoLocator {
5862
function vaultHub() external view returns (address);
5963
function predepositGuarantee() external view returns (address);
@@ -75,15 +79,14 @@ contract V3TemporaryAdmin {
7579
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
7680

7781
address public immutable AGENT;
78-
address public immutable GATE_SEAL;
82+
bool public immutable IS_HOODI;
7983

8084
bool public isSetupComplete;
8185

82-
constructor(address _agent, address _gateSeal) {
86+
constructor(address _agent, bool _isHoodi) {
8387
if (_agent == address(0)) revert ZeroAddress();
84-
if (_gateSeal == address(0)) revert ZeroAddress();
8588
AGENT = _agent;
86-
GATE_SEAL = _gateSeal;
89+
IS_HOODI = _isHoodi;
8790
}
8891

8992
/**
@@ -96,10 +99,10 @@ contract V3TemporaryAdmin {
9699

97100
IStakingRouter.StakingModule[] memory stakingModules = IStakingRouter(_stakingRouter).getStakingModules();
98101

99-
// Find the Community Staking module (index 2)
102+
// Find the Community Staking module (index 2 or 3 on Hoodi)
100103
if (stakingModules.length <= 2) revert CsmModuleNotFound();
101104

102-
IStakingRouter.StakingModule memory csm = stakingModules[2];
105+
IStakingRouter.StakingModule memory csm = stakingModules[IS_HOODI ? 3 : 2];
103106
if (keccak256(bytes(csm.name)) != keccak256(bytes("Community Staking"))) {
104107
revert CsmModuleNotFound();
105108
}
@@ -111,13 +114,11 @@ contract V3TemporaryAdmin {
111114
* @notice Complete setup for all contracts - grants all roles and transfers admin to agent
112115
* @dev This is the main external function that should be called after deployment
113116
* @param _lidoLocatorImpl The new LidoLocator implementation address
114-
* @param _evmScriptExecutor The EVM script executor address from easyTrack
115117
* @param _vaultsAdapter The vaults' adapter address from easyTrack
116118
*/
117-
function completeSetup(address _lidoLocatorImpl, address _evmScriptExecutor, address _vaultsAdapter) external {
119+
function completeSetup(address _lidoLocatorImpl, address _vaultsAdapter, address _gateSeal) external {
118120
if (isSetupComplete) revert SetupAlreadyCompleted();
119121
if (_lidoLocatorImpl == address(0)) revert ZeroLidoLocator();
120-
if (_evmScriptExecutor == address(0)) revert ZeroEvmScriptExecutor();
121122
if (_vaultsAdapter == address(0)) revert ZeroVaultsAdapter();
122123

123124
isSetupComplete = true;
@@ -126,11 +127,11 @@ contract V3TemporaryAdmin {
126127

127128
address csmAccounting = getCsmAccountingAddress(locator.stakingRouter());
128129

129-
_setupPredepositGuarantee(locator.predepositGuarantee());
130+
_setupPredepositGuarantee(locator.predepositGuarantee(), _gateSeal);
130131
_setupLazyOracle(locator.lazyOracle());
131-
_setupOperatorGrid(locator.operatorGrid(), _evmScriptExecutor, _vaultsAdapter);
132+
_setupOperatorGrid(locator.operatorGrid(), IVaultsAdapter(_vaultsAdapter).evmScriptExecutor(), _vaultsAdapter);
132133
_setupBurner(locator.burner(), locator.accounting(), csmAccounting);
133-
_setupVaultHub(locator.vaultHub(), _vaultsAdapter);
134+
_setupVaultHub(locator.vaultHub(), _vaultsAdapter, _gateSeal);
134135
}
135136

136137

@@ -139,15 +140,15 @@ contract V3TemporaryAdmin {
139140
* @param _vaultHub The VaultHub contract address
140141
* @param _vaultsAdapter The vaults' adapter address
141142
*/
142-
function _setupVaultHub(address _vaultHub, address _vaultsAdapter) private {
143+
function _setupVaultHub(address _vaultHub, address _vaultsAdapter, address _gateSeal) private {
143144
// Get roles from the contract
144145
bytes32 pauseRole = IPausableUntil(_vaultHub).PAUSE_ROLE();
145146
bytes32 vaultMasterRole = IVaultHub(_vaultHub).VAULT_MASTER_ROLE();
146147
bytes32 redemptionMasterRole = IVaultHub(_vaultHub).REDEMPTION_MASTER_ROLE();
147148
bytes32 validatorExitRole = IVaultHub(_vaultHub).VALIDATOR_EXIT_ROLE();
148149
bytes32 badDebtMasterRole = IVaultHub(_vaultHub).BAD_DEBT_MASTER_ROLE();
149150

150-
IAccessControl(_vaultHub).grantRole(pauseRole, GATE_SEAL);
151+
IAccessControl(_vaultHub).grantRole(pauseRole, _gateSeal);
151152

152153
IAccessControl(_vaultHub).grantRole(vaultMasterRole, AGENT);
153154
IAccessControl(_vaultHub).grantRole(redemptionMasterRole, AGENT);
@@ -163,9 +164,9 @@ contract V3TemporaryAdmin {
163164
* @notice Setup PredepositGuarantee with PAUSE_ROLE for gateSeal and transfer admin to agent
164165
* @param _predepositGuarantee The PredepositGuarantee contract address
165166
*/
166-
function _setupPredepositGuarantee(address _predepositGuarantee) private {
167+
function _setupPredepositGuarantee(address _predepositGuarantee, address _gateSeal) private {
167168
bytes32 pauseRole = IPausableUntil(_predepositGuarantee).PAUSE_ROLE();
168-
IAccessControl(_predepositGuarantee).grantRole(pauseRole, GATE_SEAL);
169+
IAccessControl(_predepositGuarantee).grantRole(pauseRole, _gateSeal);
169170
_transferAdminToAgent(_predepositGuarantee);
170171
}
171172

@@ -196,6 +197,7 @@ contract V3TemporaryAdmin {
196197
/**
197198
* @notice Setup Burner with required roles and transfer admin to agent
198199
* @param _burner The Burner contract address
200+
* @param _accounting The Accounting contract address
199201
* @param _csmAccounting The CSM Accounting contract address
200202
*/
201203
function _setupBurner(
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
2+
// SPDX-License-Identifier: GPL-3.0
3+
4+
// See contracts/COMPILERS.md
5+
// solhint-disable-next-line
6+
pragma solidity >=0.4.24 <0.9.0;
7+
8+
// https://github.com/lidofinance/gate-seals/blob/main/contracts/GateSeal.vy
9+
interface IGateSeal {
10+
function seal(address[] memory _sealables) external;
11+
function is_expired() external view returns (bool);
12+
function get_sealing_committee() external view returns (address);
13+
}

contracts/upgrade/V3Addresses.sol

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ pragma solidity 0.8.25;
44
import {IAccessControlEnumerable} from "@openzeppelin/contracts-v4.4/access/AccessControlEnumerable.sol";
55
import {ILidoLocator} from "contracts/common/interfaces/ILidoLocator.sol";
66

7+
interface IVaultsAdapter {
8+
function evmScriptExecutor() external view returns (address);
9+
}
10+
711
interface IStakingRouter is IAccessControlEnumerable {
812
struct StakingModule {
913
uint24 id;
@@ -53,7 +57,6 @@ contract V3Addresses {
5357
address gateSealForVaults;
5458

5559
// EasyTrack addresses
56-
address evmScriptExecutor;
5760
address vaultsAdapter;
5861

5962
// Existing proxies and contracts
@@ -107,8 +110,8 @@ contract V3Addresses {
107110
//
108111
// -------- EasyTrack addresses --------
109112
//
110-
address public immutable EVM_SCRIPT_EXECUTOR;
111113
address public immutable VAULTS_ADAPTER;
114+
address public immutable EVM_SCRIPT_EXECUTOR;
112115

113116
//
114117
// -------- Unchanged contracts --------
@@ -127,6 +130,7 @@ contract V3Addresses {
127130
address public immutable NODE_OPERATORS_REGISTRY;
128131
address public immutable SIMPLE_DVT;
129132
address public immutable CSM_ACCOUNTING;
133+
address public immutable HOODI_SANDBOX_MODULE;
130134

131135
constructor(
132136
V3AddressesParams memory params
@@ -157,7 +161,7 @@ contract V3Addresses {
157161
STAKING_VAULT_IMPL = params.stakingVaultImpl;
158162
DASHBOARD_IMPL = params.dashboardImpl;
159163
GATE_SEAL = params.gateSealForVaults;
160-
EVM_SCRIPT_EXECUTOR = params.evmScriptExecutor;
164+
EVM_SCRIPT_EXECUTOR = IVaultsAdapter(params.vaultsAdapter).evmScriptExecutor();
161165
VAULTS_ADAPTER = params.vaultsAdapter;
162166

163167
//
@@ -193,9 +197,18 @@ contract V3Addresses {
193197
IStakingRouter.StakingModule memory simpleDvt = stakingModules[1];
194198
if (_hash(simpleDvt.name) != _hash(SIMPLE_DVT_MODULE_NAME)) revert IncorrectStakingModuleName(simpleDvt.name);
195199
SIMPLE_DVT = simpleDvt.stakingModuleAddress;
196-
IStakingRouter.StakingModule memory csm = stakingModules[2];
200+
201+
// NB: there is additional module on Hoodi before CSM
202+
uint256 csmIndex = stakingModules.length - 1;
203+
IStakingRouter.StakingModule memory csm = stakingModules[csmIndex];
197204
if (_hash(csm.name) != _hash(CSM_MODULE_NAME)) revert IncorrectStakingModuleName(csm.name);
198205
CSM_ACCOUNTING = ICSModule(csm.stakingModuleAddress).accounting();
206+
207+
if (stakingModules.length == 4) {
208+
IStakingRouter.StakingModule memory hoodiSandbox = stakingModules[2];
209+
if (_hash(hoodiSandbox.name) != _hash("Sandbox")) revert IncorrectStakingModuleName(hoodiSandbox.name);
210+
HOODI_SANDBOX_MODULE = hoodiSandbox.stakingModuleAddress;
211+
}
199212
}
200213
}
201214

contracts/upgrade/V3Template.sol

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ contract V3Template is V3Addresses {
129129
contractsWithBurnerAllowances.push(CSM_ACCOUNTING);
130130
}
131131

132+
function isHoodi() internal view returns (bool) {
133+
return HOODI_SANDBOX_MODULE != address(0);
134+
}
135+
132136
/// @notice Must be called before LidoLocator is upgraded
133137
function startUpgrade() external {
134138
if (msg.sender != AGENT) revert OnlyAgentCanUpgrade();
@@ -224,7 +228,11 @@ contract V3Template is V3Addresses {
224228
function _assertFinalACL() internal view {
225229
// Burner
226230
bytes32 requestBurnSharesRole = IBurner(BURNER).REQUEST_BURN_SHARES_ROLE();
227-
_assertZeroOZRoleHolders(OLD_BURNER, requestBurnSharesRole);
231+
if (isHoodi()) {
232+
_assertSingleOZRoleHolder(OLD_BURNER, requestBurnSharesRole, HOODI_SANDBOX_MODULE);
233+
} else {
234+
_assertZeroOZRoleHolders(OLD_BURNER, requestBurnSharesRole);
235+
}
228236

229237
_assertProxyAdmin(IOssifiableProxy(BURNER), AGENT);
230238
_assertSingleOZRoleHolder(BURNER, DEFAULT_ADMIN_ROLE, AGENT);

contracts/upgrade/V3VoteScript.sol

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ contract V3VoteScript is OmnibusBase {
3030
//
3131
// Constants
3232
//
33-
uint256 public constant VOTE_ITEMS_COUNT = 13;
33+
uint256 public constant VOTE_ITEMS_COUNT = 14;
3434

3535
//
3636
// Immutables
@@ -188,6 +188,16 @@ contract V3VoteScript is OmnibusBase {
188188
call: _forwardCall(TEMPLATE.AGENT(), params.upgradeTemplate, abi.encodeCall(V3Template.finishUpgrade, ()))
189189
});
190190

191+
// Revoke REQUEST_BURN_SHARES_ROLE from Hoodi Sandbox module (only on Hoodi)
192+
voteItems[index++] = VoteItem({
193+
description: "14. Revoke REQUEST_BURN_SHARES_ROLE from Hoodi Sandbox",
194+
call: _forwardCall(
195+
TEMPLATE.AGENT(),
196+
TEMPLATE.OLD_BURNER(),
197+
abi.encodeCall(IAccessControl.revokeRole, (requestBurnSharesRole, TEMPLATE.HOODI_SANDBOX_MODULE()))
198+
)
199+
});
200+
191201
assert(index == VOTE_ITEMS_COUNT);
192202
}
193203
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity 0.8.25;
3+
4+
5+
/**
6+
* @title VaultsAdapterMock
7+
* @notice Stores immutable addresses required for the V3 upgrade process.
8+
* This contract centralizes address management for V3Template and V3VoteScript.
9+
*/
10+
contract VaultsAdapterMock {
11+
12+
address public immutable EVM_SCRIPT_EXECUTOR;
13+
14+
constructor(address _evmScriptExecutor) {
15+
EVM_SCRIPT_EXECUTOR = _evmScriptExecutor;
16+
}
17+
18+
function evmScriptExecutor() external view returns (address) {
19+
return EVM_SCRIPT_EXECUTOR;
20+
}
21+
22+
}

0 commit comments

Comments
 (0)