LidoOracle is a contract where oracles send addresses' balances controlled by the DAO on the ETH 2.0 side. The balances can go up because of reward accumulation and can go down due to slashing and staking penalties. Oracles are assigned by the DAO.
The following mechanisms are also worth mentioning.
The report variant is a report with a counter - how many times this report was pushed by
oracles. This strongly simplified logic of
_getQuorumReport, because in the majority of cases, we
only have 1 variant of the report so we just make sure that its counter exceeded the quorum value.
The important note here is that when we remove an oracle (with
also need to remove her report from the currently accepted reports. As of now, we do not keep a
mapping between members and their reports, we just clean all existing reports and wait for the
remaining oracles to push the same epoch again.
To calculate the percentage of rewards for stakers, we store and provide the following data:
preTotalPooledEther- total pooled ether mount, queried right before every report push to the Lido contract,
postTotalPooledEther- the same, but queried right after the push,
lastCompletedEpochId- the last epoch that we pushed the report to the Lido,
timeElapsed- the time in seconds between the current epoch of push and the
lastCompletedEpochId. Usually, it should be a frame long: 32 12 225 = 86400, but maybe multiples more in case that the previous frame didn't reach the quorum.
It is important to note here, that we collect post/pre pair (not current/last), to avoid the influence of new staking during the epoch.
To calculate the APR, use the following formula:
In order to limit the misbehaving oracles impact, we want to limit oracles report change by 10% APR increase in stake and 5% decrease in stake. Both values are configurable by the governance in case of extremely unusual circumstances.
Note that the change is evaluated after the quorum of oracles reports is reached, and not on the individual report.
And the logic of reporting to the Lido contract got a call to
_reportSanityChecks that does
the following. It compares the
postTotalPooledEther (see above) and
- if there is a profit or same, calculates the APR, compares it with the upper bound. If was
above, reverts the transaction with
- if there is a loss, calculates relative decrease and compares it with the lower bound. If was
below, reverts the transaction with
To provide the external contract with updates on report pushes (every time the quorum is reached among oracle daemons data), we provide the following setter and getter functions. It might be needed to implement some updates to the external contracts that should happen at the same tx the rebase happens (e.g. adjusting uniswap v2 pools to reflect the rebase).
And when the callback is set, the following function will be invoked on every report push.
The arguments provided are the same as described in section above.
Return the Lido contract address.
Return the number of exactly the same reports needed to finalize the epoch.
Return the lower bound of the reported balance possible decrease. See above about sanity checks.
Return the receiver contract address to be called when the report is pushed to Lido.
Return the current reporting bitmap, representing oracles who have already pushed their version of report during the expected epoch.
Every oracle bit corresponds to the index of the oracle in the current members list
Return the current reporting variants array size.
Return the current reporting array element with index
Return epoch that can be reported by oracles.
Return the current oracle member committee list.
Return the initialized version of this contract starting from 0.
Return beacon specification data.
Return the epoch calculated from current timestamp.
Return currently reportable epoch (the first epoch of the current frame) as well as its start and end times in seconds.
Return last completed epoch.
Report beacon balance and its change during the last frame.
Set the upper bound of the reported balance possible increase in APR to
_value. See above about
Set the lower bound of the reported balance possible decrease to
_value. See above about sanity
Set the receiver contract address to
_addr to be called when the report is pushed.
Specify 0 to disable this functionality.
Update beacon specification data.
Initialize the contract v2 data, with sanity check bounds
initialize was removed from v2 because it is not needed once the contract is
initialized for the first time, that happened in v1. Instead we added
initialize_v2 function that
initializes newly added variables, updates the contract version to 1.
_member to the oracle member committee list.
Remove '_member` from the oracle member committee list.
Set the number of exactly the same reports needed to finalize the epoch to
Accept oracle committee member reports from the ETH 2.0 side. Parameters:
_epochId- beacon chain epoch
_beaconBalance- balance in gwei on the ETH 2.0 side (9-digit denomination)
_beaconValidators- number of validators visible in this epoch