⚠️ THE DOCUMENT IS UNDER CONSTRUCTION ⚠️
DISCLAIMER
All of the magic numbers in the doc are DAO-controlled.
They are fixed here as a reasonable defaults for initial proposal.
Solidity structure definition:
struct Report {
// Consensus info
uint256 _slotId;
// CL values
uint256 _clValidators;
uint64 _clBalanceGwei;
// Staking router
address[] _stakingModules;
uint256[] _nodeOperatorsWithExitedValidators;
uint256[] _exitedValidatorsNumbers;
// EL values
uint256 _withdrawalVaultBalance;
// decision
uint256 _requestIdToFinalizeUpTo;
bool _bunkerModeFlag; // todo: to be utilized later
}
Lido:
function handleOracleReport(
// Oracle report timing
uint256 _timeElapsed,
// CL values
uint256 _clValidators,
uint256 _clBalance,
// EL values
uint256 _withdrawalVaultBalance,
uint256 _elRewardsVaultBalance,
// Decision about withdrawals processing
uint256 _requestIdToFinalizeUpTo,
uint256 _finalizationShareRate
) external returns
StakingRouter:
// Staking router
address[] _stakingModules;
uint256[] _nodeOperatorsWithExitedValidators;
uint256[] _exitedValidatorsNumbers;
WithdrawalQueue:
uint256 _previousReportTimestamp,
bool isBunkerMode
Can't exceed the balance at the current block.
if (_withdrawalVaultBalance > WithdrawalsVault.balance) {
revert IncorrectWithdrawalsVaultBalance();
}
Prohibit >5% one-off decrease.
Have to account for withdrawals vault (why: the first week of the Shapella-enabled reports with massive skimmed rewards, leading to the lowered consensus layer balance)
_unifiedPostCLBalance = _postCLBalance + _withdrawalVaultBalance;
if ((_preCLBalance - _unifiedPostCLBalance) > 5%) {
revert IncorrectCLBalanceDecrease();
}
NOTE: was for the whole TVL before.
Prohibit >10% annual increase.
Must not account for withdrawals vault (why: suppose all validators had less than 30 ETH, i.e. unable to been skimmed)
reportBalanceDiff = (_postCLBalance - _preCLBalance);
annualBalanceDiff = reportBalanceDiff * 365 days / _timeElapsed;
if (annualBalanceDiff > 10%) {
revert IncorrectBeaconBalanceIncrease();
}
NOTE: was for the whole TVL before
Networks allows changing a validator set only within a certain churn limit.
55 validators per epoch correspond to the state if roughly 95% of total Ether supply is staked.
churnLimit = 55 * _timeElapsed / 1 epoch;
if (_appearedValidators > churnLimit) {
revert IncorrectAppearedValidators();
}
if (_exitedValidators > churnLimit) {
revert IncorrectExitedValidators();
}
appeared
exited
1h before the reported slot or below DAO defined delay (set temporary)
lastRequestTimestamp = withdrawalRequest(_requestIdToFinalizeUpTo).requestBlock;
timeDiff = max(refReportTimestamp - 1h) - lastRequestTimestamp;
if (timeDiff < 0) {
revert IncorrectRequestFinalization();
}
No more than 27% apr increase per single report.
// initial value
limiterVal = 27% of TVL
// account for `beaconBalanceDiff`
// NB: can either increase or decrease `limiterVal`
limiterVal -= beaconBalanceDiff
// saturate `_reportedWCBalance`
_reportedWCBalance = min(
limiterVal,
_reportedWCBalance
);
limiterVal -= _reportedWCBalance;
// saturate `_ELRewardsVaultBalance`
_ELRewardsVaultBalance = min(
limiterVal,
_ELRewardsVaultBalance
)
limiterVal -= _ELRewardsVaultBalance;
// saturate coverage
applyCoverage(limiterVal)
Solidity structure definition:
struct Report {
uint256 _slotId,
address[] _stakingModules,
uint256[] _nodeOperatorIds,
uint256[] _validatorIds,
bytes[] _validatorPubkeys
}
TODO (tooling?)
For each particular node operator validatorId
can't be less than previously reported (i.e., The old validators are have to be exited before the new ones).