Skip to content

[VAULTS] Revamp Delegation (for the last time) #1011

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

Closed
wants to merge 20 commits into from

Conversation

failingtwice
Copy link
Contributor

This PR proposes a revamp Delegation contract.

Context

Delegation provides reward share accounting for node operator

Problem

The old Delegation is clunky and poorly designed

Solution

Change inheritance structure, refactor and improve

Copy link

github-actions bot commented Mar 28, 2025

badge

Hardhat Unit Tests Coverage Summary

Filename                                                                Stmts    Miss  Cover    Missing
--------------------------------------------------------------------  -------  ------  -------  ---------------------------------------------------------
contracts/0.4.24/Lido.sol                                                 205       7  96.59%   754, 759, 802-814, 963-964
contracts/0.4.24/StETH.sol                                                 79       0  100.00%
contracts/0.4.24/StETHPermit.sol                                           15       0  100.00%
contracts/0.4.24/lib/Packed64x4.sol                                         5       0  100.00%
contracts/0.4.24/lib/SigningKeys.sol                                       36       0  100.00%
contracts/0.4.24/lib/StakeLimitUtils.sol                                   37       0  100.00%
contracts/0.4.24/nos/NodeOperatorsRegistry.sol                            512       0  100.00%
contracts/0.4.24/oracle/LegacyOracle.sol                                   72       0  100.00%
contracts/0.4.24/utils/Pausable.sol                                         9       0  100.00%
contracts/0.4.24/utils/Versioned.sol                                        5       0  100.00%
contracts/0.6.12/WstETH.sol                                                17       0  100.00%
contracts/0.8.25/Accounting.sol                                            83       5  93.98%   118-121, 340, 368
contracts/0.8.25/interfaces/IDepositContract.sol                            0       0  100.00%
contracts/0.8.25/interfaces/ILido.sol                                       0       0  100.00%
contracts/0.8.25/interfaces/IOracleReportSanityChecker.sol                  0       0  100.00%
contracts/0.8.25/interfaces/IPostTokenRebaseReceiver.sol                    0       0  100.00%
contracts/0.8.25/interfaces/IStakingRouter.sol                              0       0  100.00%
contracts/0.8.25/interfaces/IWithdrawalQueue.sol                            0       0  100.00%
contracts/0.8.25/lib/GIndex.sol                                            33      18  45.45%   22, 34, 55, 63-70, 79, 86-101
contracts/0.8.25/lib/SSZ.sol                                               16      11  31.25%   31-100, 222-235
contracts/0.8.25/utils/AccessControlConfirmable.sol                        30       0  100.00%
contracts/0.8.25/utils/PausableUntilWithRoles.sol                           3       0  100.00%
contracts/0.8.25/vaults/StakingVault.sol                                  121       0  100.00%
contracts/0.8.25/vaults/VaultFactory.sol                                   11      11  0.00%    20-51
contracts/0.8.25/vaults/VaultHub.sol                                      173      46  73.41%   94, 200, 280, 321-376, 455-465, 500-530, 543-552, 558-573
contracts/0.8.25/vaults/dashboard/Dashboard.sol                            90      90  0.00%    81-537
contracts/0.8.25/vaults/dashboard/Delegation.sol                           42      42  0.00%    72-228
contracts/0.8.25/vaults/dashboard/Permissions.sol                          18       0  100.00%
contracts/0.8.25/vaults/interfaces/IStakingVault.sol                        0       0  100.00%
contracts/0.8.25/vaults/predeposit_guarantee/CLProofVerifier.sol           11       0  100.00%
contracts/0.8.25/vaults/predeposit_guarantee/PredepositGuarantee.sol      149       0  100.00%
contracts/0.8.4/WithdrawalsManagerProxy.sol                                61       0  100.00%
contracts/0.8.9/BeaconChainDepositor.sol                                   21       2  90.48%   48, 51
contracts/0.8.9/Burner.sol                                                 72       0  100.00%
contracts/0.8.9/DepositSecurityModule.sol                                 128       0  100.00%
contracts/0.8.9/EIP712StETH.sol                                            16       0  100.00%
contracts/0.8.9/LidoExecutionLayerRewardsVault.sol                         16       0  100.00%
contracts/0.8.9/LidoLocator.sol                                            22       0  100.00%
contracts/0.8.9/OracleDaemonConfig.sol                                     28       0  100.00%
contracts/0.8.9/StakingRouter.sol                                         316       0  100.00%
contracts/0.8.9/WithdrawalQueue.sol                                        88       0  100.00%
contracts/0.8.9/WithdrawalQueueBase.sol                                   146       0  100.00%
contracts/0.8.9/WithdrawalQueueERC721.sol                                  89       0  100.00%
contracts/0.8.9/WithdrawalVault.sol                                        21       0  100.00%
contracts/0.8.9/lib/Math.sol                                                4       0  100.00%
contracts/0.8.9/lib/PositiveTokenRebaseLimiter.sol                         22       0  100.00%
contracts/0.8.9/lib/UnstructuredRefStorage.sol                              2       0  100.00%
contracts/0.8.9/oracle/AccountingOracle.sol                               190       2  98.95%   154-155
contracts/0.8.9/oracle/BaseOracle.sol                                      89       1  98.88%   397
contracts/0.8.9/oracle/HashConsensus.sol                                  263       1  99.62%   1005
contracts/0.8.9/oracle/ValidatorsExitBusOracle.sol                         91       2  97.80%   138, 315
contracts/0.8.9/proxy/OssifiableProxy.sol                                  17       0  100.00%
contracts/0.8.9/sanity_checks/OracleReportSanityChecker.sol               217       1  99.54%   856
contracts/0.8.9/utils/DummyEmptyContract.sol                                0       0  100.00%
contracts/0.8.9/utils/PausableUntil.sol                                    31       0  100.00%
contracts/0.8.9/utils/Versioned.sol                                        11       0  100.00%
contracts/0.8.9/utils/access/AccessControl.sol                             23       0  100.00%
contracts/0.8.9/utils/access/AccessControlEnumerable.sol                    9       0  100.00%
contracts/common/utils/PausableUntil.sol                                   29       0  100.00%
contracts/testnets/sepolia/SepoliaDepositAdapter.sol                       21      21  0.00%    49-100
TOTAL                                                                    3815     260  93.18%

