-
Notifications
You must be signed in to change notification settings - Fork 262
feat: alerting harness contract #1551
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
Open
tamtamchik
wants to merge
10
commits into
develop
Choose a base branch
from
feat/vaults-alert-helper
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
43f40d8
feat: alerting harness contract
tamtamchik 0363225
Merge branch 'feat/vaults' into feat/vaults-alert-helper
tamtamchik 588cda9
feat: add version
tamtamchik dad1b1e
feat: update alertingHarness
tamtamchik ccd1318
chore: fixes after review
tamtamchik e0d196c
feat: deploy new version
tamtamchik 2701f13
Merge branch 'feat/vaults' into feat/vaults-alert-helper
tamtamchik 16a86f9
Merge branch 'feat/vaults' into feat/vaults-alert-helper
tamtamchik e9e3d33
Merge branch 'develop' into feat/vaults-alert-helper
tamtamchik c9fd07f
feat: deployed AlertingHarness contract for Lido V3
tamtamchik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,310 @@ | ||
| // SPDX-FileCopyrightText: 2025 Lido <info@lido.fi> | ||
| // SPDX-License-Identifier: GPL-3.0 | ||
|
|
||
| // ======================================================================================================= // | ||
| // DISCLAIMER: This contract is provided for tooling purposes only and is NOT part of the Lido core protocol. | ||
| // It is not audited, and may be updated in the future without notice. | ||
| // ======================================================================================================= // | ||
|
|
||
| // See contracts/COMPILERS.md | ||
| pragma solidity 0.8.25; | ||
|
|
||
| import {ILidoLocator} from "contracts/common/interfaces/ILidoLocator.sol"; | ||
| import {IStakingVault} from "contracts/0.8.25/vaults/interfaces/IStakingVault.sol"; | ||
|
|
||
| import {VaultHub} from "contracts/0.8.25/vaults/VaultHub.sol"; | ||
| import {LazyOracle} from "contracts/0.8.25/vaults/LazyOracle.sol"; | ||
| import {PredepositGuarantee} from "contracts/0.8.25/vaults/predeposit_guarantee/PredepositGuarantee.sol"; | ||
|
|
||
| /// @title AlertingHarness | ||
| /// @dev this contract is NOT a part of the Lido core protocol logic, it is only used for tooling purposes | ||
| contract AlertingHarness { | ||
| /// @notice reference to the Lido locator contract used to resolve protocol contract addresses | ||
| ILidoLocator public immutable LIDO_LOCATOR; | ||
|
|
||
| /// @notice structure containing relevant data from an underlying contract | ||
| /// @param nodeOperator the address of the node operator | ||
| /// @param depositor the address of the depositor | ||
| /// @param owner the address of the owner | ||
| /// @param pendingOwner the address of the pending owner | ||
| /// @param stagedBalance the staged balance | ||
| /// @param availableBalance the available balance | ||
| /// @param beaconChainDepositsPaused the status of the beacon chain deposits | ||
| struct ContractInfo { | ||
| address nodeOperator; | ||
| address depositor; | ||
| address owner; | ||
| address pendingOwner; | ||
| uint256 stagedBalance; | ||
| uint256 availableBalance; | ||
| bool beaconChainDepositsPaused; | ||
| } | ||
|
|
||
| /// @notice structure containing relevant data for a single connected vault | ||
| /// @param vault The address of the vault | ||
| /// @param connection The current connection parameters for the vault (such as limits and owner info) | ||
| /// @param record the current accounting record for the vault (liabilities, report, in/out delta, etc.) | ||
| /// @param quarantineInfo the quarantine info (if any) for the vault from LazyOracle | ||
| /// @param contractData the data from the underlying staking vault contracts | ||
| /// @param pendingActivationsCount the number of pending validator activations in the vault (from PredepositGuarantee) | ||
| struct VaultData { | ||
| address vault; | ||
| VaultHub.VaultConnection connection; | ||
| VaultHub.VaultRecord record; | ||
| LazyOracle.QuarantineInfo quarantineInfo; | ||
| ContractInfo contractInfo; | ||
| uint256 pendingActivationsCount; | ||
| } | ||
|
|
||
| struct VaultConnectionData { | ||
| address vault; | ||
| VaultHub.VaultConnection connection; | ||
| } | ||
|
|
||
| struct VaultRecordData { | ||
| address vault; | ||
| VaultHub.VaultRecord record; | ||
| } | ||
|
|
||
| struct VaultQuarantineInfoData { | ||
| address vault; | ||
| LazyOracle.QuarantineInfo quarantineInfo; | ||
| } | ||
|
|
||
| struct VaultPendingActivationsData { | ||
| address vault; | ||
| uint256 pendingActivationsCount; | ||
| } | ||
|
|
||
| struct VaultContractInfoData { | ||
| address vault; | ||
| ContractInfo contractInfo; | ||
| } | ||
|
|
||
| error ZeroAddress(string _argument); | ||
|
|
||
| /// @notice initializes the AlertingHarness and stores the locator contract address | ||
| /// @param _lidoLocator the address of the Lido locator contract | ||
| constructor(address _lidoLocator) { | ||
| if (_lidoLocator == address(0)) revert ZeroAddress("_lidoLocator"); | ||
|
|
||
| LIDO_LOCATOR = ILidoLocator(_lidoLocator); | ||
| } | ||
|
|
||
| /// @notice retrieves structured data for a single vault | ||
| /// @param _vault the address of the vault to query | ||
| /// @return vault data for the queried vault | ||
| function getVaultData(address _vault) external view returns (VaultData memory) { | ||
| return _collectVaultData( | ||
| _vault, | ||
| _vaultHub(), | ||
| _lazyOracle(), | ||
| _predepositGuarantee() | ||
| ); | ||
| } | ||
|
|
||
| /// @notice retrieves structured data for a batch of vaults in a single call | ||
| /// @param _offset the starting vault index in the hub [0, vaultsCount) | ||
| /// @param _limit the maximum number of vaults to return in this batch | ||
| /// @return batch of VaultData structs for the requested vaults | ||
| function batchVaultData( | ||
| uint256 _offset, | ||
| uint256 _limit | ||
| ) external view returns (VaultData[] memory batch) { | ||
| (VaultHub vaultHub, uint256 batchSize) = _getBatchSize(_offset, _limit); | ||
|
|
||
| if (batchSize == 0) return new VaultData[](0); | ||
|
|
||
| LazyOracle lazyOracle = _lazyOracle(); | ||
| PredepositGuarantee predepositGuarantee = _predepositGuarantee(); | ||
|
|
||
| batch = new VaultData[](batchSize); | ||
| for (uint256 i = 0; i < batchSize; ++i) { | ||
| address vault = vaultHub.vaultByIndex(_offset + i + 1); | ||
| batch[i] = _collectVaultData(vault, vaultHub, lazyOracle, predepositGuarantee); | ||
| } | ||
| } | ||
|
|
||
| /// @notice retrieves batch of VaultHub.VaultConnection structs in a single call | ||
| /// @param _offset the starting vault index in the hub [0, vaultsCount) | ||
| /// @param _limit maximum number of items to return in the batch | ||
| /// @return batch of VaultConnectionData structs for the requested vaults | ||
| function batchVaultConnections( | ||
| uint256 _offset, | ||
| uint256 _limit | ||
| ) external view returns (VaultConnectionData[] memory batch) { | ||
| (VaultHub vaultHub, uint256 batchSize) = _getBatchSize(_offset, _limit); | ||
|
|
||
| if (batchSize == 0) return new VaultConnectionData[](0); | ||
|
|
||
| batch = new VaultConnectionData[](batchSize); | ||
| for (uint256 i = 0; i < batchSize; ++i) { | ||
| address vault = vaultHub.vaultByIndex(_offset + i + 1); | ||
| batch[i] = VaultConnectionData({ | ||
| vault: vault, | ||
| connection: vaultHub.vaultConnection(vault) | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| /// @notice retrieves batch of VaultHub.VaultRecord structs in a single call | ||
| /// @param _offset the starting vault index in the hub [0, vaultsCount) | ||
| /// @param _limit maximum number of items to return in the batch | ||
| /// @return batch of VaultRecordData structs for the requested vaults | ||
| function batchVaultRecords( | ||
| uint256 _offset, | ||
| uint256 _limit | ||
| ) external view returns (VaultRecordData[] memory batch) { | ||
| (VaultHub vaultHub, uint256 batchSize) = _getBatchSize(_offset, _limit); | ||
|
|
||
| if (batchSize == 0) return new VaultRecordData[](0); | ||
|
|
||
| batch = new VaultRecordData[](batchSize); | ||
| for (uint256 i = 0; i < batchSize; ++i) { | ||
| address vault = vaultHub.vaultByIndex(_offset + i + 1); | ||
| batch[i] = VaultRecordData({ | ||
| vault: vault, | ||
| record: vaultHub.vaultRecord(vault) | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| /// @notice retrieves batch of LazyOracle.QuarantineInfo structs in a single call | ||
| /// @param _offset the starting vault index in the hub [0, vaultsCount) | ||
| /// @param _limit maximum number of items to return in the batch | ||
| /// @return batch of VaultQuarantineInfoData structs for the requested vaults | ||
| function batchVaultQuarantines( | ||
| uint256 _offset, | ||
| uint256 _limit | ||
| ) external view returns (VaultQuarantineInfoData[] memory batch) { | ||
| (VaultHub vaultHub, uint256 batchSize) = _getBatchSize(_offset, _limit); | ||
| if (batchSize == 0) return new VaultQuarantineInfoData[](0); | ||
|
|
||
| LazyOracle lazyOracle = _lazyOracle(); | ||
|
|
||
| batch = new VaultQuarantineInfoData[](batchSize); | ||
| for (uint256 i = 0; i < batchSize; ++i) { | ||
| address vault = vaultHub.vaultByIndex(_offset + i + 1); | ||
| batch[i] = VaultQuarantineInfoData({ | ||
| vault: vault, | ||
| quarantineInfo: lazyOracle.vaultQuarantine(vault) | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| /// @notice retrieves batch of VaultPendingActivationsData structs in a single call | ||
| /// @param _offset the starting vault index in the hub [0, vaultsCount) | ||
| /// @param _limit maximum number of items to return in the batch | ||
| /// @return batch of VaultPendingActivationsData structs for the requested vaults | ||
| function batchPendingActivations( | ||
| uint256 _offset, | ||
| uint256 _limit | ||
| ) external view returns (VaultPendingActivationsData[] memory batch) { | ||
| (VaultHub vaultHub, uint256 batchSize) = _getBatchSize(_offset, _limit); | ||
| if (batchSize == 0) return new VaultPendingActivationsData[](0); | ||
|
|
||
| PredepositGuarantee predepositGuarantee = _predepositGuarantee(); | ||
|
|
||
| batch = new VaultPendingActivationsData[](batchSize); | ||
| for (uint256 i = 0; i < batchSize; ++i) { | ||
| address vault = vaultHub.vaultByIndex(_offset + i + 1); | ||
| batch[i] = VaultPendingActivationsData({ | ||
| vault: vault, | ||
| pendingActivationsCount: predepositGuarantee.pendingActivations(IStakingVault(vault)) | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| /// @notice retrieves batch of ContractInfo structs in a single call | ||
| /// @param _offset the starting vault index in the hub [0, vaultsCount) | ||
| /// @param _limit maximum number of items to return in the batch | ||
| /// @return batch of ContractInfo structs for the requested vaults | ||
| function batchStakingVaultData( | ||
| uint256 _offset, | ||
| uint256 _limit | ||
| ) external view returns (VaultContractInfoData[] memory batch) { | ||
| (VaultHub vaultHub, uint256 batchSize) = _getBatchSize(_offset, _limit); | ||
| if (batchSize == 0) return new VaultContractInfoData[](0); | ||
|
|
||
| batch = new VaultContractInfoData[](batchSize); | ||
| for (uint256 i = 0; i < batchSize; ++i) { | ||
| address vault = vaultHub.vaultByIndex(_offset + i + 1); | ||
| batch[i] = VaultContractInfoData({ | ||
| vault: vault, | ||
| contractInfo: _collectContractInfo(IStakingVault(vault)) | ||
| }); | ||
| } | ||
| } | ||
| /// @notice helper to calculate actual batch size based on vault hub | ||
| /// @param _offset the starting vault index in the hub [0, vaultsCount) | ||
| /// @param _limit requested batch size | ||
| /// @return vaultHub the VaultHub contract instance | ||
| /// @return batchSize actual batch size to use (0 if offset out of range) | ||
| function _getBatchSize( | ||
| uint256 _offset, | ||
| uint256 _limit | ||
| ) private view returns (VaultHub vaultHub, uint256 batchSize) { | ||
| vaultHub = _vaultHub(); | ||
| uint256 vaultsCount = vaultHub.vaultsCount(); | ||
|
|
||
| if (_offset >= vaultsCount) return (vaultHub, 0); | ||
|
|
||
| batchSize = _offset + _limit > vaultsCount ? vaultsCount - _offset : _limit; | ||
| } | ||
|
|
||
| /// @notice internal utility to collect vault data from multiple protocol contracts | ||
| /// @param _vault vault address to collect data for | ||
| /// @param vaultHub vaultHub contract instance | ||
| /// @param lazyOracle lazyOracle contract instance | ||
| /// @param predepositGuarantee predepositGuarantee contract instance | ||
| /// @return populated vaultData structure | ||
| function _collectVaultData( | ||
| address _vault, | ||
| VaultHub vaultHub, | ||
| LazyOracle lazyOracle, | ||
| PredepositGuarantee predepositGuarantee | ||
| ) internal view returns (VaultData memory) { | ||
| IStakingVault stakingVault = IStakingVault(_vault); | ||
| return VaultData({ | ||
| vault: _vault, | ||
| connection: vaultHub.vaultConnection(_vault), | ||
| record: vaultHub.vaultRecord(_vault), | ||
| quarantineInfo: lazyOracle.vaultQuarantine(_vault), | ||
| pendingActivationsCount: predepositGuarantee.pendingActivations(stakingVault), | ||
| contractInfo: _collectContractInfo(stakingVault) | ||
| }); | ||
| } | ||
|
|
||
| /// @notice helper to collect staking vault data from a single staking vault | ||
| /// @param _stakingVault the staking vault to collect data from | ||
| /// @return populated stakingVaultData structure | ||
| function _collectContractInfo(IStakingVault _stakingVault) internal view returns (ContractInfo memory) { | ||
| return ContractInfo({ | ||
| nodeOperator: _stakingVault.nodeOperator(), | ||
| depositor: _stakingVault.depositor(), | ||
| owner: _stakingVault.owner(), | ||
| pendingOwner: _stakingVault.pendingOwner(), | ||
| stagedBalance: _stakingVault.stagedBalance(), | ||
| availableBalance: _stakingVault.availableBalance(), | ||
| beaconChainDepositsPaused: _stakingVault.beaconChainDepositsPaused() | ||
| }); | ||
| } | ||
|
|
||
| /// @notice helper to resolve the current VaultHub contract from the locator | ||
| /// @return contract instance of VaultHub | ||
| function _vaultHub() internal view returns (VaultHub) { | ||
| return VaultHub(payable(LIDO_LOCATOR.vaultHub())); | ||
| } | ||
|
|
||
| /// @notice helper to resolve the current LazyOracle contract from the locator | ||
| /// @return contract instance of LazyOracle | ||
| function _lazyOracle() internal view returns (LazyOracle) { | ||
| return LazyOracle(LIDO_LOCATOR.lazyOracle()); | ||
| } | ||
|
|
||
| /// @notice helper to resolve the current PredepositGuarantee contract from the locator | ||
| /// @return contract instance of PredepositGuarantee | ||
| function _predepositGuarantee() internal view returns (PredepositGuarantee) { | ||
| return PredepositGuarantee(LIDO_LOCATOR.predepositGuarantee()); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| #!/bin/bash | ||
| set -e +u | ||
| set -o pipefail | ||
|
|
||
| export NETWORK=${NETWORK:="hoodi"} # if defined use the value set to default otherwise | ||
| export RPC_URL=${RPC_URL:="http://127.0.0.1:8545"} # if defined use the value set to default otherwise | ||
|
|
||
| export DEPLOYER=${DEPLOYER:="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"} # first acc of default mnemonic "test test ..." | ||
| export GAS_PRIORITY_FEE=1 | ||
| export GAS_MAX_FEE=2 | ||
| export GAS_LIMIT=20000000 | ||
|
|
||
| export NETWORK_STATE_FILE=${NETWORK_STATE_FILE:="deployed-hoodi.json"} | ||
| export STEPS_FILE=harness/steps-deploy-alerting-harness.json | ||
|
|
||
| yarn hardhat --network $NETWORK run --no-compile scripts/utils/migrate.ts |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "steps": ["harness/steps/0000-check-env", "harness/steps/0100-deploy-alerting-harness"] | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.