Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: factory upgrade #901

Merged
merged 22 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}

/**
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
Loading