-
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
Conversation
test/0.8.25/vaults/dashboard/contracts/LidoLocator__HarnessForDashboard.sol
Outdated
Show resolved
Hide resolved
test/0.8.25/vaults/dashboard/contracts/LidoLocator__HarnessForDashboard.sol
Outdated
Show resolved
Hide resolved
…ashboard-ux-updates
Hardhat Unit Tests Coverage Summary
Diff against master
Results for commit: 067f38b Minimum allowed coverage is ♻️ This comment has been updated with latest results |
…re into feat/vaults-dashboard-recovery
[SI][Vault] Dashboard recovery
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.
Need to fix rounding error and add mint/burn StETH
PermitInput calldata _permit | ||
) | ||
external | ||
virtual | ||
onlyRole(DEFAULT_ADMIN_ROLE) | ||
trustlessPermit(address(STETH), msg.sender, address(this), _permit) | ||
{ | ||
_burn(_tokens); | ||
_burn(msg.sender, _amountOfShares); |
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.
_burn(msg.sender, _amountOfShares); | |
_burnFrom(msg.sender, _amountOfShares); |
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.
fixed
uint256 _amount; | ||
if (_token == address(0)) revert ZeroArgument("_token"); |
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.
uint256 _amount; | |
if (_token == address(0)) revert ZeroArgument("_token"); | |
if (_token == address(0)) revert ZeroArgument("_token"); | |
uint256 _amount; |
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.
fixed
@@ -357,48 +374,44 @@ contract Dashboard is AccessControlEnumerable { | |||
return; | |||
} | |||
} | |||
revert("Permit failure"); | |||
revert Erc20Error(token, "Permit failure"); |
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.
Same here.
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.
removed this error
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.
LGTM 👍 except for some insignificant changes
IWETH9 public immutable WETH; | ||
|
||
/// @notice ETH address convention per EIP-7528 | ||
address public constant ETH = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); |
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.
address public constant ETH = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); | |
address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; |
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.
fixed
@@ -109,6 +111,10 @@ contract Dashboard is AccessControlEnumerable { | |||
vaultHub = VaultHub(stakingVault.vaultHub()); | |||
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender); |
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.
let's add an admin argument to avoid using msg.sender
inside the contract
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.
maybe a bit later will be done by @failingtwice
@@ -109,6 +111,10 @@ contract Dashboard is AccessControlEnumerable { | |||
vaultHub = VaultHub(stakingVault.vaultHub()); | |||
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender); | |||
|
|||
// reduces gas cost for `burnWsteth` |
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.
// reduces gas cost for `burnWsteth` | |
// reduces gas cost for `mintWsteth` |
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.
fixed
// reduces gas cost for `burnWsteth` | ||
// dashboard will hold STETH during this tx | ||
STETH.approve(address(WSTETH), type(uint256).max); | ||
|
||
emit Initialized(); |
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.
should we emit args of the init?
@@ -180,11 +186,11 @@ contract Dashboard is AccessControlEnumerable { | |||
|
|||
/** | |||
* @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 comment
The reason will be displayed to describe this comment to others. Learn more.
* @notice Returns the maximum number of shares that can be minted with deposited ether. | |
* @notice Returns the maximum number of shares that can be minted with funded ether. |
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.
fixed
function recoverERC721(address _token, uint256 _tokenId) external onlyRole(DEFAULT_ADMIN_ROLE) { | ||
if (_token == address(0)) revert ZeroArgument("_token"); | ||
|
||
IERC721(_token).transferFrom(address(this), msg.sender, _tokenId); |
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.
IERC721(_token).transferFrom(address(this), msg.sender, _tokenId); | |
IERC721(_token).safeTransferFrom(address(this), msg.sender, _tokenId); |
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.
fixed
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.
@@ -540,4 +621,7 @@ contract Dashboard is AccessControlEnumerable { | |||
|
|||
/// @notice Error when the contract is already initialized. | |||
error AlreadyInitialized(); | |||
|
|||
/// @notice Error interacting with an ERC20 token | |||
error Erc20Error(address token, string reason); |
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.
too generic
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.
fixed
contracts/0.8.9/Burner.sol
Outdated
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.
let's move this one to 0.8.25 and take recovery lib from CSM
await expect(result).to.emit(steth, "Transfer").withArgs(wsteth, dashboard, weiStethDown); // unwrap wsteth to steth | ||
await expect(result).to.emit(wsteth, "Transfer").withArgs(dashboard, ZeroAddress, weiShare); // burn wsteth | ||
// transfer shares to hub | ||
await expect(result).to.emit(steth, "Transfer").withArgs(dashboard, hub, weiStethDownDown); |
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.
🧠
|
||
WETH.transferFrom(msg.sender, address(this), _wethAmount); | ||
WETH.withdraw(_wethAmount); | ||
function fundByWeth(uint256 _amountWETH) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { |
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.
function fundByWeth(uint256 _amountWETH) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { | |
function fundWithWeth(uint256 _amountWETH) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { |
or
function fundByWeth(uint256 _amountWETH) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { | |
function fundWeth(uint256 _amountWETH) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { |
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.
fixed
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.
LGTM, mind sync with the auth changes needed
function mintStETH( | ||
address _recipient, | ||
uint256 _amountStETH | ||
) external payable virtual onlyRole(DEFAULT_ADMIN_ROLE) fundAndProceed { |
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.
Do we still need virtual onlyRole(DEFAULT_ADMIN_ROLE)
here?
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.
no, fixed
function burnSharesWithPermit( | ||
uint256 _amountShares, | ||
PermitInput calldata _permit | ||
) external virtual onlyRole(DEFAULT_ADMIN_ROLE) safePermit(address(STETH), msg.sender, address(this), _permit) { |
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.
Do we still need role?
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.
fixed
@@ -41,19 +41,31 @@ contract VaultHub__MockForDashboard { | |||
emit Mock__VaultDisconnected(vault); | |||
} | |||
|
|||
function mintSharesBackedByVault(address /* vault */, address recipient, uint256 amount) external { | |||
function mintSharesBackedByVault(address vault, address recipient, uint256 amount) external { |
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.
function mintSharesBackedByVault(address vault, address recipient, uint256 amount) external { | |
function mintSharesBackedByVault(address _vault, address _recipient, uint256 _shares) external { |
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.
🍏 LGTM, except only a few conceptual changes not blocking DevNet-3 probably.
What is blocking for real — is the test coverage.
// SPDX-License-Identifier: GPL-3.0 | ||
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> | ||
|
||
// See contracts/COMPILERS.md | ||
pragma solidity 0.8.25; | ||
|
||
import {Permissions} from "./Permissions.sol"; | ||
import {OwnableUpgradeable} from "contracts/openzeppelin/5.2/upgradeable/access/OwnableUpgradeable.sol"; |
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.
not used
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.
fixed
|
||
// See contracts/COMPILERS.md | ||
pragma solidity 0.8.25; | ||
|
||
import {Permissions} from "./Permissions.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"; |
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.
not used
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.
fixed
import {IERC20Permit} from "@openzeppelin/contracts-v5.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 comment
The 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
2025 copyright
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.
fixed
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.
maybe namespace should be 'vaults.Permissions.X'
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.
* @param _permit data required for the stETH.permit() method to set the allowance | ||
*/ | ||
function burnWithPermit( | ||
uint256 _tokens, | ||
function burnStethWithPermit( |
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.
function burnStethWithPermit( | |
function burnStETHWithPermit( |
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.
fixed
* @param _token Address of the token to recover or 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee for ether | ||
* @param _recipient Address of the recovery recipient | ||
*/ | ||
function recoverERC20(address _token, address _recipient) external onlyRole(DEFAULT_ADMIN_ROLE) { |
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.
function recoverERC20(address _token, address _recipient) external onlyRole(DEFAULT_ADMIN_ROLE) { | |
function recoverERC20(address _token, uint256 _amount, address _recipient) external onlyRole(DEFAULT_ADMIN_ROLE) { |
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.
keccak256("Vault.Delegation.CuratorRole") ⇒ keccak256("vaults.Delegation.CuratorRole") and so on
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.
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.
let's remove msg.sender
from initialization
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.
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.
vote -> multiConfirmationRole (everywhere)
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.
A short summary of the changes.
Context
General
Dashboard Contract
LidoLocator
for retrievingstETH
andwstETH
contract addresses.stETH
towstETH
.getMintableShares
toprojectedMintableShares
.mintWstETH
to utilize the initial approval ofstETH
.burnWstETH
to align with changes in the_burn
method.burnWithPermit
to align with changes in the_burn
method.burnWstETHWithPermit
to align with changes in the_burn
method.burn
->burnShares
mint
->mintShares
burn
/fund
methods to accommodate changes in_burn/_fund
:_fund
now takesvalue
as an argument._burn
now accepts_sender
and_amountOfShares
as arguments._sender
type, eithertransferShares
ortransferSharesFrom
is executed.transferShares
is used if the operation originates from the Dashboard contract to avoid additionalstETH
approvals.Delegation Contract
fund
/burn
methods to account for updates to_burn/_fund
in the Dashboard contract.Testing
Permit
functionality when the shares-to-stETH
ratio is not 1:1 (e.g.,1 share = 0.5 stETH
,1 share = 2 stETH
).LidoLocator
setup in the constructor.Additional Notes
Permit
mechanism withstETH
during rebalancing of the shares-to-stETH
ratio will still use thevalue
ofstETH
tokens. As a result, the values passed to theburnWithPermit
method and those signed in thePermit
may differ. To address this, theStETHPermit
contract would need updates, but this could introduce unnecessary overhead.Problem
What problem this PR solves, link relevant issue if it exists
Solution
Your proposed solution