Diff against master

Filename                                                                Stmts    Miss  Cover
--------------------------------------------------------------------  -------  ------  --------
contracts/0.4.24/Lido.sol                                                  -7      +7  -3.41%
contracts/0.4.24/StETH.sol                                                 +7       0  +100.00%
contracts/0.8.25/Accounting.sol                                           +83      +5  +93.98%
contracts/0.8.25/interfaces/IDepositContract.sol                            0       0  +100.00%
contracts/0.8.25/interfaces/ILido.sol                                       0       0  +100.00%
contracts/0.8.25/interfaces/IOracleReportSanityChecker.sol                  0       0  +100.00%
contracts/0.8.25/interfaces/IPostTokenRebaseReceiver.sol                    0       0  +100.00%
contracts/0.8.25/interfaces/IStakingRouter.sol                              0       0  +100.00%
contracts/0.8.25/interfaces/IWithdrawalQueue.sol                            0       0  +100.00%
contracts/0.8.25/lib/GIndex.sol                                           +33     +18  +45.45%
contracts/0.8.25/lib/SSZ.sol                                              +16     +11  +31.25%
contracts/0.8.25/utils/AccessControlConfirmable.sol                       +30       0  +100.00%
contracts/0.8.25/utils/PausableUntilWithRoles.sol                          +3       0  +100.00%
contracts/0.8.25/vaults/StakingVault.sol                                 +121       0  +100.00%
contracts/0.8.25/vaults/VaultFactory.sol                                  +11     +11  +100.00%
contracts/0.8.25/vaults/VaultHub.sol                                     +173     +46  +73.41%
contracts/0.8.25/vaults/dashboard/Dashboard.sol                           +90     +90  +100.00%
contracts/0.8.25/vaults/dashboard/Delegation.sol                          +42     +42  +100.00%
contracts/0.8.25/vaults/dashboard/Permissions.sol                         +18       0  +100.00%
contracts/0.8.25/vaults/interfaces/IStakingVault.sol                        0       0  +100.00%
contracts/0.8.25/vaults/predeposit_guarantee/CLProofVerifier.sol          +11       0  +100.00%
contracts/0.8.25/vaults/predeposit_guarantee/PredepositGuarantee.sol     +149       0  +100.00%
contracts/0.8.9/Burner.sol                                                 +1       0  +100.00%
contracts/0.8.9/LidoLocator.sol                                            +4       0  +100.00%
contracts/0.8.9/sanity_checks/OracleReportSanityChecker.sol               -15      +1  -0.46%
contracts/common/utils/PausableUntil.sol                                  +29       0  +100.00%
TOTAL                                                                    +799    +231  -5.86%

Results for commit: 7f91994

Minimum allowed coverage is 90%

♻️ This comment has been updated with latest results

