-
Notifications
You must be signed in to change notification settings - Fork 195
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
[SI][Vaults] feat: Dashboard UX updates #915
Changes from 15 commits
b6156ad
a11d6b6
29ef4ad
b5c839f
4b16505
41f6125
ae59476
de3d3b9
3261a8c
c228afd
c6cc70f
739a60d
839b726
99f7d9a
186e266
0aea721
c4e7ceb
62710b0
95b11fb
b5ce3a4
fe87ca3
50259ec
fac1f9d
710ccac
77d8739
0903095
5300444
f24c48e
90f64d4
5a907fc
b529f7b
5215e35
f78235c
bea9a49
616a0f8
2b871fb
c9c7f74
e470a89
4ff312b
34d4519
b7e5536
0c2c170
067f38b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
folkyatina marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please update following comment:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. votingCommittee -> multiConfirmationRoles There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reserveRatio() -> reserveRatioBP() or at least comment that this is the BP convention There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's document whether already minted are included or not
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,23 +1,23 @@ | ||||||
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> | ||||||
// SPDX-License-Identifier: GPL-3.0 | ||||||
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> | ||||||
|
||||||
// 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 {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"; | ||||||
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 {ILido as IStETH} from "contracts/0.8.25/interfaces/ILido.sol"; | ||||||
import {ILidoLocator} from "contracts/common/interfaces/ILidoLocator.sol"; | ||||||
import {IStakingVault} from "contracts/0.8.25/vaults/interfaces/IStakingVault.sol"; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not used There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
|
||||||
interface IWeth is IERC20 { | ||||||
function withdraw(uint) external; | ||||||
interface IWETH9 is IERC20 { | ||||||
function withdraw(uint256) external; | ||||||
|
||||||
function deposit() external payable; | ||||||
} | ||||||
|
@@ -54,7 +54,7 @@ | |||||
IWstETH public immutable WSTETH; | ||||||
|
||||||
/// @notice The wrapped ether token contract | ||||||
IWeth public immutable WETH; | ||||||
IWETH9 public immutable WETH; | ||||||
|
||||||
/// @notice The underlying `StakingVault` contract | ||||||
IStakingVault public stakingVault; | ||||||
|
@@ -71,20 +71,18 @@ | |||||
} | ||||||
|
||||||
/** | ||||||
* @notice Constructor sets the stETH token address and the implementation contract address. | ||||||
* @param _stETH Address of the stETH token contract. | ||||||
* @notice Constructor sets the stETH, WETH, and WSTETH token addresses. | ||||||
* @param _weth Address of the weth token contract. | ||||||
* @param _wstETH Address of the wstETH token contract. | ||||||
* @param _lidoLocator Address of the Lido locator contract. | ||||||
*/ | ||||||
constructor(address _stETH, address _weth, address _wstETH) { | ||||||
if (_stETH == address(0)) revert ZeroArgument("_stETH"); | ||||||
constructor(address _weth, address _lidoLocator) { | ||||||
if (_weth == address(0)) revert ZeroArgument("_WETH"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
if (_wstETH == address(0)) revert ZeroArgument("_wstETH"); | ||||||
if (_lidoLocator == address(0)) revert ZeroArgument("_lidoLocator"); | ||||||
|
||||||
_SELF = address(this); | ||||||
STETH = IStETH(_stETH); | ||||||
WETH = IWeth(_weth); | ||||||
WSTETH = IWstETH(_wstETH); | ||||||
WETH = IWETH9(_weth); | ||||||
STETH = IStETH(ILidoLocator(_lidoLocator).lido()); | ||||||
WSTETH = IWstETH(ILidoLocator(_lidoLocator).wstETH()); | ||||||
} | ||||||
|
||||||
/** | ||||||
|
@@ -109,6 +107,12 @@ | |||||
vaultHub = VaultHub(stakingVault.vaultHub()); | ||||||
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's add an admin argument to avoid using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe a bit later will be done by @failingtwice |
||||||
|
||||||
// reduces gas cost for `burnWsteth` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
// dashboard will hold STETH during this tx | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
STETH.approve(address(WSTETH), type(uint256).max); | ||||||
// allows to uncondinitialy use transferFrom in _burn | ||||||
STETH.approve(address(this), type(uint256).max); | ||||||
|
||||||
emit Initialized(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we emit args of the init? |
||||||
} | ||||||
|
||||||
|
@@ -180,11 +184,11 @@ | |||||
|
||||||
/** | ||||||
* @notice Returns the maximum number of shares that can be minted with deposited ether. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
* @param _ether the amount of ether to be funded, can be zero | ||||||
* @param _etherToFund the amount of ether to be funded, can be zero | ||||||
* @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); | ||||||
function projectedMintableShares(uint256 _etherToFund) external view returns (uint256) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
not a strong opinion though, just to disambiguate with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
uint256 _totalShares = _totalMintableShares(stakingVault.valuation() + _etherToFund); | ||||||
uint256 _sharesMinted = vaultSocket().sharesMinted; | ||||||
|
||||||
if (_totalShares < _sharesMinted) return 0; | ||||||
|
@@ -199,14 +203,11 @@ | |||||
return Math256.min(address(stakingVault).balance, stakingVault.unlocked()); | ||||||
} | ||||||
|
||||||
// TODO: add preview view methods for minting and burning | ||||||
|
||||||
// ==================== Vault Management Functions ==================== | ||||||
|
||||||
/** | ||||||
* @dev Receive function to accept ether | ||||||
*/ | ||||||
// TODO: Consider the amount of ether on balance of the contract | ||||||
receive() external payable { | ||||||
if (msg.value == 0) revert ZeroArgument("msg.value"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
why do we need this though? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
} | ||||||
|
@@ -230,7 +231,7 @@ | |||||
* @notice Funds the staking vault with ether | ||||||
*/ | ||||||
function fund() external payable virtual onlyRole(DEFAULT_ADMIN_ROLE) { | ||||||
_fund(); | ||||||
_fund(msg.value); | ||||||
} | ||||||
|
||||||
/** | ||||||
|
@@ -243,8 +244,7 @@ | |||||
WETH.transferFrom(msg.sender, address(this), _wethAmount); | ||||||
WETH.withdraw(_wethAmount); | ||||||
|
||||||
// TODO: find way to use _fund() instead of stakingVault directly | ||||||
stakingVault.fund{value: _wethAmount}(); | ||||||
_fund(_wethAmount); | ||||||
} | ||||||
|
||||||
/** | ||||||
|
@@ -280,7 +280,7 @@ | |||||
* @param _recipient Address of the recipient | ||||||
* @param _amountOfShares Amount of shares to mint | ||||||
*/ | ||||||
function mint( | ||||||
function mintShares( | ||||||
folkyatina marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
address _recipient, | ||||||
uint256 _amountOfShares | ||||||
) external payable virtual onlyRole(DEFAULT_ADMIN_ROLE) fundAndProceed { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we still need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, fixed |
||||||
|
@@ -290,42 +290,39 @@ | |||||
/** | ||||||
* @notice Mints wstETH tokens backed by the vault to a recipient. Approvals for the passed amounts should be done before. | ||||||
Jeday marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
* @param _recipient Address of the recipient | ||||||
* @param _tokens Amount of tokens to mint | ||||||
* @param _amountOfWstETH Amount of tokens to mint | ||||||
*/ | ||||||
function mintWstETH( | ||||||
address _recipient, | ||||||
uint256 _tokens | ||||||
uint256 _amountOfWstETH | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. amountX vs Xamount is a bit incosistent There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
) external payable virtual onlyRole(DEFAULT_ADMIN_ROLE) fundAndProceed { | ||||||
_mint(address(this), _tokens); | ||||||
_mint(address(this), _amountOfWstETH); | ||||||
|
||||||
uint256 stETHAmount = STETH.getPooledEthByShares(_amountOfWstETH); | ||||||
Jeday marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
STETH.approve(address(WSTETH), _tokens); | ||||||
uint256 wstETHAmount = WSTETH.wrap(_tokens); | ||||||
uint256 wstETHAmount = WSTETH.wrap(stETHAmount); | ||||||
WSTETH.transfer(_recipient, wstETHAmount); | ||||||
} | ||||||
|
||||||
/** | ||||||
* @notice Burns stETH shares from the sender backed by the vault | ||||||
* @param _amountOfShares Amount of shares to burn | ||||||
*/ | ||||||
function burn(uint256 _amountOfShares) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { | ||||||
_burn(_amountOfShares); | ||||||
function burnShares(uint256 _amountOfShares) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { | ||||||
_burn(msg.sender, _amountOfShares); | ||||||
Jeday marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
|
||||||
/** | ||||||
* @notice Burns wstETH tokens from the sender backed by the vault. Approvals for the passed amounts should be done before. | ||||||
* @param _tokens Amount of wstETH tokens to burn | ||||||
* @param _amountOfWstETH Amount of wstETH tokens to burn | ||||||
* @dev The _amountOfWstETH = _amountOfShares by design | ||||||
*/ | ||||||
function burnWstETH(uint256 _tokens) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { | ||||||
WSTETH.transferFrom(msg.sender, address(this), _tokens); | ||||||
|
||||||
uint256 stETHAmount = WSTETH.unwrap(_tokens); | ||||||
|
||||||
STETH.transfer(address(vaultHub), stETHAmount); | ||||||
function burnWstETH(uint256 _amountOfWstETH) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { | ||||||
folkyatina marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
WSTETH.transferFrom(msg.sender, address(this), _amountOfWstETH); | ||||||
WSTETH.unwrap(_amountOfWstETH); | ||||||
|
||||||
uint256 sharesAmount = STETH.getSharesByPooledEth(stETHAmount); | ||||||
|
||||||
vaultHub.burnSharesBackedByVault(address(stakingVault), sharesAmount); | ||||||
_burn(address(this), _amountOfWstETH); | ||||||
} | ||||||
Check warning Code scanning / Slither Unused return Medium
Dashboard.burnWstETH(uint256) ignores return value by WSTETH.unwrap(_amountOfWstETH)
|
||||||
|
||||||
/** | ||||||
* @dev Modifier to check if the permit is successful, and if not, check if the allowance is sufficient | ||||||
|
@@ -362,43 +359,40 @@ | |||||
|
||||||
/** | ||||||
* @notice Burns stETH tokens from the sender backed by the vault using EIP-2612 Permit. | ||||||
* @param _tokens Amount of stETH tokens to burn | ||||||
* @param _amountOfShares Amount of shares to burn | ||||||
* @param _permit data required for the stETH.permit() method to set the allowance | ||||||
*/ | ||||||
function burnWithPermit( | ||||||
uint256 _tokens, | ||||||
function burnSharesWithPermit( | ||||||
folkyatina marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
uint256 _amountOfShares, | ||||||
PermitInput calldata _permit | ||||||
) | ||||||
external | ||||||
virtual | ||||||
onlyRole(DEFAULT_ADMIN_ROLE) | ||||||
trustlessPermit(address(STETH), msg.sender, address(this), _permit) | ||||||
Jeday marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
{ | ||||||
_burn(_tokens); | ||||||
_burn(msg.sender, _amountOfShares); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
} | ||||||
|
||||||
/** | ||||||
* @notice Burns wstETH tokens from the sender backed by the vault using EIP-2612 Permit. | ||||||
* @param _tokens Amount of wstETH tokens to burn | ||||||
* @param _amountOfWstETH Amount of wstETH tokens to burn | ||||||
* @param _permit data required for the wstETH.permit() method to set the allowance | ||||||
*/ | ||||||
function burnWstETHWithPermit( | ||||||
uint256 _tokens, | ||||||
uint256 _amountOfWstETH, | ||||||
PermitInput calldata _permit | ||||||
) | ||||||
external | ||||||
virtual | ||||||
onlyRole(DEFAULT_ADMIN_ROLE) | ||||||
trustlessPermit(address(WSTETH), msg.sender, address(this), _permit) | ||||||
{ | ||||||
WSTETH.transferFrom(msg.sender, address(this), _tokens); | ||||||
uint256 stETHAmount = WSTETH.unwrap(_tokens); | ||||||
|
||||||
STETH.transfer(address(vaultHub), stETHAmount); | ||||||
|
||||||
WSTETH.transferFrom(msg.sender, address(this), _amountOfWstETH); | ||||||
uint256 stETHAmount = WSTETH.unwrap(_amountOfWstETH); | ||||||
uint256 sharesAmount = STETH.getSharesByPooledEth(stETHAmount); | ||||||
|
||||||
vaultHub.burnSharesBackedByVault(address(stakingVault), sharesAmount); | ||||||
_burn(address(this), sharesAmount); | ||||||
} | ||||||
|
||||||
/** | ||||||
|
@@ -416,7 +410,7 @@ | |||||
*/ | ||||||
modifier fundAndProceed() { | ||||||
if (msg.value > 0) { | ||||||
_fund(); | ||||||
_fund(msg.value); | ||||||
} | ||||||
_; | ||||||
} | ||||||
|
@@ -444,8 +438,8 @@ | |||||
/** | ||||||
* @dev Funds the staking vault with the ether sent in the transaction | ||||||
*/ | ||||||
function _fund() internal { | ||||||
stakingVault.fund{value: msg.value}(); | ||||||
function _fund(uint256 _value) internal { | ||||||
stakingVault.fund{value: _value}(); | ||||||
} | ||||||
|
||||||
/** | ||||||
|
@@ -465,20 +459,6 @@ | |||||
stakingVault.requestValidatorExit(_validatorPublicKey); | ||||||
} | ||||||
|
||||||
/** | ||||||
* @dev Deposits validators to the beacon chain | ||||||
* @param _numberOfDeposits Number of validator deposits | ||||||
* @param _pubkeys Concatenated public keys of the validators | ||||||
* @param _signatures Concatenated signatures of the validators | ||||||
*/ | ||||||
function _depositToBeaconChain( | ||||||
uint256 _numberOfDeposits, | ||||||
bytes calldata _pubkeys, | ||||||
bytes calldata _signatures | ||||||
) internal { | ||||||
stakingVault.depositToBeaconChain(_numberOfDeposits, _pubkeys, _signatures); | ||||||
} | ||||||
|
||||||
/** | ||||||
* @dev Mints stETH tokens backed by the vault to a recipient | ||||||
* @param _recipient Address of the recipient | ||||||
|
@@ -492,17 +472,19 @@ | |||||
* @dev Burns stETH tokens from the sender backed by the vault | ||||||
* @param _amountOfShares Amount of tokens to burn | ||||||
*/ | ||||||
function _burn(uint256 _amountOfShares) internal { | ||||||
STETH.transferSharesFrom(msg.sender, address(vaultHub), _amountOfShares); | ||||||
function _burn(address _sender, uint256 _amountOfShares) internal { | ||||||
STETH.transferSharesFrom(_sender, address(vaultHub), _amountOfShares); | ||||||
|
||||||
vaultHub.burnSharesBackedByVault(address(stakingVault), _amountOfShares); | ||||||
} | ||||||
Check warning Code scanning / Slither Unused return Medium
Dashboard._burn(address,uint256) ignores return value by STETH.transferSharesFrom(_sender,address(vaultHub),_amountOfShares)
|
||||||
|
||||||
/** | ||||||
* @dev calculates total shares vault can mint | ||||||
* @param _valuation custom vault valuation | ||||||
*/ | ||||||
function _totalMintableShares(uint256 _valuation) internal view returns (uint256) { | ||||||
uint256 maxMintableStETH = (_valuation * (TOTAL_BASIS_POINTS - vaultSocket().reserveRatioBP)) / TOTAL_BASIS_POINTS; | ||||||
uint256 maxMintableStETH = (_valuation * (TOTAL_BASIS_POINTS - vaultSocket().reserveRatioBP)) / | ||||||
TOTAL_BASIS_POINTS; | ||||||
return Math256.min(STETH.getSharesByPooledEth(maxMintableStETH), vaultSocket().shareLimit); | ||||||
} | ||||||
|
||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. keccak256("Vault.Delegation.CuratorRole") ⇒ keccak256("vaults.Delegation.CuratorRole") and so on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. vote -> multiConfirmationRole (everywhere) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thresholdReserveRatio vs reserveRatioThreshold is a bit weird