Skip to content

Commit

Permalink
Merge pull request #901 from lidofinance/feat/factory-upgrade
Browse files Browse the repository at this point in the history
feat: factory upgrade
  • Loading branch information
loga4 authored Jan 21, 2025
2 parents deb0e47 + 3019051 commit 292920a
Show file tree
Hide file tree
Showing 37 changed files with 424 additions and 367 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ LOCAL_STAKING_ROUTER_ADDRESS=
LOCAL_VALIDATORS_EXIT_BUS_ORACLE_ADDRESS=
LOCAL_WITHDRAWAL_QUEUE_ADDRESS=
LOCAL_WITHDRAWAL_VAULT_ADDRESS=
LOCAL_STAKING_VAULT_FACTORY_ADDRESS=
LOCAL_STAKING_VAULT_BEACON_ADDRESS=

# RPC URL for a separate, non Hardhat Network node (Anvil, Infura, Alchemy, etc.)
MAINNET_RPC_URL=http://localhost:8545
Expand All @@ -46,6 +48,8 @@ MAINNET_STAKING_ROUTER_ADDRESS=
MAINNET_VALIDATORS_EXIT_BUS_ORACLE_ADDRESS=
MAINNET_WITHDRAWAL_QUEUE_ADDRESS=
MAINNET_WITHDRAWAL_VAULT_ADDRESS=
MAINNET_STAKING_VAULT_FACTORY_ADDRESS=
MAINNET_STAKING_VAULT_BEACON_ADDRESS=

HOLESKY_RPC_URL=
SEPOLIA_RPC_URL=
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/tests-integration-mainnet.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
name: Integration Tests

#on: [push]
#
#jobs:
Expand Down
4 changes: 2 additions & 2 deletions contracts/0.8.25/interfaces/ILido.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
// See contracts/COMPILERS.md
pragma solidity 0.8.25;

import {IERC20} from "@openzeppelin/contracts-v5.0.2/token/ERC20/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts-v5.0.2/token/ERC20/extensions/IERC20Permit.sol";
import {IERC20} from "@openzeppelin/contracts-v5.2/token/ERC20/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts-v5.2/token/ERC20/extensions/IERC20Permit.sol";

