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
13 changes: 8 additions & 5 deletions contracts/0.8.25/utils/V3TemporaryAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface IVaultHub {
function BAD_DEBT_MASTER_ROLE() external view returns (bytes32);
}

interface IPausableUntil {
interface IPausableUntilWithRoles {
function PAUSE_ROLE() external view returns (bytes32);
}

Expand All @@ -39,15 +39,18 @@ interface IStakingRouter {
address stakingModuleAddress;
uint16 stakingModuleFee;
uint16 treasuryFee;
uint16 targetShare;
uint16 stakeShareLimit;
uint8 status;
string name;
uint64 lastDepositAt;
uint256 lastDepositBlock;
uint256 exitedValidatorsCount;
uint16 priorityExitShareThreshold;
uint64 maxDepositsPerBlock;
uint64 minDepositBlockDistance;
}

function getStakingModules() external view returns (StakingModule[] memory);
function getStakingModules() external view returns (StakingModule[] memory res);
}

interface ICSModule {
Expand Down Expand Up @@ -142,7 +145,7 @@ contract V3TemporaryAdmin {
*/
function _setupVaultHub(address _vaultHub, address _vaultsAdapter, address _gateSeal) private {
// Get roles from the contract
bytes32 pauseRole = IPausableUntil(_vaultHub).PAUSE_ROLE();
bytes32 pauseRole = IPausableUntilWithRoles(_vaultHub).PAUSE_ROLE();
bytes32 vaultMasterRole = IVaultHub(_vaultHub).VAULT_MASTER_ROLE();
bytes32 redemptionMasterRole = IVaultHub(_vaultHub).REDEMPTION_MASTER_ROLE();
bytes32 validatorExitRole = IVaultHub(_vaultHub).VALIDATOR_EXIT_ROLE();
Expand All @@ -165,7 +168,7 @@ contract V3TemporaryAdmin {
* @param _predepositGuarantee The PredepositGuarantee contract address
*/
function _setupPredepositGuarantee(address _predepositGuarantee, address _gateSeal) private {
bytes32 pauseRole = IPausableUntil(_predepositGuarantee).PAUSE_ROLE();
bytes32 pauseRole = IPausableUntilWithRoles(_predepositGuarantee).PAUSE_ROLE();
IAccessControl(_predepositGuarantee).grantRole(pauseRole, _gateSeal);
_transferAdminToAgent(_predepositGuarantee);
}
Expand Down
15 changes: 3 additions & 12 deletions contracts/upgrade/V3Addresses.sol
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ contract V3Addresses {
address public immutable NODE_OPERATORS_REGISTRY;
address public immutable SIMPLE_DVT;
address public immutable CSM_ACCOUNTING;
address public immutable HOODI_SANDBOX_MODULE;
address public immutable ORACLE_DAEMON_CONFIG;

constructor(
V3AddressesParams memory params
Expand Down Expand Up @@ -163,7 +163,6 @@ contract V3Addresses {
GATE_SEAL = params.gateSealForVaults;
EVM_SCRIPT_EXECUTOR = IVaultsAdapter(params.vaultsAdapter).evmScriptExecutor();
VAULTS_ADAPTER = params.vaultsAdapter;

//
// Discovered via other contracts
//
Expand All @@ -187,6 +186,7 @@ contract V3Addresses {
VALIDATORS_EXIT_BUS_ORACLE = newLocatorImpl.validatorsExitBusOracle();
WITHDRAWAL_QUEUE = newLocatorImpl.withdrawalQueue();
WSTETH = newLocatorImpl.wstETH();
ORACLE_DAEMON_CONFIG = newLocatorImpl.oracleDaemonConfig();

{
// Retrieve contracts with burner allowances to migrate: NOR, SDVT and CSM ACCOUNTING
Expand All @@ -197,18 +197,9 @@ contract V3Addresses {
IStakingRouter.StakingModule memory simpleDvt = stakingModules[1];
if (_hash(simpleDvt.name) != _hash(SIMPLE_DVT_MODULE_NAME)) revert IncorrectStakingModuleName(simpleDvt.name);
SIMPLE_DVT = simpleDvt.stakingModuleAddress;

// NB: there is additional module on Hoodi before CSM
uint256 csmIndex = stakingModules.length - 1;
IStakingRouter.StakingModule memory csm = stakingModules[csmIndex];
IStakingRouter.StakingModule memory csm = stakingModules[2];
if (_hash(csm.name) != _hash(CSM_MODULE_NAME)) revert IncorrectStakingModuleName(csm.name);
CSM_ACCOUNTING = ICSModule(csm.stakingModuleAddress).accounting();

if (stakingModules.length == 4) {
IStakingRouter.StakingModule memory hoodiSandbox = stakingModules[2];
if (_hash(hoodiSandbox.name) != _hash("Sandbox")) revert IncorrectStakingModuleName(hoodiSandbox.name);
HOODI_SANDBOX_MODULE = hoodiSandbox.stakingModuleAddress;
}
}
}

Expand Down
29 changes: 13 additions & 16 deletions contracts/upgrade/V3Template.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ interface IBurner is IBurnerWithoutAccessControl, IAccessControlEnumerable {
}

interface ILidoWithFinalizeUpgrade is ILido {
function finalizeUpgrade_v3(address _oldBurner, address[] calldata _contractsWithBurnerAllowances) external;
function finalizeUpgrade_v3(address _oldBurner, address[] calldata _contractsWithBurnerAllowances, uint256 _initialMaxExternalRatioBP) external;
}

interface IAccountingOracle is IBaseOracle {
Expand Down Expand Up @@ -91,10 +91,10 @@ contract V3Template is V3Addresses {

bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

// Timestamp since startUpgrade() and finishUpgrade() revert with Expired()
// This behavior is introduced to disarm the template if the upgrade voting creation or enactment didn't
// happen in proper time period
uint256 public constant EXPIRE_SINCE_INCLUSIVE = 1761868800; // 2025-10-31 00:00:00 UTC
// Timestamp since which startUpgrade()
// This behavior is introduced to disarm the template if the upgrade voting creation or enactment
// didn't happen in proper time period
uint256 public immutable EXPIRE_SINCE_INCLUSIVE;

// Initial value of upgradeBlockNumber storage variable
uint256 public constant UPGRADE_NOT_STARTED = 0;
Expand All @@ -111,6 +111,7 @@ contract V3Template is V3Addresses {
uint256 public initialTotalShares;
uint256 public initialTotalPooledEther;
address[] public contractsWithBurnerAllowances;
uint256 public immutable INITIAL_MAX_EXTERNAL_RATIO_BP;

//
// Slots for transient storage
Expand All @@ -123,16 +124,16 @@ contract V3Template is V3Addresses {


/// @param _params Params required to initialize the addresses contract
constructor(V3AddressesParams memory _params) V3Addresses(_params) {
/// @param _expireSinceInclusive Unix timestamp after which upgrade actions revert
/// @param _initialMaxExternalRatioBP Initial maximum external ratio in basis points
constructor(V3AddressesParams memory _params, uint256 _expireSinceInclusive, uint256 _initialMaxExternalRatioBP) V3Addresses(_params) {
EXPIRE_SINCE_INCLUSIVE = _expireSinceInclusive;
INITIAL_MAX_EXTERNAL_RATIO_BP = _initialMaxExternalRatioBP;
contractsWithBurnerAllowances.push(WITHDRAWAL_QUEUE);
// NB: NOR and SIMPLE_DVT allowances are set to 0 in TW upgrade, so they are not migrated
contractsWithBurnerAllowances.push(CSM_ACCOUNTING);
}

function isHoodi() internal view returns (bool) {
return HOODI_SANDBOX_MODULE != address(0);
}

/// @notice Must be called before LidoLocator is upgraded
function startUpgrade() external {
if (msg.sender != AGENT) revert OnlyAgentCanUpgrade();
Expand Down Expand Up @@ -162,7 +163,7 @@ contract V3Template is V3Addresses {

isUpgradeFinished = true;

ILidoWithFinalizeUpgrade(LIDO).finalizeUpgrade_v3(OLD_BURNER, contractsWithBurnerAllowances);
ILidoWithFinalizeUpgrade(LIDO).finalizeUpgrade_v3(OLD_BURNER, contractsWithBurnerAllowances, INITIAL_MAX_EXTERNAL_RATIO_BP);

IAccountingOracle(ACCOUNTING_ORACLE).finalizeUpgrade_v4(EXPECTED_FINAL_ACCOUNTING_ORACLE_CONSENSUS_VERSION);

Expand Down Expand Up @@ -228,11 +229,7 @@ contract V3Template is V3Addresses {
function _assertFinalACL() internal view {
// Burner
bytes32 requestBurnSharesRole = IBurner(BURNER).REQUEST_BURN_SHARES_ROLE();
if (isHoodi()) {
_assertSingleOZRoleHolder(OLD_BURNER, requestBurnSharesRole, HOODI_SANDBOX_MODULE);
} else {
_assertZeroOZRoleHolders(OLD_BURNER, requestBurnSharesRole);
}
_assertZeroOZRoleHolders(OLD_BURNER, requestBurnSharesRole);

_assertProxyAdmin(IOssifiableProxy(BURNER), AGENT);
_assertSingleOZRoleHolder(BURNER, DEFAULT_ADMIN_ROLE, AGENT);
Expand Down
64 changes: 42 additions & 22 deletions contracts/upgrade/V3VoteScript.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ interface IKernel {
function APP_BASES_NAMESPACE() external view returns (bytes32);
}

interface IOracleDaemonConfig {
function CONFIG_MANAGER_ROLE() external view returns (bytes32);
function set(string calldata _key, bytes calldata _value) external;
}

interface IStakingRouter {
function REPORT_REWARDS_MINTED_ROLE() external view returns (bytes32);
}
Expand All @@ -30,7 +35,7 @@ contract V3VoteScript is OmnibusBase {
//
// Constants
//
uint256 public constant VOTE_ITEMS_COUNT = 14;
uint256 public constant VOTE_ITEMS_COUNT = 17;

//
// Immutables
Expand Down Expand Up @@ -58,21 +63,18 @@ contract V3VoteScript is OmnibusBase {
voteItems = new VoteItem[](VOTE_ITEMS_COUNT);
uint256 index = 0;

// Start the upgrade process
voteItems[index++] = VoteItem({
description: "1. Call UpgradeTemplateV3.startUpgrade",
call: _forwardCall(TEMPLATE.AGENT(), params.upgradeTemplate, abi.encodeCall(V3Template.startUpgrade, ()))
});

// Upgrade LidoLocator implementation
voteItems[index++] = VoteItem({
description: "2. Upgrade LidoLocator implementation",
call: _forwardCall(TEMPLATE.AGENT(), TEMPLATE.LOCATOR(), abi.encodeCall(IOssifiableProxy.proxy__upgradeTo, (TEMPLATE.NEW_LOCATOR_IMPL())))
});

// Grant APP_MANAGER_ROLE to the AGENT
voteItems[index++] = VoteItem({
description: "3. Grant APP_MANAGER_ROLE to the AGENT",
description: "3. Grant Aragon APP_MANAGER_ROLE to the AGENT",
call: _forwardCall(
TEMPLATE.AGENT(),
TEMPLATE.ACL(),
Expand All @@ -85,7 +87,6 @@ contract V3VoteScript is OmnibusBase {
)
});

// Set Lido implementation in Kernel
voteItems[index++] = VoteItem({
description: "4. Set Lido implementation in Kernel",
call: _forwardCall(
Expand All @@ -95,9 +96,8 @@ contract V3VoteScript is OmnibusBase {
)
});

// Revoke APP_MANAGER_ROLE from the AGENT on Kernel ACL
voteItems[index++] = VoteItem({
description: "5. Revoke APP_MANAGER_ROLE from the AGENT",
description: "5. Revoke Aragon APP_MANAGER_ROLE from the AGENT",
call: _forwardCall(
TEMPLATE.AGENT(),
TEMPLATE.ACL(),
Expand All @@ -110,7 +110,6 @@ contract V3VoteScript is OmnibusBase {
)
});

// Revoke REQUEST_BURN_SHARES_ROLE from Lido
bytes32 requestBurnSharesRole = IBurner(TEMPLATE.OLD_BURNER()).REQUEST_BURN_SHARES_ROLE();
voteItems[index++] = VoteItem({
description: "6. Revoke REQUEST_BURN_SHARES_ROLE from Lido",
Expand All @@ -121,7 +120,6 @@ contract V3VoteScript is OmnibusBase {
)
});

// Revoke REQUEST_BURN_SHARES_ROLE from Curated staking modules (NodeOperatorsRegistry)
voteItems[index++] = VoteItem({
description: "7. Revoke REQUEST_BURN_SHARES_ROLE from Curated staking module",
call: _forwardCall(
Expand All @@ -131,7 +129,6 @@ contract V3VoteScript is OmnibusBase {
)
});

// Revoke REQUEST_BURN_SHARES_ROLE from SimpleDVT
voteItems[index++] = VoteItem({
description: "8. Revoke REQUEST_BURN_SHARES_ROLE from SimpleDVT",
call: _forwardCall(
Expand All @@ -141,7 +138,6 @@ contract V3VoteScript is OmnibusBase {
)
});

// Revoke REQUEST_BURN_SHARES_ROLE from CS Accounting
voteItems[index++] = VoteItem({
description: "9. Revoke REQUEST_BURN_SHARES_ROLE from Community Staking Accounting",
call: _forwardCall(
Expand All @@ -151,7 +147,6 @@ contract V3VoteScript is OmnibusBase {
)
});

// Upgrade AccountingOracle implementation
voteItems[index++] = VoteItem({
description: "10. Upgrade AccountingOracle implementation",
call: _forwardCall(
Expand All @@ -161,7 +156,6 @@ contract V3VoteScript is OmnibusBase {
)
});

// Revoke REPORT_REWARDS_MINTED_ROLE from Lido
bytes32 reportRewardsMintedRole = IStakingRouter(TEMPLATE.STAKING_ROUTER()).REPORT_REWARDS_MINTED_ROLE();
voteItems[index++] = VoteItem({
description: "11. Revoke REPORT_REWARDS_MINTED_ROLE from Lido",
Expand All @@ -172,7 +166,6 @@ contract V3VoteScript is OmnibusBase {
)
});

// Grant REPORT_REWARDS_MINTED_ROLE to Accounting
voteItems[index++] = VoteItem({
description: "12. Grant REPORT_REWARDS_MINTED_ROLE to Accounting",
call: _forwardCall(
Expand All @@ -182,22 +175,49 @@ contract V3VoteScript is OmnibusBase {
)
});

// Finish the upgrade process
bytes32 configManagerRole = IOracleDaemonConfig(TEMPLATE.ORACLE_DAEMON_CONFIG()).CONFIG_MANAGER_ROLE();

voteItems[index++] = VoteItem({
description: "13. Call UpgradeTemplateV3.finishUpgrade",
call: _forwardCall(TEMPLATE.AGENT(), params.upgradeTemplate, abi.encodeCall(V3Template.finishUpgrade, ()))
description: "13. Grant OracleDaemonConfig's CONFIG_MANAGER_ROLE to Agent",
call: _forwardCall(
TEMPLATE.AGENT(),
TEMPLATE.ORACLE_DAEMON_CONFIG(),
abi.encodeCall(IAccessControl.grantRole, (configManagerRole, TEMPLATE.AGENT()))
)
});

// Revoke REQUEST_BURN_SHARES_ROLE from Hoodi Sandbox module (only on Hoodi)
voteItems[index++] = VoteItem({
description: "14. Revoke REQUEST_BURN_SHARES_ROLE from Hoodi Sandbox",
description: "14. Set SLASHING_RESERVE_WE_RIGHT_SHIFT to 0x2000 at OracleDaemonConfig",
call: _forwardCall(
TEMPLATE.AGENT(),
TEMPLATE.OLD_BURNER(),
abi.encodeCall(IAccessControl.revokeRole, (requestBurnSharesRole, TEMPLATE.HOODI_SANDBOX_MODULE()))
TEMPLATE.ORACLE_DAEMON_CONFIG(),
abi.encodeCall(IOracleDaemonConfig.set, ("SLASHING_RESERVE_WE_RIGHT_SHIFT", abi.encode(0x2000)))
)
});

voteItems[index++] = VoteItem({
description: "15. Set SLASHING_RESERVE_WE_LEFT_SHIFT to 0x2000 at OracleDaemonConfig",
call: _forwardCall(
TEMPLATE.AGENT(),
TEMPLATE.ORACLE_DAEMON_CONFIG(),
abi.encodeCall(IOracleDaemonConfig.set, ("SLASHING_RESERVE_WE_LEFT_SHIFT", abi.encode(0x2000)))
)
});

voteItems[index++] = VoteItem({
description: "16. Revoke OracleDaemonConfig's CONFIG_MANAGER_ROLE from Agent",
call: _forwardCall(
TEMPLATE.AGENT(),
TEMPLATE.ORACLE_DAEMON_CONFIG(),
abi.encodeCall(IAccessControl.revokeRole, (configManagerRole, TEMPLATE.AGENT()))
)
});

voteItems[index++] = VoteItem({
description: "17. Call UpgradeTemplateV3.finishUpgrade",
call: _forwardCall(TEMPLATE.AGENT(), params.upgradeTemplate, abi.encodeCall(V3Template.finishUpgrade, ()))
});

assert(index == VOTE_ITEMS_COUNT);
}
}
9 changes: 8 additions & 1 deletion lib/config-schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const BurnerSchema = z.object({
totalNonCoverSharesBurnt: BigIntStringSchema.optional(),
});

// Triggerable withdrawals gateway schema
// Triggerable withdrawals gateway schema (used in scratch configs)
const TriggerableWithdrawalsGatewaySchema = z.object({
maxExitRequestsLimit: PositiveIntSchema,
exitsPerFrame: PositiveIntSchema,
Expand All @@ -92,6 +92,12 @@ const OracleVersionsSchema = z.object({
ao_consensus_version: PositiveIntSchema,
});

// V3 vote script params
const V3VoteScriptSchema = z.object({
expiryTimestamp: NonNegativeIntSchema,
initialMaxExternalRatioBP: BasisPointsSchema,
});

// Aragon app versions schema
const AragonAppVersionsSchema = z.object({
nor_version: z.array(z.number()).length(3),
Expand Down Expand Up @@ -119,6 +125,7 @@ export const UpgradeParametersSchema = z.object({
burner: BurnerSchema,
oracleVersions: OracleVersionsSchema.optional(),
aragonAppVersions: AragonAppVersionsSchema.optional(),
v3VoteScript: V3VoteScriptSchema,
});

// Gate seal schema (for scratch deployment)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
"test:integration:upgrade-hoodi": "yarn test:integration:upgrade:helper-hoodi test/integration/**/*.ts",
"test:integration:upgrade:helper-hoodi": "cp deployed-hoodi.json deployed-hoodi-upgrade.json && NETWORK_STATE_FILE=deployed-hoodi-upgrade.json UPGRADE_PARAMETERS_FILE=scripts/upgrade/upgrade-params-hoodi.toml MODE=forking UPGRADE=true GAS_PRIORITY_FEE=1 GAS_MAX_FEE=100 DEPLOYER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 hardhat test --trace --disabletracer",
"test:integration:upgrade-template": "cp deployed-mainnet.json deployed-mainnet-upgrade.json && NETWORK_STATE_FILE=deployed-mainnet-upgrade.json UPGRADE_PARAMETERS_FILE=scripts/upgrade/upgrade-params-mainnet.toml MODE=forking TEMPLATE_TEST=true GAS_PRIORITY_FEE=1 GAS_MAX_FEE=100 DEPLOYER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 hardhat test test/integration/upgrade/*.ts --fulltrace --disabletracer",
"test:integration:scratch": "DEPLOYER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 SKIP_INTERFACES_CHECK=true SKIP_CONTRACT_SIZE=true SKIP_GAS_REPORT=true GENESIS_TIME=1639659600 GAS_PRIORITY_FEE=1 GAS_MAX_FEE=100 yarn test:integration:scratch:helper test/integration/**/*.ts",
"test:integration:upgrade-template-hoodi": "cp deployed-hoodi.json deployed-hoodi-upgrade.json && NETWORK_STATE_FILE=deployed-hoodi-upgrade.json UPGRADE_PARAMETERS_FILE=scripts/upgrade/upgrade-params-hoodi.toml MODE=forking TEMPLATE_TEST=true GAS_PRIORITY_FEE=1 GAS_MAX_FEE=100 DEPLOYER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 hardhat test test/integration/upgrade/*.ts --fulltrace --disabletracer",
"test:integration:scratch": "SKIP_INTERFACES_CHECK=true SKIP_CONTRACT_SIZE=true SKIP_GAS_REPORT=true GENESIS_TIME=1639659600 GAS_PRIORITY_FEE=1 GAS_MAX_FEE=100 DEPLOYER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 yarn test:integration:scratch:helper test/integration/**/*.ts",
"test:integration:scratch:helper": "MODE=scratch hardhat test",
"test:integration:scratch:trace": "MODE=scratch hardhat test test/integration/**/*.ts --trace --disabletracer",
"test:integration:scratch:fulltrace": "MODE=scratch hardhat test test/integration/**/*.ts --fulltrace --disabletracer",
Expand Down

This file was deleted.

Loading