Skip to content

Commit

Permalink
Merge branch 'feat/vaults' of https://github.com/lidofinance/core int…
Browse files Browse the repository at this point in the history
…o feat/flash-leverage
  • Loading branch information
failingtwice committed Feb 28, 2025
2 parents a70789c + 3241ec7 commit 3f1622a
Show file tree
Hide file tree
Showing 83 changed files with 4,855 additions and 1,112 deletions.
9 changes: 5 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ LOCAL_STAKING_VAULT_BEACON_ADDRESS=

# RPC URL for a separate, non Hardhat Network node (Anvil, Infura, Alchemy, etc.)
MAINNET_RPC_URL=http://localhost:8545

# RPC URL for Hardhat Network forking, required for running tests on mainnet fork with tracing (Infura, Alchemy, etc.)
# https://hardhat.org/hardhat-network/docs/guides/forking-other-networks#forking-other-networks
FORK_RPC_URL=https://eth.drpc.org

# https://docs.lido.fi/deployed-contracts
MAINNET_LOCATOR_ADDRESS=0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb
MAINNET_AGENT_ADDRESS=0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c
Expand Down Expand Up @@ -54,10 +59,6 @@ MAINNET_STAKING_VAULT_BEACON_ADDRESS=
HOLESKY_RPC_URL=
SEPOLIA_RPC_URL=

# RPC URL for Hardhat Network forking, required for running tests on mainnet fork with tracing (Infura, Alchemy, etc.)
# https://hardhat.org/hardhat-network/docs/guides/forking-other-networks#forking-other-networks
HARDHAT_FORKING_URL=https://eth.drpc.org

