Skip to content

Commit

Permalink
feat: sync feat/vaults
Browse files Browse the repository at this point in the history
  • Loading branch information
loga4 committed Jan 16, 2025
2 parents f52348a + 73b10cc commit 455c189
Show file tree
Hide file tree
Showing 31 changed files with 983 additions and 583 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ SEPOLIA_RPC_URL=

# RPC URL for Hardhat Network forking, required for running tests on mainnet fork with tracing (Infura, Alchemy, etc.)
# https://hardhat.org/hardhat-network/docs/guides/forking-other-networks#forking-other-networks
HARDHAT_FORKING_URL=
HARDHAT_FORKING_URL=https://eth.drpc.org

# Scratch deployment via hardhat variables
DEPLOYER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/tests-integration-mainnet.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Integration Tests

#on: [push]
#
#jobs:
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ mainnet environment, allowing you to run integration tests with trace logging.
> [!NOTE]
> Ensure that `HARDHAT_FORKING_URL` is set to Ethereum Mainnet RPC and `MAINNET_*` environment variables are set in the
> `.env` file (refer to `.env.example` for guidance).
> `.env` file (refer to `.env.example` for guidance). Otherwise, the tests will run against the Scratch deployment.
```bash
# Run all integration tests
Expand Down
17 changes: 16 additions & 1 deletion contracts/0.4.24/Lido.sol
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ contract Lido is Versioned, StETHPermit, AragonApp {
* @param _eip712StETH eip712 helper contract for StETH
*/
function initialize(address _lidoLocator, address _eip712StETH) public payable onlyInit {
_bootstrapInitialHolder();
_bootstrapInitialHolder(); // stone in the elevator

LIDO_LOCATOR_POSITION.setStorageAddress(_lidoLocator);
emit LidoLocatorSet(_lidoLocator);
Expand Down Expand Up @@ -947,6 +947,21 @@ contract Lido is Versioned, StETHPermit, AragonApp {
return internalEther.add(_getExternalEther(internalEther));
}

/// @dev the numerator (in ether) of the share rate for StETH conversion between shares and ether and vice versa.
/// using the numerator and denominator different from totalShares and totalPooledEther allows to:
/// - avoid double precision loss on additional division on external ether calculations
/// - optimize gas cost of conversions between shares and ether
function _getShareRateNumerator() internal view returns (uint256) {
return _getInternalEther();
}

/// @dev the denominator (in shares) of the share rate for StETH conversion between shares and ether and vice versa.
function _getShareRateDenominator() internal view returns (uint256) {
uint256 externalShares = EXTERNAL_SHARES_POSITION.getStorageUint256();
uint256 internalShares = _getTotalShares() - externalShares; // never 0 because of the stone in the elevator
return internalShares;
}

/// @notice Calculate the maximum amount of external shares that can be minted while maintaining
/// maximum allowed external ratio limits
/// @return Maximum amount of external shares that can be minted
Expand Down
34 changes: 25 additions & 9 deletions contracts/0.4.24/StETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -303,17 +303,17 @@ contract StETH is IERC20, Pausable {
*/
function getSharesByPooledEth(uint256 _ethAmount) public view returns (uint256) {
return _ethAmount
.mul(_getTotalShares())
.div(_getTotalPooledEther());
.mul(_getShareRateDenominator()) // denominator in shares
.div(_getShareRateNumerator()); // numerator in ether
}

/**
* @return the amount of ether that corresponds to `_sharesAmount` token shares.
*/
function getPooledEthByShares(uint256 _sharesAmount) public view returns (uint256) {
return _sharesAmount
.mul(_getTotalPooledEther())
.div(_getTotalShares());
.mul(_getShareRateNumerator()) // numerator in ether
.div(_getShareRateDenominator()); // denominator in shares
}

/**
Expand All @@ -322,14 +322,14 @@ contract StETH is IERC20, Pausable {
* for `shareRate >= 0.5`, `getSharesByPooledEth(getPooledEthBySharesRoundUp(1))` will be 1.
*/
function getPooledEthBySharesRoundUp(uint256 _sharesAmount) public view returns (uint256 etherAmount) {
uint256 totalEther = _getTotalPooledEther();
uint256 totalShares = _getTotalShares();
uint256 numeratorInEther = _getShareRateNumerator();
uint256 denominatorInShares = _getShareRateDenominator();

etherAmount = _sharesAmount
.mul(totalEther)
.div(totalShares);
.mul(numeratorInEther)
.div(denominatorInShares);

if (etherAmount.mul(totalShares) != _sharesAmount.mul(totalEther)) {
if (_sharesAmount.mul(numeratorInEther) != etherAmount.mul(denominatorInShares)) {
++etherAmount;
}
}
Expand Down Expand Up @@ -389,6 +389,22 @@ contract StETH is IERC20, Pausable {
*/
function _getTotalPooledEther() internal view returns (uint256);

/**
* @return the numerator of the protocol's share rate (in ether).
* @dev used to convert shares to tokens and vice versa.
*/
function _getShareRateNumerator() internal view returns (uint256) {
return _getTotalPooledEther();
}

/**
* @return the denominator of the protocol's share rate (in shares).
* @dev used to convert shares to tokens and vice versa.
*/
function _getShareRateDenominator() internal view returns (uint256) {
return _getTotalShares();
}

/**
* @notice Moves `_amount` tokens from `_sender` to `_recipient`.
* Emits a `Transfer` event.
Expand Down
51 changes: 51 additions & 0 deletions contracts/0.8.25/utils/PausableUntilWithRoles.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2025 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

// See contracts/COMPILERS.md
pragma solidity 0.8.25;

import {PausableUntil} from "contracts/common/utils/PausableUntil.sol";
import {AccessControlEnumerableUpgradeable} from "contracts/openzeppelin/5.0.2/upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol";

/**
* @title PausableUntilWithRoles
* @notice a `PausableUntil` implementation using OpenZeppelin's `AccessControlEnumerableUpgradeable`
* @dev the inheriting contract must use `whenNotPaused` modifier from `PausableUntil` to block some functions on pause
*/
abstract contract PausableUntilWithRoles is PausableUntil, AccessControlEnumerableUpgradeable {
/// @notice role that allows to pause the contract
bytes32 public constant PAUSE_ROLE = keccak256("PausableUntilWithRoles.PauseRole");
/// @notice role that allows to resume the contract
bytes32 public constant RESUME_ROLE = keccak256("PausableUntilWithRoles.ResumeRole");

/**
* @notice Resume the contract
* @dev Reverts if contracts is not paused
* @dev Reverts if sender has no `RESUME_ROLE`
*/
function resume() external onlyRole(RESUME_ROLE) {
_resume();
}

/**
* @notice Pause the contract for a specified period
* @param _duration pause duration in seconds (use `PAUSE_INFINITELY` for unlimited)
* @dev Reverts if contract is already paused
* @dev Reverts if sender has no `PAUSE_ROLE`
* @dev Reverts if zero duration is passed
*/
function pauseFor(uint256 _duration) external onlyRole(PAUSE_ROLE) {
_pauseFor(_duration);
}

/**
* @notice Pause the contract until a specified timestamp
* @param _pauseUntilInclusive the last second to pause until inclusive
* @dev Reverts if the timestamp is in the past
* @dev Reverts if sender has no `PAUSE_ROLE`
* @dev Reverts if contract is already paused
*/
function pauseUntil(uint256 _pauseUntilInclusive) external onlyRole(PAUSE_ROLE) {
_pauseUntil(_pauseUntilInclusive);
}
}
12 changes: 5 additions & 7 deletions contracts/0.8.25/vaults/Dashboard.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ import {Clones} from "@openzeppelin/contracts-v5.2.0/proxy/Clones.sol";

import {Math256} from "contracts/common/lib/Math256.sol";


import {VaultHub} from "./VaultHub.sol";


import {IStakingVault} from "./interfaces/IStakingVault.sol";
import {ILido as IStETH} from "../interfaces/ILido.sol";

Expand Down Expand Up @@ -47,9 +45,6 @@ contract Dashboard is AccessControlEnumerable {
/// @notice Total basis points for fee calculations; equals to 100%.
uint256 internal constant TOTAL_BASIS_POINTS = 10000;

/// @notice Indicates whether the contract has been initialized
bool public isInitialized;

/// @notice The stETH token contract
IStETH public immutable STETH;

Expand All @@ -59,6 +54,9 @@ contract Dashboard is AccessControlEnumerable {
/// @notice The wrapped ether token contract
IWeth public immutable WETH;

/// @notice Indicates whether the contract has been initialized
bool public initialized;

/// @notice The `VaultHub` contract
VaultHub public vaultHub;

Expand Down Expand Up @@ -99,10 +97,10 @@ contract Dashboard is AccessControlEnumerable {
* @dev Internal initialize function.
*/
function _initialize() internal {
if (isInitialized) revert AlreadyInitialized();
if (initialized) revert AlreadyInitialized();
if (address(this) == _SELF) revert NonProxyCallsForbidden();

isInitialized = true;
initialized = true;
vaultHub = VaultHub(stakingVault().vaultHub());
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

Expand Down
Loading

0 comments on commit 455c189

Please sign in to comment.