Skip to content

Commit

Permalink
mvp1 changes draft
Browse files Browse the repository at this point in the history
  • Loading branch information
krogla committed Nov 30, 2023
1 parent cadffa4 commit 84cd96a
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 67 deletions.
64 changes: 33 additions & 31 deletions contracts/0.4.24/Lido.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,7 @@ interface IWithdrawalVault {
}

interface IStakingRouter {
function deposit(
uint256 _depositsCount,
uint256 _stakingModuleId,
bytes _depositCalldata
) external payable;
function deposit(uint256 _depositsCount, uint256 _stakingModuleId, bytes _depositCalldata) external payable;

function getStakingRewardsDistribution()
external
Expand All @@ -108,10 +104,10 @@ interface IStakingRouter {
uint16 modulesFee, uint16 treasuryFee
);

function getStakingModuleMaxDepositsCount(uint256 _stakingModuleId, uint256 _maxDepositsValue)
function getStakingModuleMaxDeposits(uint256 _stakingModuleId, uint256 _maxDepositsValue)
external
view
returns (uint256);
returns (uint256 depositsCount, uint256 depositSize);

function TOTAL_BASIS_POINTS() external view returns (uint256);
}
Expand Down Expand Up @@ -185,6 +181,9 @@ contract Lido is Versioned, StETHPermit, AragonApp {
/// @dev number of deposited validators (incrementing counter of deposit operations).
bytes32 internal constant DEPOSITED_VALIDATORS_POSITION =
0xe6e35175eb53fc006520a2a9c3e9711a7c00de6ff2c32dd31df8c5a24cac1b5c; // keccak256("lido.Lido.depositedValidators");
bytes32 internal constant DEPOSITED_BALANCE_POSITION =
0x824b553776062cb6506c204b2d4c4bfb9bcc04d85f2cb247e68b2ff456314036; // keccak256("lido.Lido.depositedBalance");

/// @dev total amount of ether on Consensus Layer (sum of all the balances of Lido validators)
// "beacon" in the `keccak256()` parameter is staying here for compatibility reason
bytes32 internal constant CL_BALANCE_POSITION =
Expand Down Expand Up @@ -213,9 +212,9 @@ contract Lido is Versioned, StETHPermit, AragonApp {
uint256 postCLValidators
);

// Emits when var at `DEPOSITED_VALIDATORS_POSITION` changed
event DepositedValidatorsChanged(
uint256 depositedValidators
// Emits when var at `DEPOSITED_VALIDATORS_BALANCE_POSITION` changed
event DepositedBalanceChanged(
uint256 depositedValidatorsBalance
);

// Emits when oracle accounting report processed
Expand Down Expand Up @@ -293,6 +292,8 @@ contract Lido is Versioned, StETHPermit, AragonApp {
emit LidoLocatorSet(_lidoLocator);
}

//@todo _initialize_v3

/**
* @notice A function to finalize upgrade to v2 (from v1). Can be called only once
* @dev Value "1" in CONTRACT_VERSION_POSITION is skipped due to change in numbering
Expand All @@ -313,6 +314,8 @@ contract Lido is Versioned, StETHPermit, AragonApp {
_initialize_v2(_lidoLocator, _eip712StETH);
}

//@todo finalizeUpgrade_v3

/**
* @notice Stops accepting new Ether to the protocol
*
Expand Down Expand Up @@ -608,14 +611,14 @@ contract Lido is Versioned, StETHPermit, AragonApp {
* Can be required when onboarding external validators to Lido
* (i.e., had deposited before and rotated their type-0x00 withdrawal credentials to Lido)
*
* @param _newDepositedValidators new value
* @param _newDepositedBalance new value
*/
function unsafeChangeDepositedValidators(uint256 _newDepositedValidators) external {
function unsafeChangeDepositedBalance(uint256 _newDepositedBalance) external {
_auth(UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE);

DEPOSITED_VALIDATORS_POSITION.setStorageUint256(_newDepositedValidators);
DEPOSITED_BALANCE_POSITION.setStorageUint256(_newDepositedBalance);

emit DepositedValidatorsChanged(_newDepositedValidators);
emit DepositedBalanceChanged(_newDepositedBalance);
}

/**
Expand Down Expand Up @@ -655,15 +658,15 @@ contract Lido is Versioned, StETHPermit, AragonApp {

/**
* @notice Returns the key values related to Consensus Layer side of the contract. It historically contains beacon
* @return depositedValidators - number of deposited validators from Lido contract side
* @return depositedBalance - total balance of deposited validators from Lido contract side
* @return beaconValidators - number of Lido validators visible on Consensus Layer, reported by oracle
* @return beaconBalance - total amount of ether on the Consensus Layer side (sum of all the balances of Lido validators)
*
* @dev `beacon` in naming still here for historical reasons
*/
function getBeaconStat() external view returns (uint256 depositedValidators, uint256 beaconValidators, uint256 beaconBalance) {
depositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256();
beaconValidators = CL_VALIDATORS_POSITION.getStorageUint256();
function getBeaconStat() external view returns (uint256 depositedBalance, uint256, uint256 beaconBalance) {
depositedBalance = DEPOSITED_BALANCE_POSITION.getStorageUint256();
// beaconValidators = CL_VALIDATORS_POSITION.getStorageUint256();
beaconBalance = CL_BALANCE_POSITION.getStorageUint256();
}

Expand All @@ -687,7 +690,7 @@ contract Lido is Versioned, StETHPermit, AragonApp {

/**
* @dev Invokes a deposit call to the Staking Router contract and updates buffered counters
* @param _maxDepositsCount max deposits count
* @param _maxDepositsCount max deposits value for module
* @param _stakingModuleId id of the staking module to be deposited
* @param _depositCalldata module calldata
*/
Expand All @@ -698,22 +701,21 @@ contract Lido is Versioned, StETHPermit, AragonApp {
require(canDeposit(), "CAN_NOT_DEPOSIT");

IStakingRouter stakingRouter = _stakingRouter();
uint256 depositsCount = Math256.min(
_maxDepositsCount,
stakingRouter.getStakingModuleMaxDepositsCount(_stakingModuleId, getDepositableEther())
);

(uint256 depositsCount, uint256 depositSize) = stakingRouter.getStakingModuleMaxDeposits(_stakingModuleId, getDepositableEther());
depositsCount = Math256.min(_maxDepositsCount, depositsCount);

uint256 depositsValue;
if (depositsCount > 0) {
depositsValue = depositsCount.mul(DEPOSIT_SIZE);
depositsValue = depositsCount.mul(depositSize);
/// @dev firstly update the local state of the contract to prevent a reentrancy attack,
/// even if the StakingRouter is a trusted contract.
BUFFERED_ETHER_POSITION.setStorageUint256(_getBufferedEther().sub(depositsValue));
emit Unbuffered(depositsValue);

uint256 newDepositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256().add(depositsCount);
DEPOSITED_VALIDATORS_POSITION.setStorageUint256(newDepositedValidators);
emit DepositedValidatorsChanged(newDepositedValidators);
uint256 newDepositedBalance = DEPOSITED_BALANCE_POSITION.getStorageUint256().add(depositsValue);
DEPOSITED_BALANCE_POSITION.setStorageUint256(newDepositedBalance);
emit DepositedBalanceChanged(newDepositedBalance);
}

/// @dev transfer ether to StakingRouter and make a deposit at the same time. All the ether
Expand Down Expand Up @@ -1092,11 +1094,11 @@ contract Lido is Versioned, StETHPermit, AragonApp {
/// i.e. submitted to the official Deposit contract but not yet visible in the CL state.
/// @return transient balance in wei (1e-18 Ether)
function _getTransientBalance() internal view returns (uint256) {
uint256 depositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256();
uint256 clValidators = CL_VALIDATORS_POSITION.getStorageUint256();
uint256 depositedBalance = DEPOSITED_BALANCE_POSITION.getStorageUint256();
uint256 clBalance = CL_BALANCE_POSITION.getStorageUint256();
// clValidators can never be less than deposited ones.
assert(depositedValidators >= clValidators);
return (depositedValidators - clValidators).mul(DEPOSIT_SIZE);
assert(depositedBalance >= clBalance);
return depositedBalance - clBalance;
}

/**
Expand Down
69 changes: 41 additions & 28 deletions contracts/0.8.9/StakingRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Version
uint16 treasuryFee;
uint16 targetShare;
StakingModuleStatus status;
uint256 activeValidatorsCount;
uint256 activeValidatorsBalance;
uint256 availableValidatorsCount;
uint256 depositSize;
}

bytes32 public constant MANAGE_WITHDRAWAL_CREDENTIALS_ROLE = keccak256("MANAGE_WITHDRAWAL_CREDENTIALS_ROLE");
Expand Down Expand Up @@ -858,6 +859,7 @@ contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Version
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
if (StakingModuleStatus(stakingModule.status) == _status)
revert StakingModuleStatusTheSame();
}
_setStakingModuleStatus(stakingModule, _status);
}

Expand Down Expand Up @@ -938,20 +940,21 @@ contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Version
/// on the passed `_maxDepositsValue` amount
/// @param _stakingModuleId id of the staking module to be deposited
/// @param _maxDepositsValue max amount of ether that might be used for deposits count calculation
/// @return max number of deposits might be done using the given staking module
function getStakingModuleMaxDepositsCount(uint256 _stakingModuleId, uint256 _maxDepositsValue)
/// @return depositsCount max number of deposits might be done using the given staking module
/// depositSize deposit size value set for module
function getStakingModuleMaxDeposits(uint256 _stakingModuleId, uint256 _maxDepositsValue)
public
view
returns (uint256)
returns (uint256 depositsCount, uint256 depositSize)
{
(
/* uint256 allocated */,
uint256[] memory newDepositsAllocation,
StakingModuleCache[] memory stakingModulesCache
) = _getDepositsAllocation(_maxDepositsValue / DEPOSIT_SIZE);
) = _getDepositsAllocation(_maxDepositsValue);
uint256 stakingModuleIndex = _getStakingModuleIndexById(_stakingModuleId);
return
newDepositsAllocation[stakingModuleIndex] - stakingModulesCache[stakingModuleIndex].activeValidatorsCount;
depositSize = stakingModulesCache[stakingModuleIndex].depositSize;
depositsCount = (newDepositsAllocation[stakingModuleIndex] - stakingModulesCache[stakingModuleIndex].activeValidatorsBalance) / depositSize;
}

/**
Expand Down Expand Up @@ -1058,7 +1061,7 @@ contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Version
/// reduced, 1e4 precision.
function getTotalFeeE4Precision() external view returns (uint16 totalFee) {
/// @dev The logic is placed here but in Lido contract to save Lido bytecode
(, , , uint96 totalFeeInHighPrecision, uint256 precision) = getStakingRewardsDistribution();
(,,, uint96 totalFeeInHighPrecision, uint256 precision) = getStakingRewardsDistribution();
// Here we rely on (totalFeeInHighPrecision <= precision)
totalFee = _toE4Precision(totalFeeInHighPrecision, precision);
}
Expand All @@ -1082,8 +1085,8 @@ contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Version
}

/// @notice returns new deposits allocation after the distribution of the `_depositsCount` deposits
function getDepositsAllocation(uint256 _depositsCount) external view returns (uint256 allocated, uint256[] memory allocations) {
(allocated, allocations, ) = _getDepositsAllocation(_depositsCount);
function getDepositsAllocation(uint256 _depositsAmount) external view returns (uint256 allocated, uint256[] memory allocations) {
(allocated, allocations,) = _getDepositsAllocation(_depositsAmount);
}

/// @dev Invokes a deposit call to the official Deposit contract
Expand Down Expand Up @@ -1111,9 +1114,11 @@ contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Version

uint256 depositsValue = msg.value;
emit StakingRouterETHDeposited(_stakingModuleId, depositsValue);
uint256 depositSize = _getStakingModuleDepositSizrById(_stakingModuleId);

if (depositsValue != _depositsCount * DEPOSIT_SIZE)
if (depositsValue != _depositsCount * depositSize) {
revert InvalidDepositsValue(depositsValue, _depositsCount);
}

if (_depositsCount > 0) {
(bytes memory publicKeysBatch, bytes memory signaturesBatch) =
Expand Down Expand Up @@ -1190,17 +1195,17 @@ contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Version

/// @dev load modules into a memory cache
///
/// @return totalActiveValidators total active validators across all modules
/// @return totalActiveValidatorsBalance total active validators across all modules
/// @return stakingModulesCache array of StakingModuleCache structs
function _loadStakingModulesCache() internal view returns (
uint256 totalActiveValidators,
uint256 totalActiveValidatorsBalance,
StakingModuleCache[] memory stakingModulesCache
) {
uint256 stakingModulesCount = getStakingModulesCount();
stakingModulesCache = new StakingModuleCache[](stakingModulesCount);
for (uint256 i; i < stakingModulesCount; ) {
stakingModulesCache[i] = _loadStakingModulesCacheItem(i);
totalActiveValidators += stakingModulesCache[i].activeValidatorsCount;
totalActiveValidatorsBalance += stakingModulesCache[i].activeValidatorsBalance;
unchecked {
++i;
}
Expand All @@ -1220,16 +1225,15 @@ contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Version
cacheItem.treasuryFee = stakingModuleData.treasuryFee;
cacheItem.targetShare = stakingModuleData.targetShare;
cacheItem.status = StakingModuleStatus(stakingModuleData.status);
cacheItem.depositSize = _getStakingModuleDepositSizrById(stakingModuleData.id);

(
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
uint256 depositableValidatorsCount
) = IStakingModule(cacheItem.stakingModuleAddress).getStakingModuleSummary();

cacheItem.availableValidatorsCount = cacheItem.status == StakingModuleStatus.Active
? depositableValidatorsCount
: 0;
cacheItem.availableValidatorsCount = cacheItem.status == StakingModuleStatus.Active ? depositableValidatorsCount : 0;

// the module might not receive all exited validators data yet => we need to replacing
// the exitedValidatorsCount with the one that the staking router is aware of
Expand All @@ -1247,31 +1251,36 @@ contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Version
}

function _getDepositsAllocation(
uint256 _depositsToAllocate
uint256 _depositsAmountToAllocate
) internal view returns (uint256 allocated, uint256[] memory allocations, StakingModuleCache[] memory stakingModulesCache) {
// calculate total used validators for operators
uint256 totalActiveValidators;
uint256 totalActiveValidatorsBalance;

(totalActiveValidators, stakingModulesCache) = _loadStakingModulesCache();
(totalActiveValidatorsBalance, stakingModulesCache) = _loadStakingModulesCache();

uint256 stakingModulesCount = stakingModulesCache.length;
allocations = new uint256[](stakingModulesCount);
if (stakingModulesCount > 0) {
/// @dev new estimated active validators count
totalActiveValidators += _depositsToAllocate;
totalActiveValidatorsBalance += _depositsAmountToAllocate;
uint256[] memory capacities = new uint256[](stakingModulesCount);
uint256 targetValidators;

for (uint256 i; i < stakingModulesCount; ) {
allocations[i] = stakingModulesCache[i].activeValidatorsCount;
targetValidators = (stakingModulesCache[i].targetShare * totalActiveValidators) / TOTAL_BASIS_POINTS;
capacities[i] = Math256.min(targetValidators, stakingModulesCache[i].activeValidatorsCount + stakingModulesCache[i].availableValidatorsCount);
uint256 targetValidatorsBalance;

for (uint256 i; i < stakingModulesCount;) {
allocations[i] = stakingModulesCache[i].activeValidatorsBalance;
// rounding to per module depositSize
// targetValidatorsBalance = Math256.round((stakingModulesCache[i].targetShare * totalActiveValidatorsBalance) / TOTAL_BASIS_POINTS, stakingModulesCache[i].depositSize);
targetValidatorsBalance = (stakingModulesCache[i].targetShare * totalActiveValidatorsBalance) / TOTAL_BASIS_POINTS;
capacities[i] = Math256.min(
targetValidatorsBalance,
stakingModulesCache[i].activeValidatorsBalance + (stakingModulesCache[i].availableValidatorsCount * stakingModulesCache[i].depositSize)
);
unchecked {
++i;
}
}

allocated = MinFirstAllocationStrategy.allocate(allocations, capacities, _depositsToAllocate);
allocated = MinFirstAllocationStrategy.allocate(allocations, capacities, _depositsAmountToAllocate);
}
}

Expand Down Expand Up @@ -1300,6 +1309,10 @@ contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Version
return _getStakingModuleById(_stakingModuleId).stakingModuleAddress;
}

function _getStakingModuleDepositSizrById(uint256 _stakingModuleId) internal pure returns (uint256) {
return DEPOSIT_SIZE;
}

function _getStorageStakingModulesMapping() internal pure returns (mapping(uint256 => StakingModule) storage result) {
bytes32 position = STAKING_MODULES_MAPPING_POSITION;
assembly {
Expand Down
Loading

0 comments on commit 84cd96a

Please sign in to comment.