Skip to content

Commit

Permalink
Merge pull request #933 from lidofinance/feat/vaults-triggerable-exits
Browse files Browse the repository at this point in the history
feat: vaults validators triggerable exits
  • Loading branch information
tamtamchik authored Feb 26, 2025
2 parents 80d2e49 + aa92ca3 commit 2dc2281
Show file tree
Hide file tree
Showing 52 changed files with 4,225 additions and 623 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests-integration-mainnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

services:
hardhat-node:
image: ghcr.io/lidofinance/hardhat-node:2.22.18
image: ghcr.io/lidofinance/hardhat-node:2.22.19
ports:
- 8545:8545
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests-integration-scratch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

services:
hardhat-node:
image: ghcr.io/lidofinance/hardhat-node:2.22.18-scratch
image: ghcr.io/lidofinance/hardhat-node:2.22.19-scratch
ports:
- 8555:8545

Expand Down
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
yarn lint-staged
yarn typecheck
10 changes: 8 additions & 2 deletions contracts/0.8.25/Accounting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,16 @@ contract Accounting is VaultHub {
/// @notice Lido contract
ILido public immutable LIDO;

/// @param _lidoLocator Lido Locator contract
/// @param _lido Lido contract
/// @param _connectedVaultsLimit Maximum number of active vaults that can be connected to the hub
/// @param _relativeShareLimitBP Maximum share limit for a single vault relative to Lido TVL in basis points
constructor(
ILidoLocator _lidoLocator,
ILido _lido
) VaultHub(_lido) {
ILido _lido,
uint256 _connectedVaultsLimit,
uint256 _relativeShareLimitBP
) VaultHub(_lido, _connectedVaultsLimit, _relativeShareLimitBP) {
LIDO_LOCATOR = _lidoLocator;
LIDO = _lido;
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/0.8.25/interfaces/IStakingRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ interface IStakingRouter {
uint256 precisionPoints
);

function reportRewardsMinted(uint256[] memory _stakingModuleIds, uint256[] memory _totalShares) external;
function reportRewardsMinted(uint256[] calldata _stakingModuleIds, uint256[] calldata _totalShares) external;
}
44 changes: 31 additions & 13 deletions contracts/0.8.25/vaults/Dashboard.sol
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,18 @@ contract Dashboard is Permissions {

/**
* @notice Returns the reserve ratio of the vault in basis points
* @return The reserve ratio as a uint16
* @return The reserve ratio in basis points as a uint16
*/
function reserveRatioBP() public view returns (uint16) {
return vaultSocket().reserveRatioBP;
}

/**
* @notice Returns the threshold reserve ratio of the vault in basis points.
* @return The threshold reserve ratio as a uint16.
* @notice Returns the rebalance threshold of the vault in basis points.
* @return The rebalance threshold in basis points as a uint16.
*/
function thresholdReserveRatioBP() external view returns (uint16) {
return vaultSocket().reserveRatioThresholdBP;
function rebalanceThresholdBP() external view returns (uint16) {
return vaultSocket().rebalanceThresholdBP;
}

/**
Expand Down Expand Up @@ -260,14 +260,6 @@ contract Dashboard is Permissions {
SafeERC20.safeTransfer(WETH, _recipient, _amountOfWETH);
}

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

/**
* @notice Mints stETH shares backed by the vault to the recipient.
* @param _recipient Address of the recipient
Expand Down Expand Up @@ -427,6 +419,32 @@ contract Dashboard is Permissions {
_resumeBeaconChainDeposits();
}

/**
* @notice Signals to node operators that specific validators should exit from the beacon chain. It DOES NOT
* directly trigger the exit - node operators must monitor for request events and handle the exits.
* @param _pubkeys Concatenated validator public keys (48 bytes each).
* @dev Emits `ValidatorExitRequested` event for each validator public key through the `StakingVault`.
* This is a voluntary exit request - node operators can choose whether to act on it or not.
*/
function requestValidatorExit(bytes calldata _pubkeys) external {
_requestValidatorExit(_pubkeys);
}

/**
* @notice Initiates a withdrawal from validator(s) on the beacon chain using EIP-7002 triggerable withdrawals
* Both partial withdrawals (disabled for unhealthy `StakingVault`) and full validator exits are supported.
* @param _pubkeys Concatenated validator public keys (48 bytes each).
* @param _amounts Withdrawal amounts in wei for each validator key and must match _pubkeys length.
* Set amount to 0 for a full validator exit.
* For partial withdrawals, amounts will be trimmed to keep MIN_ACTIVATION_BALANCE on the validator to avoid deactivation
* @param _refundRecipient Address to receive any fee refunds, if zero, refunds go to msg.sender.
* @dev A withdrawal fee must be paid via msg.value.
* Use `StakingVault.calculateValidatorWithdrawalFee()` to determine the required fee for the current block.
*/
function triggerValidatorWithdrawal(bytes calldata _pubkeys, uint64[] calldata _amounts, address _refundRecipient) external payable {
_triggerValidatorWithdrawal(_pubkeys, _amounts, _refundRecipient);
}

// ==================== Internal Functions ====================

/**
Expand Down
19 changes: 16 additions & 3 deletions contracts/0.8.25/vaults/Permissions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ abstract contract Permissions is AccessControlConfirmable {
*/
bytes32 public constant REQUEST_VALIDATOR_EXIT_ROLE = keccak256("vaults.Permissions.RequestValidatorExit");

/**
* @notice Permission for triggering validator withdrawal from the StakingVault using EIP-7002 triggerable exit.
*/
bytes32 public constant TRIGGER_VALIDATOR_WITHDRAWAL_ROLE = keccak256("vaults.Permissions.TriggerValidatorWithdrawal");

/**
* @notice Permission for voluntary disconnecting the StakingVault.
*/
Expand Down Expand Up @@ -218,10 +223,18 @@ abstract contract Permissions is AccessControlConfirmable {

/**
* @dev Checks the REQUEST_VALIDATOR_EXIT_ROLE and requests validator exit on the StakingVault.
* @param _pubkey The public key of the validator to request exit for.
* @dev The zero check for _pubkeys is performed in the StakingVault contract.
*/
function _requestValidatorExit(bytes calldata _pubkeys) internal onlyRole(REQUEST_VALIDATOR_EXIT_ROLE) {
stakingVault().requestValidatorExit(_pubkeys);
}

/**
* @dev Checks the TRIGGER_VALIDATOR_WITHDRAWAL_ROLE and triggers validator withdrawal on the StakingVault using EIP-7002 triggerable exit.
* @dev The zero checks for parameters are performed in the StakingVault contract.
*/
function _requestValidatorExit(bytes calldata _pubkey) internal onlyRole(REQUEST_VALIDATOR_EXIT_ROLE) {
stakingVault().requestValidatorExit(_pubkey);
function _triggerValidatorWithdrawal(bytes calldata _pubkeys, uint64[] calldata _amounts, address _refundRecipient) internal onlyRole(TRIGGER_VALIDATOR_WITHDRAWAL_ROLE) {
stakingVault().triggerValidatorWithdrawal{value: msg.value}(_pubkeys, _amounts, _refundRecipient);
}

/**
Expand Down
Loading

0 comments on commit 2dc2281

Please sign in to comment.