diff --git a/contracts/0.8.25/sr/SRLib.sol b/contracts/0.8.25/sr/SRLib.sol index daa701f375..f1eb74bc4a 100644 --- a/contracts/0.8.25/sr/SRLib.sol +++ b/contracts/0.8.25/sr/SRLib.sol @@ -179,7 +179,8 @@ library SRLib { depositTargetShare: smOld.stakeShareLimit, withdrawalProtectShare: smOld.priorityExitShareThreshold, status: StakingModuleStatus(smOld.status), - moduleType: StakingModuleType.Legacy + moduleType: StakingModuleType.Legacy, + withdrawalCredentialsType: SRUtils.WC_TYPE_01 }) ); @@ -283,6 +284,7 @@ library SRLib { SRUtils._validateModuleName(_moduleName); SRUtils._validateModulesCount(); SRUtils._validateModuleType(_moduleConfig.moduleType); + SRUtils._validateWithdrawalCredentialsType(_moduleConfig.withdrawalCredentialsType); // Check for duplicate module address /// @dev due to small number of modules, we can afford to do this check on add @@ -301,6 +303,7 @@ library SRLib { moduleState.config.moduleAddress = _moduleAddress; moduleState.config.status = StakingModuleStatus.Active; moduleState.config.moduleType = StakingModuleType(_moduleConfig.moduleType); + moduleState.config.withdrawalCredentialsType = uint8(_moduleConfig.withdrawalCredentialsType); moduleState.name = _moduleName; @@ -384,7 +387,7 @@ library SRLib { if (_getCapacity && stateConfig.status == StakingModuleStatus.Active) { // todo rethink getting capacity for new modules (maybe some additional limits will be applied) (,, uint256 depositableValidatorsCount) = _moduleId.getIStakingModule().getStakingModuleSummary(); - capacity = SRUtils._getModuleCapacity(stateConfig.moduleType, depositableValidatorsCount); + capacity = SRUtils._getModuleCapacity(stateConfig.withdrawalCredentialsType, depositableValidatorsCount); } // else capacity = 0 } diff --git a/contracts/0.8.25/sr/SRTypes.sol b/contracts/0.8.25/sr/SRTypes.sol index dd249f486a..d07af57c5d 100644 --- a/contracts/0.8.25/sr/SRTypes.sol +++ b/contracts/0.8.25/sr/SRTypes.sol @@ -20,9 +20,9 @@ enum StakingModuleStatus { } /// @dev Type identifier for modules -/// For simplicity, only one deposit type is allowed per module. -/// Legacy - keys count-based accounting, old IStakingModule, WC type 0x01 -/// New - balance-based accounting, new IStakingModuleV2, WC type 0x02 +/// Identify module accounting algorithm +/// Legacy +/// New enum StakingModuleType { Legacy, New @@ -68,6 +68,8 @@ struct StakingModuleConfig { /// @dev 0 = Legacy, 0x01 withdrawals, 1 = New, 0x02 withdrawals. /// @dev See {StakingModuleType} enum. uint256 moduleType; + /// @notice The type of withdrawal credentials for creation of validators + uint256 withdrawalCredentialsType; } /// @dev old data struct, kept for backward compatibility @@ -106,6 +108,7 @@ struct StakingModule { /// @dev Must be harmonized with `OracleReportSanityChecker.appearedValidatorsPerDayLimit`. /// See docs for the `OracleReportSanityChecker.setAppearedValidatorsPerDayLimit` function). uint64 minDepositBlockDistance; + /// TODO: we dont need moduleType in this release /// @notice The type of staking module (Legacy/Standard), defines the module interface and withdrawal credentials type. /// @dev 0 = Legacy, 0x01 withdrawals, 1 = New, 0x02 withdrawals. /// @dev See {StakingModuleType} enum. @@ -131,6 +134,8 @@ struct ModuleStateConfig { StakingModuleStatus status; /// @notice Staking module type (Legacy/Standard) StakingModuleType moduleType; + /// @notice The type of withdrawal credentials for creation of validators + uint8 withdrawalCredentialsType; } // /// @notice The type of withdrawal credentials for creation of validators // uint8 wcType; diff --git a/contracts/0.8.25/sr/SRUtils.sol b/contracts/0.8.25/sr/SRUtils.sol index c5f8d2bf4b..28bcacd794 100644 --- a/contracts/0.8.25/sr/SRUtils.sol +++ b/contracts/0.8.25/sr/SRUtils.sol @@ -30,6 +30,7 @@ library SRUtils { error StakingModuleWrongName(); error StakingModuleUnregistered(); error InvalidStakingModuleType(); + error InvalidWithdrawalCredentialsType(); error InvalidPriorityExitShareThreshold(); error InvalidMinDepositBlockDistance(); error InvalidMaxDepositPerBlockValue(); @@ -83,6 +84,13 @@ library SRUtils { } } + function _validateWithdrawalCredentialsType(uint256 v) internal pure{ + // allow exactly 1 or 2 (0x01 / 0x02) + if (v != WC_TYPE_01 && v != WC_TYPE_02) { + revert InvalidWithdrawalCredentialsType(); + } + } + function _validateModulesCount() internal view { if (SRStorage.getModulesCount() >= MAX_STAKING_MODULES_COUNT) { revert StakingModulesLimitExceeded(); @@ -95,24 +103,10 @@ library SRUtils { } } - function _getModuleWCType(StakingModuleType moduleType) internal pure returns (uint8) { - if (moduleType == StakingModuleType.Legacy) { - return WC_TYPE_01; - } else if (moduleType == StakingModuleType.New) { - return WC_TYPE_02; - } else { - revert InvalidStakingModuleType(); - } - } - - function _getModuleMEB(StakingModuleType moduleType) internal pure returns (uint256) { - if (moduleType == StakingModuleType.Legacy) { - return MAX_EFFECTIVE_BALANCE_WC_TYPE_01; - } else if (moduleType == StakingModuleType.New) { - return MAX_EFFECTIVE_BALANCE_WC_TYPE_02; - } else { - revert InvalidStakingModuleType(); - } + function _getModuleMEB(uint8 wcType) internal pure returns (uint256) { + if (wcType == WC_TYPE_01) return MAX_EFFECTIVE_BALANCE_WC_TYPE_01; + if (wcType == WC_TYPE_02) return MAX_EFFECTIVE_BALANCE_WC_TYPE_02; + revert InvalidWithdrawalCredentialsType(); } function _toE4Precision(uint256 _value, uint256 _precision) internal pure returns (uint16) { @@ -157,12 +151,12 @@ library SRUtils { } /// @dev calculate module capacity in ETH - function _getModuleCapacity(StakingModuleType moduleType, uint256 availableKeysCount) + function _getModuleCapacity(uint8 wcType, uint256 availableKeysCount) internal pure returns (uint256) { - return availableKeysCount * _getModuleMEB(moduleType); + return availableKeysCount * _getModuleMEB(wcType); } function _toGwei(uint256 amount) internal pure returns (uint96) { diff --git a/contracts/0.8.25/sr/StakingRouter.sol b/contracts/0.8.25/sr/StakingRouter.sol index d99741e4b8..2cb5f3c93a 100644 --- a/contracts/0.8.25/sr/StakingRouter.sol +++ b/contracts/0.8.25/sr/StakingRouter.sol @@ -10,10 +10,8 @@ import {AccessControlEnumerableUpgradeable} from import {BeaconChainDepositor, IDepositContract} from "contracts/0.8.25/lib/BeaconChainDepositor.sol"; import {DepositsTracker} from "contracts/common/lib/DepositsTracker.sol"; import {DepositedState} from "contracts/common/interfaces/DepositedState.sol"; -import {DepositsTempStorage} from "contracts/common/lib/DepositsTempStorage.sol"; import {WithdrawalCredentials} from "contracts/common/lib/WithdrawalCredentials.sol"; import {IStakingModule} from "contracts/common/interfaces/IStakingModule.sol"; -import {IStakingModuleV2} from "contracts/common/interfaces/IStakingModuleV2.sol"; import {STASStorage} from "contracts/0.8.25/stas/STASTypes.sol"; import {STASCore} from "contracts/0.8.25/stas/STASCore.sol"; import {SRLib} from "./SRLib.sol"; @@ -73,7 +71,7 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { uint256 public constant INITIAL_DEPOSIT_SIZE = SRUtils.MAX_EFFECTIVE_BALANCE_WC_TYPE_01; /// @dev Module trackers will be derived from this position - bytes32 internal constant DEPOSITS_TRACKER = keccak256("lido.StakingRouter.depositTracker"); + // bytes32 internal constant DEPOSITS_TRACKER = keccak256("lido.StakingRouter.depositTracker"); /// @dev ACL roles bytes32 public constant MANAGE_WITHDRAWAL_CREDENTIALS_ROLE = keccak256("MANAGE_WITHDRAWAL_CREDENTIALS_ROLE"); @@ -104,6 +102,7 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { error StakingModuleStatusTheSame(); error LegacyStakingModuleNotSupported(); error LegacyStakingModuleRequired(); + error WithdrawalCredentials01TypeRequired(); /// @dev compatibility getters for constants removed in favor of SRLib // function INITIAL_DEPOSIT_SIZE() external pure returns (uint256) { @@ -336,6 +335,7 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { /// @param _operatorIds Ids of the node operators to be updated /// @param _effectiveBalances Effective balances for the specified operators /// @dev TODO: add separate role for this function + /// TODO: maybe dont need in first release function reportStakingModuleOperatorBalances( uint256 _stakingModuleId, bytes calldata _operatorIds, @@ -347,7 +347,7 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { if (stateConfig.moduleType != StakingModuleType.New) { revert LegacyStakingModuleNotSupported(); } - + _stakingModuleId.getIStakingModuleV2().updateOperatorBalances(_operatorIds, _effectiveBalances); } @@ -710,7 +710,7 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { /// @param _stakingModuleId Id of the staking module to be deposited. /// @return withdrawal credentials: 0x01... - for Legacy modules, 0x02... - for New modules function getStakingModuleWithdrawalCredentials(uint256 _stakingModuleId) external view returns (bytes32) { - return _getWithdrawalCredentialsWithType(getStakingModuleType(_stakingModuleId)); + return _getWithdrawalCredentialsWithType(getStakingModuleWithdrawalCredentialsType(_stakingModuleId)); } /// @notice Returns the staking module type: Legacy or New, i.e. balance-based (uses 0x02 withdrawal credentials) @@ -721,6 +721,14 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { return stateConfig.moduleType; } + /// @notice Returns the staking module type: Legacy or New, i.e. balance-based (uses 0x02 withdrawal credentials) + /// @param _stakingModuleId Id of the staking module + /// @return Staking module type: 0 - Legacy (WC type 0x01) or 1 - New (WC type 0x02) + function getStakingModuleWithdrawalCredentialsType(uint256 _stakingModuleId) public view returns (uint8) { + (, ModuleStateConfig storage stateConfig) = _validateAndGetModuleState(_stakingModuleId); + return stateConfig.withdrawalCredentialsType; + } + /// @notice Returns the max amount of Eth for initial 32 eth deposits in staking module. /// @param _stakingModuleId Id of the staking module to be deposited. /// @param _depositableEth Max amount of ether that might be used for deposits count calculation. @@ -728,39 +736,29 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { /// @return depositsCount Count of deposits corresponding to the deposits amount function getStakingModuleMaxInitialDepositsAmount(uint256 _stakingModuleId, uint256 _depositableEth) public + view returns (uint256 depositsAmount, uint256 depositsCount) { (, ModuleStateConfig storage stateConfig) = _validateAndGetModuleState(_stakingModuleId); - // get max eth amount that can be deposited into the module, capped by its capacity (depositable validators count) // If module is not active, then it capacity is 0, so stakingModuleDepositableEthAmount will be 0. uint256 stakingModuleDepositableEthAmount = _getTargetDepositAllocation(_stakingModuleId, _depositableEth); if (stakingModuleDepositableEthAmount == 0) return (0, 0); - if (stateConfig.moduleType == StakingModuleType.New) { - // get operators and their available operatorAllocations for deposits. In general case, not all depositable validators - // will be used for deposits, due to Module can apply its own rules to limit deposits per operator - (uint256[] memory operators, uint256[] memory operatorAllocations) = - IStakingModuleV2(stateConfig.moduleAddress).getAllocation(stakingModuleDepositableEthAmount); - - uint256[] memory counts; - (depositsCount, counts) = _getNewDepositsCount02(stakingModuleDepositableEthAmount, operatorAllocations); - - // this will be read and clean in deposit method - DepositsTempStorage.storeOperatorCounts(operators, counts); - - depositsAmount = _getInitialDepositAmountByCount(depositsCount); - } else if (stateConfig.moduleType == StakingModuleType.Legacy) { - depositsCount = _getInitialDepositCountByAmount(stakingModuleDepositableEthAmount); - depositsAmount = _getInitialDepositAmountByCount(depositsCount); + if (stateConfig.withdrawalCredentialsType == SRUtils.WC_TYPE_01) { + depositsCount = stakingModuleDepositableEthAmount / SRUtils.MAX_EFFECTIVE_BALANCE_WC_TYPE_01; + depositsAmount = _getInitialDepositAmountByCount(depositsCount); + } else if (stateConfig.withdrawalCredentialsType == SRUtils.WC_TYPE_02) { + depositsCount = stakingModuleDepositableEthAmount / SRUtils.MAX_EFFECTIVE_BALANCE_WC_TYPE_02; + depositsAmount = _getInitialDepositAmountByCount(depositsCount); } else { - revert SRUtils.InvalidStakingModuleType(); + revert SRUtils.InvalidWithdrawalCredentialsType(); } } /// @notice DEPRECATED: use getStakingModuleMaxInitialDepositsAmount - /// This method only for the legacy modules + /// This method only for the modules with withdrawalCredentialsType 0x01 function getStakingModuleMaxDepositsCount(uint256 _stakingModuleId, uint256 _depositableEth) external view @@ -769,8 +767,9 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { (, ModuleStateConfig storage stateConfig) = _validateAndGetModuleState(_stakingModuleId); /// @dev This method is only supported for legacy modules - if (stateConfig.moduleType != StakingModuleType.Legacy) { - revert LegacyStakingModuleRequired(); + // TODO: change on withdrawalCredentialsType check + if (stateConfig.withdrawalCredentialsType != SRUtils.WC_TYPE_01) { + revert WithdrawalCredentials01TypeRequired(); } // If module is not active, then it capacity is 0, so stakingModuleDepositableEthAmount will be 0. @@ -780,38 +779,6 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { return _getInitialDepositCountByAmount(stakingModuleDepositableEthAmount); } - function _getNewDepositsCount02(uint256 moduleMaxAllocation, uint256[] memory operatorAllocations) - internal - pure - returns (uint256 totalCount, uint256[] memory counts) - { - uint256 len = operatorAllocations.length; - counts = new uint256[](len); - unchecked { - for (uint256 i = 0; i < len; ++i) { - uint256 allocation = operatorAllocations[i]; - - // sum of all `operatorAllocations` items should be <= moduleMaxAllocation - if (allocation > moduleMaxAllocation) { - revert AllocationExceedsTarget(); - } - - moduleMaxAllocation -= allocation; - - if (allocation >= INITIAL_DEPOSIT_SIZE) { - // if allocation is 4000 - 2 (= 2048 (enough for 1st key: initial deposit 32 and rest deposit 2016) + 1952 (enough for 2nd key: initial deposit 32 and rest deposit 1920) ) - // if allocation 32 - 1 (enough for initial deposit) - // if less than 32 - 0 (not enough for initial deposit) - // if allocation 2050 - 1 (= 2048 (enough for 1st key: initial deposit 32 and rest deposit 2016) + 2 (not enough even for initial deposit) ) - uint256 depositsCount = - 1 + (allocation - INITIAL_DEPOSIT_SIZE) / SRUtils.MAX_EFFECTIVE_BALANCE_WC_TYPE_02; - counts[i] = depositsCount; - totalCount += depositsCount; - } - } - } - } - /// @notice Returns the aggregate fee distribution proportion. /// @return modulesFee Modules aggregate fee in base precision. /// @return treasuryFee Treasury fee in base precision. @@ -977,7 +944,7 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { if (stateConfig.status != StakingModuleStatus.Active) revert StakingModuleNotActive(); - bytes32 withdrawalCredentials = _getWithdrawalCredentialsWithType(stateConfig.moduleType); + bytes32 withdrawalCredentials = _getWithdrawalCredentialsWithType(stateConfig.withdrawalCredentialsType); uint256 depositsValue = msg.value; address stakingModuleAddress = stateConfig.moduleAddress; @@ -996,7 +963,7 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { uint256 depositsCount = _getInitialDepositCountByAmount(depositsValue); (bytes memory publicKeysBatch, bytes memory signaturesBatch) = - _getOperatorAvailableKeys(stateConfig.moduleType, stakingModuleAddress, depositsCount, _depositCalldata); + IStakingModule(stakingModuleAddress).obtainDepositData(depositsCount, _depositCalldata); // TODO: maybe some checks of module's answer @@ -1019,22 +986,6 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { assert(etherBalanceBeforeDeposits - etherBalanceAfterDeposits == depositsValue); } - function _getOperatorAvailableKeys( - StakingModuleType moduleType, - address stakingModuleAddress, - uint256 depositsCount, - bytes calldata depositCalldata - ) internal returns (bytes memory keys, bytes memory signatures) { - if (moduleType == StakingModuleType.Legacy) { - return IStakingModule(stakingModuleAddress).obtainDepositData(depositsCount, depositCalldata); - } else { - (uint256[] memory operators, uint256[] memory counts) = DepositsTempStorage.getOperatorCounts(); - (keys, signatures) = IStakingModuleV2(stakingModuleAddress).getOperatorAvailableKeys(operators, counts); - - DepositsTempStorage.clearOperatorCounts(); - } - } - /// @notice Set 0x01 credentials to withdraw ETH on Consensus Layer side. /// @param _withdrawalCredentials 0x01 withdrawal credentials field as defined in the Consensus Layer specs. /// @dev Note that setWithdrawalCredentials discards all unused deposits data as the signatures are invalidated. @@ -1059,10 +1010,10 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { emit WithdrawalCredentialsSet(wc, _msgSender()); } - function _getWithdrawalCredentialsWithType(StakingModuleType moduleType) internal view returns (bytes32) { + function _getWithdrawalCredentialsWithType(uint8 withdrawalCredentialsType) internal view returns (bytes32) { bytes32 wc = getWithdrawalCredentials(); // if (wc == 0) revert EmptyWithdrawalsCredentials(); - return wc.setType(SRUtils._getModuleWCType(moduleType)); + return wc.setType(withdrawalCredentialsType); } /// @dev Save the last deposit state for the staking module and emit the event @@ -1154,7 +1105,7 @@ contract StakingRouter is AccessControlEnumerableUpgradeable { moduleState.status = uint8(stateConfig.status); moduleState.priorityExitShareThreshold = stateConfig.withdrawalProtectShare; moduleState.moduleType = uint8(stateConfig.moduleType); - moduleState.withdrawalCredentialsType = SRUtils._getModuleWCType(stateConfig.moduleType); + moduleState.withdrawalCredentialsType = uint8(stateConfig.withdrawalCredentialsType); ModuleStateDeposits storage stateDeposits = state.getStateDeposits(); moduleState.lastDepositAt = stateDeposits.lastDepositAt; diff --git a/contracts/common/interfaces/IStakingModuleV2.sol b/contracts/common/interfaces/IStakingModuleV2.sol index 9457cb283a..96cf3480e4 100644 --- a/contracts/common/interfaces/IStakingModuleV2.sol +++ b/contracts/common/interfaces/IStakingModuleV2.sol @@ -8,15 +8,11 @@ struct KeyData { bytes pubkey; uint256 keyIndex; uint256 operatorIndex; + uint256 moduleId; uint256 balance; } interface IStakingModuleV2 { - /// @notice Hook to notify module about deposit on operator - /// @param operatorId - Id of operator - /// @param amountInWei - Wei deposit amount - function onDeposit(uint256 operatorId, uint256 amountInWei) external; - // Flow of creation of validators /// @notice Get Eth allocation for operators based on available eth for deposits and current operator balances @@ -43,6 +39,12 @@ interface IStakingModuleV2 { /// @param data - validator data function verifyKeys(KeyData[] calldata data) external view returns (bool); + + /// @notice Check keys belong to operator of module + /// @param data - validator data + + function getKeysForTopUp(KeyData[] calldata data) external view returns (KeyData[] calldata wantToTopUp); + /// @notice Get Eth allocation for operators based on available eth for deposits and current operator balances /// @param depositAmount - Value available for deposit in module /// @param operators - Array of operators ids diff --git a/contracts/common/lib/DepositsTempStorage.sol b/contracts/common/lib/DepositsTempStorage.sol deleted file mode 100644 index cdb9cf9d42..0000000000 --- a/contracts/common/lib/DepositsTempStorage.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Lido -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.25; - -import {TransientSession} from "contracts/common/lib/TransientSession.sol"; - -library DepositsTempStorage { - using TransientSession for bytes32; - - bytes32 private constant OPERATORS = keccak256("lido.DepositsTempStorage.operatorIds"); - bytes32 private constant COUNTS = keccak256("lido.DepositsTempStorage.depositCounts"); - - modifier _sessionBegin() { - TransientSession._invalidateSession(); - _; - } - - modifier _sessionEnd() { - _; - TransientSession._invalidateSession(); - } - - /// need to store operators and allocations - /// allocations or counts - - // function storeOperators(uint256[] memory operators) public { - // OPERATORS._storeArray(operators); - // } - - // function storeCounts(uint256[] memory counts) public { - // COUNTS._storeArray(counts); - // } - - /// @dev store new values from current session - function storeOperatorCounts(uint256[] memory operators, uint256[] memory counts) public _sessionBegin { - OPERATORS._storeArray(operators); - COUNTS._storeArray(counts); - } - - // function getOperators() public view returns (uint256[] memory operators) { - // return OPERATORS._readArray(); - // } - - // function getCounts() public view returns (uint256[] memory operators) { - // return COUNTS._readArray(); - // } - - /// @dev read values from current session - function getOperatorCounts() public view returns (uint256[] memory operators, uint256[] memory counts) { - operators = OPERATORS._readArray(); - counts = COUNTS._readArray(); - } - - // function clearOperators() public { - // OPERATORS._clearArray(); - // } - - // function clearCounts() public { - // COUNTS._clearArray(); - // } - - /// @notice Clear all transient storage data at once - /// @dev Should be called at the end of transactions as it invalidates the session - function clearOperatorCounts() public _sessionEnd { - OPERATORS._clearArray(); - COUNTS._clearArray(); - } - - /// TODO: need to store {operator_id, module_id} => allocations - /// topUps will be calculated based on IStakingModuleV2.getAllocation(depositAmount,operators,topUpLimits) returns (uint256[] memory allocations) method - /// topUpLimits - based on keys balances calc sum on each operator -} diff --git a/contracts/common/lib/TransientSession.sol b/contracts/common/lib/TransientSession.sol deleted file mode 100644 index d317385b64..0000000000 --- a/contracts/common/lib/TransientSession.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -// solhint-disable-next-line lido/fixed-compiler-version -pragma solidity >=0.8.24 <0.9.0; - -import {TransientStorage} from "contracts/common/lib/TransientStorage.sol"; - -/** - * @title Transient storage session. - * @author KRogLA - * @notice Provides functionality for managing transient storage sessions (based on caller nonce) - * and wrappers for storing/reading/clearing arrays. - */ -library TransientSession { - using TransientStorage for bytes32; - - bytes32 private constant BASE = keccak256("TransientSession.base"); - - function _nonceSlot(address caller) private pure returns (bytes32 slot) { - return keccak256(abi.encode(BASE, caller)); - } - - function _getNonce(address caller) private view returns (uint256 nonce) { - return _nonceSlot(caller).__get(); - } - - function _itemSlot(bytes32 key) internal view returns (bytes32 slot) { - address caller = msg.sender; - uint256 nonce = _getNonce(caller); - return keccak256(abi.encode(BASE, caller, nonce, key)); - } - - function _bumpNonce(address caller) private { - bytes32 slot = _nonceSlot(caller); - unchecked { - slot.__put(slot.__get() + 1); - } - } - - function _invalidateSession() internal { - _bumpNonce(msg.sender); - } - - // storage wrappers - function _storeArray(bytes32 key, uint256[] memory values) internal { - _itemSlot(key).__storeArray(values); - } - - function _readArray(bytes32 key) internal view returns (uint256[] memory values) { - return _itemSlot(key).__readArray(); - } - - function _clearArray(bytes32 key) internal { - _itemSlot(key).__clearArray(); - } -} diff --git a/contracts/common/lib/TransientStorage.sol b/contracts/common/lib/TransientStorage.sol deleted file mode 100644 index b308b7f10c..0000000000 --- a/contracts/common/lib/TransientStorage.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -// solhint-disable-next-line lido/fixed-compiler-version -pragma solidity >=0.8.24 <0.9.0; - -/** - * @title Transient storage primitives. - * @author KRogLA - * @notice Provides low level functionality for reading/writing transient storage (EIP-1153) values - * and methods for storing/reading/clearing arrays. - */ -library TransientStorage { - // helpers - function __put(bytes32 slot, uint256 val) internal { - assembly { - tstore(slot, val) - } - } - - // прочитать значение (в текущей nonce) - function __get(bytes32 slot) internal view returns (uint256 val) { - assembly { - val := tload(slot) - } - } - - function __storeArray(bytes32 slot, uint256[] memory values) internal { - uint256 len = values.length; - // store length of array - __put(slot, len); - - unchecked { - uint256 slotItems = uint256(slot) + 1; - for (uint256 i = 0; i < len; ++i) { - __put(bytes32(slotItems + i), values[i]); - } - } - } - - function __readArray(bytes32 slot) internal view returns (uint256[] memory values) { - // load length of array - uint256 len = __get(slot); - values = new uint256[](len); - - unchecked { - uint256 slotItems = uint256(slot) + 1; - for (uint256 i = 0; i < len; ++i) { - values[i] = __get(bytes32(slotItems + i)); - } - } - } - - function __clearArray(bytes32 slot) internal { - uint256 len = __get(slot); - __put(slot, 0); - - unchecked { - uint256 slotItems = uint256(slot) + 1; - for (uint256 i = 0; i < len; ++i) { - __put(bytes32(slotItems + i), 0); - } - } - } -} diff --git a/lib/state-file.ts b/lib/state-file.ts index 7fcac3ee23..c083ac1782 100644 --- a/lib/state-file.ts +++ b/lib/state-file.ts @@ -112,7 +112,7 @@ export enum Sk { // Dual Governance dgDualGovernance = "dg:dualGovernance", dgEmergencyProtectedTimelock = "dg:emergencyProtectedTimelock", - depositsTempStorage = "depositsTempStorage", + // SR public libs beaconChainDepositor = "beaconChainDepositor", srLib = "srLib", // Easy Track @@ -180,7 +180,6 @@ export function getAddress(contractKey: Sk, state: DeploymentState): string { case Sk.minFirstAllocationStrategy: case Sk.validatorConsolidationRequests: case Sk.v3VoteScript: - case Sk.depositsTempStorage: case Sk.beaconChainDepositor: case Sk.vaultsAdapter: case Sk.gateSealFactory: diff --git a/scripts/scratch/steps/0083-deploy-core.ts b/scripts/scratch/steps/0083-deploy-core.ts index d4c71cf9f0..1493aef6ea 100644 --- a/scripts/scratch/steps/0083-deploy-core.ts +++ b/scripts/scratch/steps/0083-deploy-core.ts @@ -147,13 +147,6 @@ export async function main() { // Deploy StakingRouter // - // deploy deposit tracker - - // const depositsTracker = await deployWithoutProxy(Sk.depositsTracker, "DepositsTracker", deployer); - - // deploy temporary storage - const depositsTempStorage = await deployWithoutProxy(Sk.depositsTempStorage, "DepositsTempStorage", deployer); - // deploy beacon chain depositor const beaconChainDepositor = await deployWithoutProxy(Sk.beaconChainDepositor, "BeaconChainDepositor", deployer); @@ -172,7 +165,6 @@ export async function main() { libraries: { // DepositsTracker: depositsTracker.address, BeaconChainDepositor: beaconChainDepositor.address, - DepositsTempStorage: depositsTempStorage.address, SRLib: srLib.address, }, }, diff --git a/scripts/scratch/steps/0140-plug-staking-modules.ts b/scripts/scratch/steps/0140-plug-staking-modules.ts index 3a84543b27..2585d693eb 100644 --- a/scripts/scratch/steps/0140-plug-staking-modules.ts +++ b/scripts/scratch/steps/0140-plug-staking-modules.ts @@ -1,6 +1,6 @@ import { ethers } from "hardhat"; -import { StakingModuleType } from "lib"; +import { StakingModuleType, WITHDRAWAL_CREDENTIALS_TYPE_01 } from "lib"; import { loadContract } from "lib/contract"; import { makeTx } from "lib/deploy"; import { streccak } from "lib/keccak"; @@ -49,6 +49,7 @@ export async function main() { maxDepositsPerBlock: NOR_STAKING_MODULE_MAX_DEPOSITS_PER_BLOCK, minDepositBlockDistance: NOR_STAKING_MODULE_MIN_DEPOSIT_BLOCK_DISTANCE, moduleType: NOR_MODULE_TYPE, + withdrawalCredentialsType: WITHDRAWAL_CREDENTIALS_TYPE_01, }, ], { from: deployer }, @@ -69,6 +70,7 @@ export async function main() { maxDepositsPerBlock: SDVT_STAKING_MODULE_MAX_DEPOSITS_PER_BLOCK, minDepositBlockDistance: SDVT_STAKING_MODULE_MIN_DEPOSIT_BLOCK_DISTANCE, moduleType: SDVT_MODULE_TYPE, + withdrawalCredentialsType: WITHDRAWAL_CREDENTIALS_TYPE_01, }, ], { from: deployer }, diff --git a/scripts/upgrade/steps/0100-deploy-v3-contracts.ts b/scripts/upgrade/steps/0100-deploy-v3-contracts.ts index 41bd7d1f42..2ea7d26ab0 100644 --- a/scripts/upgrade/steps/0100-deploy-v3-contracts.ts +++ b/scripts/upgrade/steps/0100-deploy-v3-contracts.ts @@ -4,6 +4,7 @@ import { readUpgradeParameters } from "scripts/utils/upgrade"; import { Burner, + ConsolidationGateway, IGateSealFactory, IOracleReportSanityChecker_preV3, LazyOracle, @@ -303,8 +304,8 @@ export async function main() { ]); console.log("VaultFactory address", await vaultFactory.getAddress()); - const consolidationGateway = await deployWithoutProxy(Sk.consolidationGateway, "ConsolidationGateway", deployer, [ - agentAddress, // TODO: check + const consolidationGateway_ = await deployWithoutProxy(Sk.consolidationGateway, "ConsolidationGateway", deployer, [ + deployer, locator.address, // ToDo: Replace dummy parameters with real ones 10, // maxConsolidationRequestsLimit, @@ -312,7 +313,24 @@ export async function main() { 60, // frameDurationInSec ]); - console.log("ConsolidationGateway address", await consolidationGateway.getAddress()); + const consolidationGateway = await loadContract( + "ConsolidationGateway", + consolidationGateway_.address, + ); + + // ToDo: Grant ADD_CONSOLIDATION_REQUEST_ROLE to MessageBus address instead of deployer + // ADD_CONSOLIDATION_REQUEST_ROLE granted to deployer for testing convenience + await makeTx( + consolidationGateway, + "grantRole", + [await consolidationGateway.ADD_CONSOLIDATION_REQUEST_ROLE(), deployer], + { from: deployer }, + ); + const DEFAULT_ADMIN_ROLE = ethers.ZeroHash; + await makeTx(consolidationGateway, "grantRole", [DEFAULT_ADMIN_ROLE, agentAddress], { from: deployer }); + await makeTx(consolidationGateway, "renounceRole", [DEFAULT_ADMIN_ROLE, deployer], { from: deployer }); + + console.log("ConsolidationGateway address", await consolidationGateway_.address); // // Deploy new LidoLocator implementation diff --git a/test/0.8.25/contracts/DepositCallerWrapper__MockForStakingRouter.sol b/test/0.8.25/contracts/DepositCallerWrapper__MockForStakingRouter.sol index 5239f1a3a5..6c857c8838 100644 --- a/test/0.8.25/contracts/DepositCallerWrapper__MockForStakingRouter.sol +++ b/test/0.8.25/contracts/DepositCallerWrapper__MockForStakingRouter.sol @@ -8,11 +8,6 @@ interface IStakingRouter { uint256 _stakingModuleId, uint256 _depositableEth ) external view returns (uint256, uint256); - - function mock_storeTemp(uint256[] calldata operators, uint256[] calldata counts) external; - - /// @notice FOR TEST: clear temp - function mock_clearTemp() external; } /// @notice Test-only wrapper that must be set as the authorized Lido caller in the router. @@ -30,8 +25,6 @@ contract DepositCallerWrapper__MockForStakingRouter { uint256[] calldata operators, uint256[] calldata counts ) external payable { - stakingRouter.mock_storeTemp(operators, counts); - stakingRouter.deposit{value: msg.value}(stakingModuleId, bytes("")); } } diff --git a/test/0.8.25/contracts/StakingModuleV2__MockForStakingRouter.sol b/test/0.8.25/contracts/StakingModuleV2__MockForStakingRouter.sol index db42b56cb3..4c5cf80381 100644 --- a/test/0.8.25/contracts/StakingModuleV2__MockForStakingRouter.sol +++ b/test/0.8.25/contracts/StakingModuleV2__MockForStakingRouter.sol @@ -4,9 +4,8 @@ pragma solidity 0.8.25; import {IStakingModule} from "contracts/common/interfaces/IStakingModule.sol"; -import {IStakingModuleV2, KeyData} from "contracts/common/interfaces/IStakingModuleV2.sol"; -contract StakingModuleV2__MockForStakingRouter is IStakingModule, IStakingModuleV2 { +contract StakingModuleV2__MockForStakingRouter is IStakingModule { event Mock__TargetValidatorsLimitsUpdated(uint256 _nodeOperatorId, uint256 _targetLimitMode, uint256 _targetLimit); event Mock__RefundedValidatorsCountUpdated(uint256 _nodeOperatorId, uint256 _refundedValidatorsCount); event Mock__OnRewardsMinted(uint256 _totalShares); @@ -26,65 +25,6 @@ contract StakingModuleV2__MockForStakingRouter is IStakingModule, IStakingModule uint256 exitType ); - // allocation by operators - - uint256[] private modulesOperators__mocked; - uint256[] private modulesAllocations__mocked; - - function mock_getAllocation(uint256[] memory operators, uint256[] memory allocations) external { - modulesOperators__mocked = operators; - modulesAllocations__mocked = allocations; - } - - function getAllocation( - uint256 target - ) external view returns (uint256[] memory operators, uint256[] memory allocations) { - operators = modulesOperators__mocked; - allocations = modulesAllocations__mocked; - } - - // data by keys for specific operators - - function getOperatorAvailableKeys( - uint256[] memory operators, - uint256[] memory counts - ) external view returns (bytes memory publicKeys, bytes memory signatures) { - uint256 count; - - for (uint256 i; i < counts.length; i++) { - count += counts[i]; - } - - publicKeys = new bytes(48 * count); - signatures = new bytes(96 * count); - } - - bool private verifyKeys__mocked; - - function mock_verifyKeys(bool value) external { - verifyKeys__mocked = value; - } - - function verifyKeys(KeyData[] calldata data) external view returns (bool) { - return verifyKeys__mocked; - } - - uint256[] private allocations__mocked; - - function mock_getAllocationTopUp(uint256[] memory allocations) external { - allocations__mocked = allocations; - } - - function getAllocation( - uint256 depositAmount, - uint256[] memory operators, - uint256[] memory topUpLimits - ) external view returns (uint256[] memory allocations) { - return allocations__mocked; - } - - function onDeposit(uint256 operatorId, uint256 amount) external {} - function getType() external view returns (bytes32) { return keccak256(abi.encodePacked("staking.module")); } diff --git a/test/0.8.25/contracts/StakingRouter__Harness.sol b/test/0.8.25/contracts/StakingRouter__Harness.sol index 51311ea5fc..bb5205a22c 100644 --- a/test/0.8.25/contracts/StakingRouter__Harness.sol +++ b/test/0.8.25/contracts/StakingRouter__Harness.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.25; import {StakingRouter} from "contracts/0.8.25/sr/StakingRouter.sol"; -import {DepositsTempStorage} from "contracts/common/lib/DepositsTempStorage.sol"; import {SRLib} from "contracts/0.8.25/sr/SRLib.sol"; import {SRStorage} from "contracts/0.8.25/sr/SRStorage.sol"; import {StakingModuleStatus, ModuleStateAccounting} from "contracts/0.8.25/sr/SRTypes.sol"; @@ -33,16 +32,6 @@ contract StakingRouter__Harness is StakingRouter { uint64 _genesisTime ) StakingRouter(_depositContract, _secondsPerSlot, _genesisTime) {} - /// @notice FOR TEST: write operators & counts into the router's transient storage. - function mock_storeTemp(uint256[] calldata operators, uint256[] calldata counts) external { - DepositsTempStorage.storeOperatorCounts(operators, counts); - } - - /// @notice FOR TEST: clear temp - function mock_clearTemp() external { - DepositsTempStorage.clearOperatorCounts(); - } - /// @notice method for testing migrateUpgrade_v4 /// as version in new version will be stored in another slot, no need to set here old version /// will check migration of lido contract address and WC_01 diff --git a/test/0.8.25/stakingRouter/stakingRouter.02-keys-type.test.ts b/test/0.8.25/stakingRouter/stakingRouter.02-keys-type.test.ts index 74df6d8e14..8c2bfb0a92 100644 --- a/test/0.8.25/stakingRouter/stakingRouter.02-keys-type.test.ts +++ b/test/0.8.25/stakingRouter/stakingRouter.02-keys-type.test.ts @@ -12,7 +12,7 @@ import { StakingRouter__Harness, } from "typechain-types"; -import { ether, StakingModuleType } from "lib"; +import { ether, StakingModuleType, WithdrawalCredentialsType } from "lib"; import { Snapshot } from "test/suite"; @@ -77,6 +77,7 @@ describe("StakingRouter.sol:keys-02-type", () => { maxDepositsPerBlock, minDepositBlockDistance, moduleType: StakingModuleType.New, + withdrawalCredentialsType: WithdrawalCredentialsType.WC0x02, }; await stakingRouter.addStakingModule(name, stakingModuleAddress, stakingModuleConfig); @@ -121,11 +122,6 @@ describe("StakingRouter.sol:keys-02-type", () => { context("getStakingModuleMaxInitialDepositsAmount", () => { it("correctly returns max initial deposits amount", async () => { - // mock allocation that will return staking module of second type - // 2 keys + 2 keys + 0 + 1 - const opIds = [1, 2, 3, 4]; - const opAllocs = [ether("4096"), ether("4000"), ether("31"), ether("32")]; - await stakingModuleV2.mock_getAllocation(opIds, opAllocs); await stakingModuleV2.mock__getStakingModuleSummary(moduleId, 0n, 100n); const depositableEth = ether("10242"); diff --git a/test/0.8.25/stakingRouter/stakingRouter.exit.test.ts b/test/0.8.25/stakingRouter/stakingRouter.exit.test.ts index 2f5aeea5cc..9057c8bef7 100644 --- a/test/0.8.25/stakingRouter/stakingRouter.exit.test.ts +++ b/test/0.8.25/stakingRouter/stakingRouter.exit.test.ts @@ -6,7 +6,7 @@ import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; import { StakingModule__MockForTriggerableWithdrawals, StakingRouter__Harness } from "typechain-types"; -import { certainAddress, ether, randomString, StakingModuleType } from "lib"; +import { certainAddress, ether, randomString, StakingModuleType, WITHDRAWAL_CREDENTIALS_TYPE_01 } from "lib"; import { Snapshot } from "test/suite"; @@ -78,6 +78,7 @@ describe("StakingRouter.sol:exit", () => { /// @dev 0 = Legacy, 0x01 withdrawals, 1 = New, 0x02 withdrawals. /// @dev See {StakingModuleType} enum. moduleType: StakingModuleType.Legacy, + withdrawalCredentialsType: WITHDRAWAL_CREDENTIALS_TYPE_01, }; // Add staking module diff --git a/test/0.8.25/stakingRouter/stakingRouter.module-management.test.ts b/test/0.8.25/stakingRouter/stakingRouter.module-management.test.ts index efc355ca1e..48dc72c6fd 100644 --- a/test/0.8.25/stakingRouter/stakingRouter.module-management.test.ts +++ b/test/0.8.25/stakingRouter/stakingRouter.module-management.test.ts @@ -71,8 +71,8 @@ describe("StakingRouter.sol:module-management", () => { /// Value must be > 0 and ≤ type(uint64).max. minDepositBlockDistance: MIN_DEPOSIT_BLOCK_DISTANCE, /// @notice The type of withdrawal credentials for creation of validators. - /// @dev 1 = 0x01 withdrawals, 2 = 0x02 withdrawals. moduleType: StakingModuleType.Legacy, + withdrawalCredentialsType: WithdrawalCredentialsType.WC0x01, }; it("Reverts if the caller does not have the role", async () => { @@ -146,6 +146,7 @@ describe("StakingRouter.sol:module-management", () => { maxDepositsPerBlock: MAX_DEPOSITS_PER_BLOCK, minDepositBlockDistance: MIN_DEPOSIT_BLOCK_DISTANCE, moduleType: StakingModuleType.Legacy, + withdrawalCredentialsType: WithdrawalCredentialsType.WC0x01, }; for (let i = 0; i < MAX_STAKING_MODULES_COUNT; i++) { @@ -236,6 +237,7 @@ describe("StakingRouter.sol:module-management", () => { maxDepositsPerBlock: MAX_DEPOSITS_PER_BLOCK, minDepositBlockDistance: MIN_DEPOSIT_BLOCK_DISTANCE, moduleType: StakingModuleType.Legacy, + withdrawalCredentialsType: WithdrawalCredentialsType.WC0x01, }; beforeEach(async () => { diff --git a/test/0.8.25/stakingRouter/stakingRouter.module-sync.test.ts b/test/0.8.25/stakingRouter/stakingRouter.module-sync.test.ts index 6ff3e01ab2..1796a6e4cd 100644 --- a/test/0.8.25/stakingRouter/stakingRouter.module-sync.test.ts +++ b/test/0.8.25/stakingRouter/stakingRouter.module-sync.test.ts @@ -81,6 +81,7 @@ describe("StakingRouter.sol:module-sync", () => { maxDepositsPerBlock, minDepositBlockDistance, moduleType: StakingModuleType.Legacy, + withdrawalCredentialsType: WithdrawalCredentialsType.WC0x01, }; await stakingRouter.addStakingModule(name, stakingModuleAddress, stakingModuleConfig); diff --git a/test/0.8.25/stakingRouter/stakingRouter.rewards.test.ts b/test/0.8.25/stakingRouter/stakingRouter.rewards.test.ts index 737c47accb..abb13b4743 100644 --- a/test/0.8.25/stakingRouter/stakingRouter.rewards.test.ts +++ b/test/0.8.25/stakingRouter/stakingRouter.rewards.test.ts @@ -7,7 +7,13 @@ import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; import { StakingModule__MockForStakingRouter, StakingRouter__Harness } from "typechain-types"; import { certainAddress, ether } from "lib"; -import { getModuleMEB, StakingModuleStatus, StakingModuleType, TOTAL_BASIS_POINTS } from "lib/constants"; +import { + getModuleMEB, + StakingModuleStatus, + StakingModuleType, + TOTAL_BASIS_POINTS, + WithdrawalCredentialsType, +} from "lib/constants"; import { Snapshot } from "test/suite"; @@ -522,6 +528,7 @@ describe("StakingRouter.sol:rewards", () => { maxDepositsPerBlock, minDepositBlockDistance, moduleType, + withdrawalCredentialsType: WithdrawalCredentialsType.WC0x01, }; await stakingRouter diff --git a/test/0.8.25/stakingRouter/stakingRouter.status-control.test.ts b/test/0.8.25/stakingRouter/stakingRouter.status-control.test.ts index 9ce5b4f889..4a067ce692 100644 --- a/test/0.8.25/stakingRouter/stakingRouter.status-control.test.ts +++ b/test/0.8.25/stakingRouter/stakingRouter.status-control.test.ts @@ -7,7 +7,7 @@ import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; import { StakingRouter__Harness } from "typechain-types"; -import { certainAddress, StakingModuleType } from "lib"; +import { certainAddress, StakingModuleType, WITHDRAWAL_CREDENTIALS_TYPE_01 } from "lib"; import { Snapshot } from "test/suite"; @@ -55,6 +55,7 @@ context("StakingRouter.sol:status-control", () => { maxDepositsPerBlock: 150, minDepositBlockDistance: 25, moduleType: StakingModuleType.Legacy, + withdrawalCredentialsType: WITHDRAWAL_CREDENTIALS_TYPE_01, }; // add staking module diff --git a/test/deploy/stakingRouter.ts b/test/deploy/stakingRouter.ts index cb17b16790..df21a9817a 100644 --- a/test/deploy/stakingRouter.ts +++ b/test/deploy/stakingRouter.ts @@ -35,13 +35,11 @@ export async function deployStakingRouter( } const beaconChainDepositor = await ethers.deployContract("BeaconChainDepositor", deployer); - const depositsTempStorage = await ethers.deployContract("DepositsTempStorage", deployer); // const depositsTracker = await ethers.deployContract("DepositsTracker", deployer); const srLib = await ethers.deployContract("SRLib", deployer); const stakingRouterFactory = await ethers.getContractFactory("StakingRouter__Harness", { libraries: { ["contracts/0.8.25/lib/BeaconChainDepositor.sol:BeaconChainDepositor"]: await beaconChainDepositor.getAddress(), - ["contracts/common/lib/DepositsTempStorage.sol:DepositsTempStorage"]: await depositsTempStorage.getAddress(), // ["contracts/common/lib/DepositsTracker.sol:DepositsTracker"]: await depositsTracker.getAddress(), ["contracts/0.8.25/sr/SRLib.sol:SRLib"]: await srLib.getAddress(), },