# Scratch deployment via hardhat variables
DEPLOYER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
GENESIS_TIME=1639659600
Expand Down
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ lib/abi/*.json
accounts.json
deployed-local.json
deployed-hardhat.json
deployed-local-devnet.json

# MacOS
.DS_Store
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
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ integration tests follows the `*.integration.ts` postfix, for example, `myScenar
Foundry's Solidity tests are specifically used for fuzzing library contracts or functions that perform complex
calculations or byte manipulation. These Solidity tests are located under `/tests` and organized into appropriate
subdirectories. The naming conventions follow
Foundry's [documentation](https://book.getfoundry.sh/tutorials/best-practices#general-test-guidance):
Foundry's [documentation](https://book.getfoundry.sh/guides/best-practices#general-test-guidance):
- For tests, use the `.t.sol` postfix (e.g., `MyContract.t.sol`).
- For scripts, use the `.s.sol` postfix (e.g., `MyScript.s.sol`).
Expand Down Expand Up @@ -327,7 +327,7 @@ This is the most common method for running integration tests. It uses an instanc
mainnet environment, allowing you to run integration tests with trace logging.
> [!NOTE]
> Ensure that `HARDHAT_FORKING_URL` is set to Ethereum Mainnet RPC and `MAINNET_*` environment variables are set in the
> Ensure that `FORK_RPC_URL` is set to Ethereum Mainnet RPC and `MAINNET_*` environment variables are set in the
> `.env` file (refer to `.env.example` for guidance). Otherwise, the tests will run against the Scratch deployment.
```bash
Expand Down
6 changes: 3 additions & 3 deletions contracts/0.4.24/Lido.sol
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ contract Lido is Versioned, StETHPermit, AragonApp {
* @dev can be called only by accounting
*/
function mintShares(address _recipient, uint256 _amountOfShares) public {
_auth(getLidoLocator().accounting());
require(msg.sender == getLidoLocator().accounting() || msg.sender == getLidoLocator().vaultHub(), "APP_AUTH_FAILED");
_whenNotStopped();

_mintShares(_recipient, _amountOfShares);
Expand Down Expand Up @@ -639,7 +639,7 @@ contract Lido is Versioned, StETHPermit, AragonApp {
*/
function burnExternalShares(uint256 _amountOfShares) external {
require(_amountOfShares != 0, "BURN_ZERO_AMOUNT_OF_SHARES");
_auth(getLidoLocator().accounting());
_auth(getLidoLocator().vaultHub());
_whenNotStopped();

uint256 externalShares = EXTERNAL_SHARES_POSITION.getStorageUint256();
Expand All @@ -663,7 +663,7 @@ contract Lido is Versioned, StETHPermit, AragonApp {
*/
function rebalanceExternalEtherToInternal() external payable {
require(msg.value != 0, "ZERO_VALUE");
_auth(getLidoLocator().accounting());
_auth(getLidoLocator().vaultHub());
_whenNotStopped();

uint256 shares = getSharesByPooledEth(msg.value);
Expand Down
30 changes: 15 additions & 15 deletions contracts/0.8.25/Accounting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,15 @@ import {ReportValues} from "contracts/common/interfaces/ReportValues.sol";
/// @notice contract is responsible for handling accounting oracle reports
/// calculating all the state changes that is required to apply the report
/// and distributing calculated values to relevant parts of the protocol
/// @dev accounting is inherited from VaultHub contract to reduce gas costs and
/// simplify the auth flows, but they are mostly independent
contract Accounting is VaultHub {
contract Accounting {
struct Contracts {
address accountingOracleAddress;
IOracleReportSanityChecker oracleReportSanityChecker;
IBurner burner;
IWithdrawalQueue withdrawalQueue;
IPostTokenRebaseReceiver postTokenRebaseReceiver;
IStakingRouter stakingRouter;
VaultHub vaultHub;
}

struct PreReportState {
Expand Down Expand Up @@ -83,6 +82,8 @@ contract Accounting is VaultHub {
uint256 precisionPoints;
}

error NotAuthorized(string operation, address addr);

/// @notice deposit size in wei (for pre-maxEB accounting)
uint256 private constant DEPOSIT_SIZE = 32 ether;

Expand All @@ -91,20 +92,16 @@ contract Accounting is VaultHub {
/// @notice Lido contract
ILido public immutable LIDO;

/// @param _lidoLocator Lido Locator contract
/// @param _lido Lido contract
constructor(
ILidoLocator _lidoLocator,
ILido _lido
) VaultHub(_lido) {
) {
LIDO_LOCATOR = _lidoLocator;
LIDO = _lido;
}

function initialize(address _admin) external initializer {
if (_admin == address(0)) revert ZeroArgument("_admin");

__VaultHub_init(_admin);
}

/// @notice calculates all the state changes that is required to apply the report
/// @param _report report values
/// @param _withdrawalShareRate maximum share rate used for withdrawal finalization
Expand Down Expand Up @@ -226,7 +223,7 @@ contract Accounting is VaultHub {
// Calculate the amount of ether locked in the vaults to back external balance of stETH
// and the amount of shares to mint as fees to the treasury for each vaults
(update.vaultsLockedEther, update.vaultsTreasuryFeeShares, update.totalVaultsTreasuryFeeShares) =
_calculateVaultsRebase(
_contracts.vaultHub.calculateVaultsRebase(
update.postTotalShares,
update.postTotalPooledEther,
_pre.totalShares,
Expand Down Expand Up @@ -335,15 +332,16 @@ contract Accounting is VaultHub {
_update.etherToFinalizeWQ
);

_updateVaults(
// TODO: Remove this once decide on vaults reporting
_contracts.vaultHub.updateVaults(
_report.vaultValues,
_report.inOutDeltas,
_update.vaultsLockedEther,
_update.vaultsTreasuryFeeShares
);

if (_update.totalVaultsTreasuryFeeShares > 0) {
STETH.mintExternalShares(LIDO_LOCATOR.treasury(), _update.totalVaultsTreasuryFeeShares);
LIDO.mintExternalShares(LIDO_LOCATOR.treasury(), _update.totalVaultsTreasuryFeeShares);
}

_notifyRebaseObserver(_contracts.postTokenRebaseReceiver, _report, _pre, _update);
Expand Down Expand Up @@ -463,7 +461,8 @@ contract Accounting is VaultHub {
address burner,
address withdrawalQueue,
address postTokenRebaseReceiver,
address stakingRouter
address stakingRouter,
address vaultHub
) = LIDO_LOCATOR.oracleReportComponents();

return
Expand All @@ -473,7 +472,8 @@ contract Accounting is VaultHub {
IBurner(burner),
IWithdrawalQueue(withdrawalQueue),
IPostTokenRebaseReceiver(postTokenRebaseReceiver),
IStakingRouter(stakingRouter)
IStakingRouter(stakingRouter),
VaultHub(vaultHub)
);
}

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 3f1622a

Please sign in to comment.