@tamtamchik tamtamchik added the solidity issues/tasks related to smart contract code label Mar 31, 2025
@failingtwice failingtwice marked this pull request as ready for review April 4, 2025 07:48
@failingtwice failingtwice requested a review from a team as a code owner April 4, 2025 07:48
@TheDZhon TheDZhon requested review from TheDZhon and tamtamchik April 9, 2025 09:01
/**
* @notice Returns the roles that can:
* - change the confirm expiry;
* - set the curator fee;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curator??

Copy link
Member

@tamtamchik tamtamchik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪄 Looks good, no major blockers...

ofc need to update tests and sync

Comment on lines +77 to +78
emit VaultCreated(address(dashboard), address(vault));
emit DashboardCreated(_defaultAdmin, address(dashboard));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 maybe move the contract address to the first place; it looks confusing when admin comes first, or is this a common scenario for creation events?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, makes sense. We can also combine the two events into one, since they don't emit separately.

uint256 private constant TOTAL_BASIS_POINTS = 100_00;

/**
* @notice Maximum fee value; equals to 100.00%.
*/
uint256 private constant MAX_FEE_BP = TOTAL_BASIS_POINTS;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 why do we need separate private const? maybe use TOTAL_BASIS_POINTS instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's arguable but they are semantically different. One is used for fee calculations, the other to revert when new fee is over 100%. Idk, just reads better in code but not a strong preference

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, the issue lies in usage. MAX_FEE_BP suggests it could be less than or greater than 100%, yet the error states it is always 100%

Screenshot 2025-04-09 at 16 20 45

import {Dashboard} from "./Dashboard.sol";
import {IStakingVault} from "../interfaces/IStakingVault.sol";
import {Permissions} from "./Permissions.sol";
import {VaultHub} from "../VaultHub.sol";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

unused

* @notice Maximum combined feeBP value; equals to 100%.
* @notice Total basis points; 1bp = 0.01%, 100_00bp = 100.00%.
*/
uint256 private constant TOTAL_BASIS_POINTS = 100_00;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint256 private constant TOTAL_BASIS_POINTS = 100_00;
uint256 internal constant TOTAL_BASIS_POINTS = 100_00;

So can it can be used in UXLayer without definition there?

* @notice This contract is a UX-layer for StakingVault and meant to be used as its owner.
* This contract improves the vault UX by bundling all functions from the StakingVault and VaultHub
* in this single contract. It provides administrative functions for managing the StakingVault,
* including funding, withdrawing, minting, burning, and rebalancing operations.
*/
contract Dashboard is Permissions {
abstract contract UXLayer is NodeOperatorFee {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TOTAL_BASIS_POINTS may be taken from NodeOperatorFee

@@ -508,15 +492,28 @@ contract Dashboard is Permissions {
revert InvalidPermit(token);
}

/**
function _mintableValuation() internal view returns (uint256) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 natspec?

return stakingVault().valuation() - nodeOperatorUnclaimedFee();
}

function _mintSharesWithinMintableValuation(address _recipient, uint256 _amountOfShares) internal {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 natspec?

Copy link
Contributor

@TheDZhon TheDZhon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this PR conceptually, the things that are not in sync yet:

  • latest PDG changes and shortcut accounting change
  • new permissions due to the authorize/deauthorize base fns
  • merge conflicts 🤒

Seems we might not need Dashboard contract though and can have it merged with UXLayer, but not sure
to have smth like:

Permissions ← NodeOperatorFee ← InitializableUtils ← UXLayer

* @notice Creates a new StakingVault and Dashboard contracts
* @param _defaultAdmin The address of the default admin
* @param _nodeOperator The address of the node operator
* @param _extraParams The params of vault creation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param _extraParams The params of vault creation

dup (see below)

return IStakingVault(_loadStakingVaultAddress());
}

function vaultHub() public view override returns (VaultHub) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function vaultHub() public view override returns (VaultHub) {
function vaultHub() public view override returns (IVaultHub) {

error AlreadyInitialized();

/**
* @dev Error emitted when the contract is called as a proxy.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @dev Error emitted when the contract is called as a proxy.
* @dev Error emitted when the contract is called not through a proxy.

* It reserves a portion of the staking rewards for the node operator, and allows
* the node operator to claim their fee.
*
* Key features:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and negotiation sidecar for the fee adjustment (e.g. PDG unguaranteed deposits)

/**
* @notice Returns the address of the VaultHub
*/
function vaultHub() public view virtual returns (VaultHub);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function vaultHub() public view virtual returns (VaultHub);
function vaultHub() public view virtual returns (IVaultHub);

@failingtwice
Copy link
Contributor Author

Closed in favor of #1031

@failingtwice failingtwice deleted the delegation-revamp branch April 15, 2025 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
solidity issues/tasks related to smart contract code vaults
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants