This document presents an approach for implementing a permissionless method for rewards distribution in Curated and Simple DVT modules.
distributeReward
method in each staking module.Currently, for the Curated and Simple DVT modules, the finalization hook initiates reward distribution as part of the last transaction in the third phase. However, for the CSM module, rewards are distributed by a performance oracle.
The situation where some modules distribute rewards during the third phase of the oracle report, while others use alternative mechanisms, complicates the accounting report and could potentially become a source of bugs in the future. Additionally, this approach increases complexity as it necessitates consideration of gas consumption for the distribution of rewards.
The reward distribution for a single node operator in curated-based modules consumes 29,500 gas. For the full module with 200 operators, the total gas consumption amounts to 5.9 million.
During the first wave of SimpleDVT operator onboarding, 12 new operators were added.
Gas consumption was analyzed for the submitReportExtraDataEmpty
transaction in the third phase, which does not contain updates to exited or stuck validator statistics and only performs reward distribution.
Considering the plans for SimpleDVT operator onboarding and a possible slight increase in the number of node operators for the curated module, we can estimate that reward distribution for 200 operators in the SimpleDVT module and 50 operators in the Curated module (250 operators in total) will require 7.5 million gas.
A very large transaction could potentially take longer to process or even stall, as validators might delay including it due to its size and the commitment of a large portion of a block's gas limit.
Introducing additional SimpleDVT modules, each consuming up to 5.9 million gas for reward distribution, risks reaching the maximum Ethereum block limit.
The proposed design suggests that reward distribution should be decoupled from the finalization hook and instead be executed separately from the Accounting Oracle report, using permissionless distributeReward
method for the each staking module.
The distributeReward
method can be called at any time after the third phase of the oracle report is completed and before the start of the second phase processing of the next oracle report. This limitation exists because the total module reward is transferred to the module during the second phase of the oracle report, but distribution among node operators can only begin after the validators' statistics per node operator have been submitted to the module during the third phase of the oracle report.
It's required to develop a bot that will trigger distributeReward
permissionless method after each Accounting Report.
Proposed design suggest that bot will periodically check target modules state and will trigger reward distribution once oracle report completed, depend on the report state (positive, negative, failed) and the moment when bot trigger reward distribution several cases possible.
Summary: Reward can be distributed at any time after the completion of the third phase of the oracle report and before the start of the second phase processing of the next oracle report.
Currently, it is also possible for situations to arise where rewards are not distributed for a couple of days.
In such cases rewards accumulated and we distribute rewards based on the latest report; previous reports are not retained or considered in the distribution process. In the worst-case scenario, if the number of active validators changes significantly for some operators, it might result in inaccurate rewards distribution.
However, the occurrence where rewards are not distributed on the report day is exceptional and should not happen under normal operation of the oracle. Such instances are treated as incidents and are addressed by the ValSet team. It's worth noting that during the previous years, there was only one instance when rewards were not distributed on the report day.
In summary, while the fundamental approach to reward distribution among node operators within a module remains unchanged, we are planning a minor adjustment to the timing of these distributions. Currently, rewards are distributed as part of the Third Phase report. Going forward, we intend to separate this process from the Third Phase, opting to distribute reward in a separate transactions following the Third Phase.
Once the delivery of third-phase report updates is complete, the Accounting Oracle triggers the Finalization hook (calling the Staking Router's onValidatorsCountsByNodeOperatorReportingFinished
). Within this method, for each staking module, we call onExitedAndStuckValidatorsCountsUpdated
. This signals that all Node Operator updates have been successfully delivered and the Accounting Oracle report is finalized.
Currently, rewards distribution in curated-based staking modules occurs within the onExitedAndStuckValidatorsCountsUpdated
method. The proposed solution is to update this method to mark the module as ready for reward distribution instead of distributing the reward directly. The actual reward distribution will subsequently start within the distributeReward method.
It's worth mentioning the existing onRewardsMinted
method, which is used by the Staking Router during the second phase of the Accounting Oracle report to notify each Staking Module that the total module reward has been transferred to the module. In this method, we block reward distribution until the third phase report is finished (as per onExitedAndStuckValidatorsCountsUpdated
above), ensuring that rewards can be distributed among node operators.
NodeOperatorRegistry.sol
code example:
For the CSM module reward will be distributed by a dedicated performance oracle.
Proposed design will not affect CSM module, the new distributeReward
method required only for Curated-based modules.
No addition changes are required in the case when the Accounting Oracle third phase report is empty.
The AccountingOracle's submitReportExtraDataEmpty
is called, which validates the report state and calls the finalization hook (Staking Router onValidatorsCountsByNodeOperatorReportingFinished
).
After that the new Reward bot can trigger reward distribution.
As an alternative solution, a claimable reward approach was considered.
There is an existing prototype written by KRogLA that contains a RewardsDistributor library for claimable reward approach.
Each curated-based module tracks node operators claims and allows each node operator to claim the total accumulated rewards they have earned at any given moment.
The flow can be observed in this Excel preview .
The advantage of this approach is its decentralized nature. Node operators gain control over when they receive rewards, and the cost of reward transfers becomes their responsibility. However, the implementation will require significantly more time as it necessitates extensive changes to the on-chain contract of curated-based modules. The mechanism for penalizing stuck validators requires further elaboration, and it will be necessary to handle the accumulation of division residues and cases where node operators, for some reason, do not claim their rewards.
As a result, the implementation of this approach will take much more time. That is why a simpler approach with a bot for rewards distribution was preferred.