interface ILido is IERC20, IERC20Permit {
function getSharesByPooledEth(uint256) external view returns (uint256);
Expand Down
2 changes: 1 addition & 1 deletion contracts/0.8.25/utils/PausableUntilWithRoles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
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";
import {AccessControlEnumerableUpgradeable} from "contracts/openzeppelin/5.2/upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol";

/**
* @title PausableUntilWithRoles
Expand Down
79 changes: 42 additions & 37 deletions contracts/0.8.25/vaults/Dashboard.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
// See contracts/COMPILERS.md
pragma solidity 0.8.25;

import {AccessControlEnumerable} from "@openzeppelin/contracts-v5.0.2/access/extensions/AccessControlEnumerable.sol";
import {OwnableUpgradeable} from "contracts/openzeppelin/5.0.2/upgradeable/access/OwnableUpgradeable.sol";
import {IERC20} from "@openzeppelin/contracts-v5.0.2/token/ERC20/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts-v5.0.2/token/ERC20/extensions/IERC20Permit.sol";
import {AccessControlEnumerable} from "@openzeppelin/contracts-v5.2/access/extensions/AccessControlEnumerable.sol";
import {OwnableUpgradeable} from "contracts/openzeppelin/5.2/upgradeable/access/OwnableUpgradeable.sol";
import {IERC20} from "@openzeppelin/contracts-v5.2/token/ERC20/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts-v5.2/token/ERC20/extensions/IERC20Permit.sol";
import {Clones} from "@openzeppelin/contracts-v5.2/proxy/Clones.sol";

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

Expand Down Expand Up @@ -44,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 @@ -56,8 +54,8 @@ contract Dashboard is AccessControlEnumerable {
/// @notice The wrapped ether token contract
IWeth public immutable WETH;

/// @notice The underlying `StakingVault` contract
IStakingVault public stakingVault;
/// @notice Indicates whether the contract has been initialized
bool public initialized;

/// @notice The `VaultHub` contract
VaultHub public vaultHub;
Expand Down Expand Up @@ -88,25 +86,22 @@ contract Dashboard is AccessControlEnumerable {
}

/**
* @notice Initializes the contract with the default admin and `StakingVault` address.
* @param _stakingVault Address of the `StakingVault` contract.
* @notice Initializes the contract with the default admin
* and `vaultHub` address
*/
function initialize(address _stakingVault) external virtual {
_initialize(_stakingVault);
function initialize() external virtual {
_initialize();
}

/**
* @dev Internal initialize function.
* @param _stakingVault Address of the `StakingVault` contract.
*/
function _initialize(address _stakingVault) internal {
if (_stakingVault == address(0)) revert ZeroArgument("_stakingVault");
if (isInitialized) revert AlreadyInitialized();
function _initialize() internal {
if (initialized) revert AlreadyInitialized();
if (address(this) == _SELF) revert NonProxyCallsForbidden();

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

emit Initialized();
Expand All @@ -119,7 +114,7 @@ contract Dashboard is AccessControlEnumerable {
* @return VaultSocket struct containing vault data
*/
function vaultSocket() public view returns (VaultHub.VaultSocket memory) {
return vaultHub.vaultSocket(address(stakingVault));
return vaultHub.vaultSocket(address(stakingVault()));
}

/**
Expand Down Expand Up @@ -167,15 +162,15 @@ contract Dashboard is AccessControlEnumerable {
* @return The valuation as a uint256.
*/
function valuation() external view returns (uint256) {
return stakingVault.valuation();
return stakingVault().valuation();
}

/**
* @notice Returns the total of shares that can be minted on the vault bound by valuation and vault share limit.
* @return The maximum number of stETH shares as a uint256.
*/
function totalMintableShares() public view returns (uint256) {
return _totalMintableShares(stakingVault.valuation());
return _totalMintableShares(stakingVault().valuation());
}

/**
Expand All @@ -184,7 +179,7 @@ contract Dashboard is AccessControlEnumerable {
* @return the maximum number of shares that can be minted by ether
*/
function getMintableShares(uint256 _ether) external view returns (uint256) {
uint256 _totalShares = _totalMintableShares(stakingVault.valuation() + _ether);
uint256 _totalShares = _totalMintableShares(stakingVault().valuation() + _ether);
uint256 _sharesMinted = vaultSocket().sharesMinted;

if (_totalShares < _sharesMinted) return 0;
Expand All @@ -196,7 +191,7 @@ contract Dashboard is AccessControlEnumerable {
* @return The amount of ether that can be withdrawn.
*/
function getWithdrawableEther() external view returns (uint256) {
return Math256.min(address(stakingVault).balance, stakingVault.unlocked());
return Math256.min(address(stakingVault()).balance, stakingVault().unlocked());
}

// TODO: add preview view methods for minting and burning
Expand Down Expand Up @@ -244,7 +239,7 @@ contract Dashboard is AccessControlEnumerable {
WETH.withdraw(_wethAmount);

// TODO: find way to use _fund() instead of stakingVault directly
stakingVault.fund{value: _wethAmount}();
stakingVault().fund{value: _wethAmount}();
}

/**
Expand Down Expand Up @@ -324,7 +319,7 @@ contract Dashboard is AccessControlEnumerable {

uint256 sharesAmount = STETH.getSharesByPooledEth(stETHAmount);

vaultHub.burnSharesBackedByVault(address(stakingVault), sharesAmount);
vaultHub.burnSharesBackedByVault(address(stakingVault()), sharesAmount);
}

/**
Expand Down Expand Up @@ -398,7 +393,7 @@ contract Dashboard is AccessControlEnumerable {

uint256 sharesAmount = STETH.getSharesByPooledEth(stETHAmount);

vaultHub.burnSharesBackedByVault(address(stakingVault), sharesAmount);
vaultHub.burnSharesBackedByVault(address(stakingVault()), sharesAmount);
}

/**
Expand Down Expand Up @@ -426,7 +421,7 @@ contract Dashboard is AccessControlEnumerable {
* @param _newOwner Address of the new owner
*/
function _transferStVaultOwnership(address _newOwner) internal {
OwnableUpgradeable(address(stakingVault)).transferOwnership(_newOwner);
OwnableUpgradeable(address(stakingVault())).transferOwnership(_newOwner);
}

/**
Expand All @@ -438,14 +433,14 @@ contract Dashboard is AccessControlEnumerable {
_rebalanceVault(STETH.getPooledEthBySharesRoundUp(shares));
}

vaultHub.voluntaryDisconnect(address(stakingVault));
vaultHub.voluntaryDisconnect(address(stakingVault()));
}

/**
* @dev Funds the staking vault with the ether sent in the transaction
*/
function _fund() internal {
stakingVault.fund{value: msg.value}();
stakingVault().fund{value: msg.value}();
}

/**
Expand All @@ -454,15 +449,15 @@ contract Dashboard is AccessControlEnumerable {
* @param _ether Amount of ether to withdraw
*/
function _withdraw(address _recipient, uint256 _ether) internal {
stakingVault.withdraw(_recipient, _ether);
stakingVault().withdraw(_recipient, _ether);
}

/**
* @dev Requests the exit of a validator from the staking vault
* @param _validatorPublicKey Public key of the validator to exit
*/
function _requestValidatorExit(bytes calldata _validatorPublicKey) internal {
stakingVault.requestValidatorExit(_validatorPublicKey);
stakingVault().requestValidatorExit(_validatorPublicKey);
}

/**
Expand All @@ -476,7 +471,7 @@ contract Dashboard is AccessControlEnumerable {
bytes calldata _pubkeys,
bytes calldata _signatures
) internal {
stakingVault.depositToBeaconChain(_numberOfDeposits, _pubkeys, _signatures);
stakingVault().depositToBeaconChain(_numberOfDeposits, _pubkeys, _signatures);
}

/**
Expand All @@ -485,7 +480,7 @@ contract Dashboard is AccessControlEnumerable {
* @param _amountOfShares Amount of tokens to mint
*/
function _mint(address _recipient, uint256 _amountOfShares) internal {
vaultHub.mintSharesBackedByVault(address(stakingVault), _recipient, _amountOfShares);
vaultHub.mintSharesBackedByVault(address(stakingVault()), _recipient, _amountOfShares);
}

/**
Expand All @@ -494,7 +489,7 @@ contract Dashboard is AccessControlEnumerable {
*/
function _burn(uint256 _amountOfShares) internal {
STETH.transferSharesFrom(msg.sender, address(vaultHub), _amountOfShares);
vaultHub.burnSharesBackedByVault(address(stakingVault), _amountOfShares);
vaultHub.burnSharesBackedByVault(address(stakingVault()), _amountOfShares);
}

Check warning

Code scanning / Slither

Unused return Medium


/**
Expand All @@ -511,7 +506,17 @@ contract Dashboard is AccessControlEnumerable {
* @param _ether Amount of ether to rebalance
*/
function _rebalanceVault(uint256 _ether) internal {
stakingVault.rebalance(_ether);
stakingVault().rebalance(_ether);
}

/// @notice The underlying `StakingVault` contract
function stakingVault() public view returns (IStakingVault) {
bytes memory args = Clones.fetchCloneArgs(address(this));
address addr;
assembly {
addr := mload(add(args, 32))
}
return IStakingVault(addr);
}

// ==================== Events ====================
Expand Down
16 changes: 7 additions & 9 deletions contracts/0.8.25/vaults/Delegation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,14 @@ contract Delegation is Dashboard {

/**
* @notice Initializes the contract:
* - sets the address of StakingVault;
* - sets up the roles;
* - sets the vote lifetime to 7 days (can be changed later by CURATOR_ROLE and NODE_OPERATOR_MANAGER_ROLE).
* @param _stakingVault The address of StakingVault.
* @dev The msg.sender here is VaultFactory. The VaultFactory is temporarily granted
* DEFAULT_ADMIN_ROLE AND NODE_OPERATOR_MANAGER_ROLE to be able to set initial fees and roles in VaultFactory.
* All the roles are revoked from VaultFactory by the end of the initialization.
*/
function initialize(address _stakingVault) external override {
_initialize(_stakingVault);
function initialize() external override {
_initialize();

// the next line implies that the msg.sender is an operator
// however, the msg.sender is the VaultFactory, and the role will be revoked
Expand Down Expand Up @@ -178,8 +176,8 @@ contract Delegation is Dashboard {
* @return uint256: the amount of unreserved ether.
*/
function unreserved() public view returns (uint256) {
uint256 reserved = stakingVault.locked() + curatorUnclaimedFee() + nodeOperatorUnclaimedFee();
uint256 valuation = stakingVault.valuation();
uint256 reserved = stakingVault().locked() + curatorUnclaimedFee() + nodeOperatorUnclaimedFee();
uint256 valuation = stakingVault().valuation();

return reserved > valuation ? 0 : valuation - reserved;
}
Expand Down Expand Up @@ -307,7 +305,7 @@ contract Delegation is Dashboard {
*/
function claimCuratorFee(address _recipient) external onlyRole(CURATOR_ROLE) {
uint256 fee = curatorUnclaimedFee();
curatorFeeClaimedReport = stakingVault.latestReport();
curatorFeeClaimedReport = stakingVault().latestReport();
_claimFee(_recipient, fee);
}

Expand All @@ -319,7 +317,7 @@ contract Delegation is Dashboard {
*/
function claimNodeOperatorFee(address _recipient) external onlyRole(NODE_OPERATOR_FEE_CLAIMER_ROLE) {
uint256 fee = nodeOperatorUnclaimedFee();
nodeOperatorFeeClaimedReport = stakingVault.latestReport();
nodeOperatorFeeClaimedReport = stakingVault().latestReport();
_claimFee(_recipient, fee);
}

Expand Down Expand Up @@ -428,7 +426,7 @@ contract Delegation is Dashboard {
uint256 _feeBP,
IStakingVault.Report memory _lastClaimedReport
) internal view returns (uint256) {
IStakingVault.Report memory latestReport = stakingVault.latestReport();
IStakingVault.Report memory latestReport = stakingVault().latestReport();

int128 rewardsAccrued = int128(latestReport.valuation - _lastClaimedReport.valuation) -
(latestReport.inOutDelta - _lastClaimedReport.inOutDelta);
Expand Down
25 changes: 3 additions & 22 deletions contracts/0.8.25/vaults/StakingVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@
// See contracts/COMPILERS.md
pragma solidity 0.8.25;

import {OwnableUpgradeable} from "contracts/openzeppelin/5.0.2/upgradeable/access/OwnableUpgradeable.sol";
import {OwnableUpgradeable} from "contracts/openzeppelin/5.2/upgradeable/access/OwnableUpgradeable.sol";
import {BeaconChainDepositLogistics} from "./BeaconChainDepositLogistics.sol";

import {VaultHub} from "./VaultHub.sol";
import {IStakingVault} from "./interfaces/IStakingVault.sol";
import {IBeaconProxy} from "./interfaces/IBeaconProxy.sol";

import {ERC1967Utils} from "@openzeppelin/contracts-v5.0.2/proxy/ERC1967/ERC1967Utils.sol";

/**
* @title StakingVault
Expand Down Expand Up @@ -52,7 +49,7 @@ import {ERC1967Utils} from "@openzeppelin/contracts-v5.0.2/proxy/ERC1967/ERC1967
* deposit contract.
*
*/
contract StakingVault is IStakingVault, IBeaconProxy, BeaconChainDepositLogistics, OwnableUpgradeable {
contract StakingVault is IStakingVault, BeaconChainDepositLogistics, OwnableUpgradeable {
/**
* @notice ERC-7201 storage namespace for the vault
* @dev ERC-7201 namespace is used to prevent upgrade collisions
Expand Down Expand Up @@ -106,21 +103,13 @@ contract StakingVault is IStakingVault, IBeaconProxy, BeaconChainDepositLogistic
_disableInitializers();
}

/**
* @notice Ensures the function can only be called by the beacon
*/
modifier onlyBeacon() {
if (msg.sender != getBeacon()) revert SenderNotBeacon(msg.sender, getBeacon());
_;
}

/**
* @notice Initializes `StakingVault` with an owner, node operator, and optional parameters
* @param _owner Address that will own the vault
* @param _nodeOperator Address of the node operator
* @param - Additional initialization parameters
*/
function initialize(address _owner, address _nodeOperator, bytes calldata /* _params */ ) external onlyBeacon initializer {
function initialize(address _owner, address _nodeOperator, bytes calldata /* _params */ ) external initializer {
__Ownable_init(_owner);
_getStorage().nodeOperator = _nodeOperator;
}
Expand All @@ -141,14 +130,6 @@ contract StakingVault is IStakingVault, IBeaconProxy, BeaconChainDepositLogistic
return _VERSION;
}

/**
* @notice Returns the address of the beacon
* @return Address of the beacon
*/
function getBeacon() public view returns (address) {
return ERC1967Utils.getBeacon();
}

// * * * * * * * * * * * * * * * * * * * * //
// * * * STAKING VAULT BUSINESS LOGIC * * * //
// * * * * * * * * * * * * * * * * * * * * //
Expand Down
Loading

0 comments on commit 292920a

Please sign in to comment.