Skip to content

Commit

Permalink
Merge branch 'feat-immutable-operator-in-vault' into feat/factory-upg…
Browse files Browse the repository at this point in the history
…rade

# Conflicts:
#	test/0.8.25/vaults/dashboard/dashboard.test.ts
#	test/0.8.25/vaults/delegation/delegation.test.ts
#	test/0.8.25/vaults/staking-vault/staking-vault.test.ts
  • Loading branch information
tamtamchik committed Dec 17, 2024
2 parents 8c6bfd4 + 496e6f2 commit fac38f8
Show file tree
Hide file tree
Showing 123 changed files with 11,545 additions and 4,823 deletions.
11 changes: 5 additions & 6 deletions .github/workflows/analyse.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: Analysis

on: [pull_request]
on:
pull_request:
push:
branches: [master]

jobs:
slither:
Expand Down Expand Up @@ -37,11 +40,7 @@ jobs:
- name: Run slither
run: >
poetry run slither . \
--no-fail-pedantic \
--compile-force-framework hardhat \
--sarif results.sarif \
--exclude pess-strange-setter,pess-arbitrary-call-calldata-tainted
poetry run slither . --no-fail-pedantic --sarif results.sarif
- name: Check results.sarif presence
id: results
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ jobs:
with:
path: ./coverage/cobertura-coverage.xml
publish: true
threshold: 95
# TODO: restore to 95% before release
threshold: 80
diff: true
diff-branch: master
diff-storage: _core_coverage_reports
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/tests-integration-scratch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ jobs:
run: yarn test:integration:fork:local
env:
LOG_LEVEL: "debug"
INTEGRATION_WITH_CSM: "off"
109 changes: 62 additions & 47 deletions contracts/0.4.24/Lido.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ contract Lido is Versioned, StETHPermit, AragonApp {
/// @dev Just a counter of total amount of execution layer rewards received by Lido contract. Not used in the logic.
bytes32 internal constant TOTAL_EL_REWARDS_COLLECTED_POSITION =
0xafe016039542d12eec0183bb0b1ffc2ca45b027126a494672fba4154ee77facb; // keccak256("lido.Lido.totalELRewardsCollected");
/// @dev amount of external balance that is counted into total pooled eth
/// @dev amount of external balance that is counted into total protocol pooled ether
bytes32 internal constant EXTERNAL_BALANCE_POSITION =
0xc5293dc5c305f507c944e5c29ae510e33e116d6467169c2daa1ee0db9af5b91d; // keccak256("lido.Lido.externalBalance");
/// @dev maximum allowed external balance as basis points of total pooled ether
/// @dev maximum allowed external balance as basis points of total protocol pooled ether
/// this is a soft limit (can eventually hit the limit as a part of rebase)
bytes32 internal constant MAX_EXTERNAL_BALANCE_POSITION =
0x5248bc99214b4b9bfb04eed7603bdab7b47ab5b436236fcbf7bda3acc9aea148; // keccak256("lido.Lido.maxExternalBalanceBP")
Expand Down Expand Up @@ -375,14 +375,17 @@ contract Lido is Versioned, StETHPermit, AragonApp {
prevStakeBlockNumber = stakeLimitData.prevStakeBlockNumber;
}

/**
* @notice Sets the maximum allowed external balance as basis points of total pooled ether
* @param _maxExternalBalanceBP The maximum basis points [0-10000]
*/
/// @return max external balance in basis points
function getMaxExternalBalanceBP() external view returns (uint256) {
return MAX_EXTERNAL_BALANCE_POSITION.getStorageUint256();
}

/// @notice Sets the maximum allowed external balance as basis points of total pooled ether
/// @param _maxExternalBalanceBP The maximum basis points [0-10000]
function setMaxExternalBalanceBP(uint256 _maxExternalBalanceBP) external {
_auth(STAKING_CONTROL_ROLE);

require(_maxExternalBalanceBP >= 0 && _maxExternalBalanceBP <= TOTAL_BASIS_POINTS, "INVALID_MAX_EXTERNAL_BALANCE");
require(_maxExternalBalanceBP <= TOTAL_BASIS_POINTS, "INVALID_MAX_EXTERNAL_BALANCE");

MAX_EXTERNAL_BALANCE_POSITION.setStorageUint256(_maxExternalBalanceBP);

Expand Down Expand Up @@ -492,12 +495,10 @@ contract Lido is Versioned, StETHPermit, AragonApp {
return EXTERNAL_BALANCE_POSITION.getStorageUint256();
}

/**
* @notice Get the maximum allowed external ether balance
* @return max external balance in wei
*/
function getMaxExternalEther() external view returns (uint256) {
return _getMaxExternalEther();
/// @notice Get the maximum additional stETH amount that can be added to external balance without exceeding limits
/// @return Maximum stETH amount that can be added to external balance
function getMaxAvailableExternalBalance() external view returns (uint256) {
return _getMaxAvailableExternalBalance();
}

/**
Expand Down Expand Up @@ -621,19 +622,17 @@ contract Lido is Versioned, StETHPermit, AragonApp {
///
/// @param _receiver Address to receive the minted shares
/// @param _amountOfShares Amount of shares to mint
/// @return stethAmount The amount of stETH minted
/// @dev can be called only by accounting (authentication in mintShares method)
/// @dev Can be called only by accounting (authentication in mintShares method).
/// NB: Reverts if the the external balance limit is exceeded.
function mintExternalShares(address _receiver, uint256 _amountOfShares) external {
require(_receiver != address(0), "MINT_RECEIVER_ZERO_ADDRESS");
require(_amountOfShares != 0, "MINT_ZERO_AMOUNT_OF_SHARES");
_whenNotStakingPaused();

uint256 stethAmount = super.getPooledEthByShares(_amountOfShares);

uint256 newExternalBalance = EXTERNAL_BALANCE_POSITION.getStorageUint256().add(stethAmount);
uint256 maxExternalBalance = _getMaxExternalEther();
// TODO: separate role and flag for external shares minting pause
require(!STAKING_STATE_POSITION.getStorageStakeLimitStruct().isStakingPaused(), "STAKING_PAUSED");

require(newExternalBalance <= maxExternalBalance, "EXTERNAL_BALANCE_LIMIT_EXCEEDED");
uint256 stethAmount = super.getPooledEthByShares(_amountOfShares);
uint256 newExternalBalance = _getNewExternalBalance(stethAmount);

EXTERNAL_BALANCE_POSITION.setStorageUint256(newExternalBalance);

Expand All @@ -648,7 +647,6 @@ contract Lido is Versioned, StETHPermit, AragonApp {
function burnExternalShares(uint256 _amountOfShares) external {
require(_amountOfShares != 0, "BURN_ZERO_AMOUNT_OF_SHARES");
_auth(getLidoLocator().accounting());
_whenNotStakingPaused();

uint256 stethAmount = super.getPooledEthByShares(_amountOfShares);
uint256 extBalance = EXTERNAL_BALANCE_POSITION.getStorageUint256();
Expand Down Expand Up @@ -914,31 +912,54 @@ contract Lido is Versioned, StETHPermit, AragonApp {
}

/**
* @dev Gets the maximum allowed external balance as basis points of total pooled ether
* @return max external balance in wei
*/
function _getMaxExternalEther() internal view returns (uint256) {
return _getPooledEther()
.mul(MAX_EXTERNAL_BALANCE_POSITION.getStorageUint256())
.div(TOTAL_BASIS_POINTS);
}

/**
* @dev Gets the total amount of Ether controlled by the protocol
* @dev Gets the total amount of Ether controlled by the protocol and external entities
* @return total balance in wei
*/
function _getPooledEther() internal view returns (uint256) {
function _getTotalPooledEther() internal view returns (uint256) {
return _getBufferedEther()
.add(CL_BALANCE_POSITION.getStorageUint256())
.add(_getTransientBalance());
.add(_getTransientBalance())
.add(EXTERNAL_BALANCE_POSITION.getStorageUint256());
}

/**
* @dev Gets the total amount of Ether controlled by the protocol and external entities
* @return total balance in wei
*/
function _getTotalPooledEther() internal view returns (uint256) {
return _getPooledEther().add(EXTERNAL_BALANCE_POSITION.getStorageUint256());
/// @notice Calculates the maximum amount of ether that can be added to the external balance while maintaining
/// maximum allowed external balance limits for the protocol pooled ether
/// @return Maximum amount of ether that can be safely added to external balance
/// @dev This function enforces the ratio between external and protocol balance to stay below a limit.
/// The limit is defined by some maxBP out of totalBP.
///
/// The calculation ensures: (external + x) / (totalPooled + x) <= maxBP / totalBP
/// Which gives formula: x <= (maxBP * totalPooled - external * totalBP) / (totalBP - maxBP)
///
/// Special cases:
/// - Returns 0 if maxBP is 0 (external balance disabled) or external balance already exceeds the limit
/// - Returns uint256(-1) if maxBP >= totalBP (no limit)
function _getMaxAvailableExternalBalance() internal view returns (uint256) {
uint256 maxBP = MAX_EXTERNAL_BALANCE_POSITION.getStorageUint256();
uint256 externalBalance = EXTERNAL_BALANCE_POSITION.getStorageUint256();
uint256 totalPooledEther = _getTotalPooledEther();

if (maxBP == 0) return 0;
if (maxBP >= TOTAL_BASIS_POINTS) return uint256(-1);
if (externalBalance.mul(TOTAL_BASIS_POINTS) > totalPooledEther.mul(maxBP)) return 0;

return (maxBP.mul(totalPooledEther).sub(externalBalance.mul(TOTAL_BASIS_POINTS)))
.div(TOTAL_BASIS_POINTS.sub(maxBP));
}

/// @notice Calculates the new external balance after adding stETH and validates against maximum limit
///
/// @param _stethAmount The amount of stETH being added to external balance
/// @return The new total external balance after adding _stethAmount
/// @dev Validates that the new external balance would not exceed the maximum allowed amount
/// by comparing with _getMaxAvailableExternalBalance
function _getNewExternalBalance(uint256 _stethAmount) internal view returns (uint256) {
uint256 currentExternal = EXTERNAL_BALANCE_POSITION.getStorageUint256();
uint256 maxAmountToAdd = _getMaxAvailableExternalBalance();

require(_stethAmount <= maxAmountToAdd, "EXTERNAL_BALANCE_LIMIT_EXCEEDED");

return currentExternal.add(_stethAmount);
}

function _pauseStaking() internal {
Expand Down Expand Up @@ -1013,10 +1034,4 @@ contract Lido is Versioned, StETHPermit, AragonApp {
_mintInitialShares(balance);
}
}

// There is an invariant that protocol pause also implies staking pause.
// Thus, no need to check protocol pause explicitly.
function _whenNotStakingPaused() internal view {
require(!STAKING_STATE_POSITION.getStorageStakeLimitStruct().isStakingPaused(), "STAKING_PAUSED");
}
}
Loading

0 comments on commit fac38f8

Please sign in to comment.