[ODAO] Rewards Tree Spec v4

Hey everyone,

I’d like to make a quick post about the state of Rewards Tree Spec v4 to keep everyone in the loop.


The spec has now been written and is ready for review.

It makes one change to the existing rules:

In Collateral Rewards, the way nodeEffectiveStake is determined has changed. Previously, it relied on this contract call:

nodeEffectiveStake := RocketNodeStaking.getNodeEffectiveRPLStake(nodeAddress)

Now, it is calculated explicitly for each node using the number of active minipools according to the Beacon Chain, not according to the contracts. Please see that section for more details on the new calculation method.

I also have a reference implementation built, which I use to build my nightly preview trees alongside the canonical ones using ruleset v3.

I’ll include the details on v4 below.


Currently, during RPL rewards calculation, the Smartnode asks the contracts what the effective stake for each node is and determines the RPL distribution for everyone based on those numbers.

The contracts internally check how many minipools the node had in the staking state at the end of the interval and uses that to calculate the minimum and maximum RPL collateral.

For those that appreciate the technical details, here’s some simplified pseudocode that shows how the effective stake is calculated:

min := 16 / rplPriceRatio * numberOfStakingMinipools * 0.1
max := 16 / rplPriceRatio * numberOfStakingMinipools * 1.5

effectiveStake = stakedRpl
if stakedRpl < min {
    effectiveStake = 0
} else if stakedRpl > max {
    effectiveStake = max

A minipool that has entered staking status is one that has deposited all 32 ETH and is ready for activation on Beacon. It enters staking the moment the second deposit (the stake transaction) is validated and added to the chain.

The problem here is that the contracts don’t know when a validator has actually been activated on Beacon or exited Beacon; this requires the Oracle DAO to shuttle state updates over to the contracts. For a few reasons that became apparent during Atlas development, the team has decided not to put this particular behavior into the watchtower.

(Note: those are out-of-scope for this particular thread and can be explored further in a separate one, so I’ll stick to the rewards spec here.)

Without knowing that a minipool has exited, the contracts will happily keep a staking minipool in the staking status and thus, it will continue to be eligible for RPL rewards even though it is now dormant and can be withdrawn-from at any time (predicated on the Beacon chain’s withdrawal queue).

Ruleset v4 changes this by changing the way effective stake is calculated for each node. Instead of asking the contracts to calculate it, the Smartnode will now calculate it manually using the activation and exit times of each minipool on the Beacon Chain.

This is the only change that ruleset v4 includes.


At its core, this ruleset changes the way effectiveStake for each node is calculated by changing the number of eligibleMinipools.
Previously, eligibleMinipools was simply equal to the number of minipools in the staking state.
With this ruleset, eligibleMinipools is now determined by querying the Beacon Chain.

The following process is run for each node:

  1. Get the list of minipools for the node. Ignore all minipools that are not in the staking state.

  2. For each staking minipool, get the validator pubkey. Ask the Beacon Chain for the status of this validator (using /eth/v1/beacon/states/<targetBcSlot>/validators?id=0x<pubkey>, where targetBcSlot is the checkpoint slot for this interval).

    1. This will have two important fields: activation_epoch and exit_epoch - the epoch the minipool was activated on, and exited, the Beacon Chain.
  3. If activation_epoch is before the checkpoint interval’s ending epoch, and if exit_epoch is after the checkpoint interval’s ending epoch, then this is an eligible minipool.

  4. Run the calculation above in the Rationale section, but replace numberOfStakingMinipools with eligibleMinipools.

The rest of the rewards generation is calculated normally.


I would like to include this release for Interval 6 which occurs on February 16th. At the time of this writing, this is in 5 weeks.

This would give @Peteris and I 2-3 weeks to compare our implementations, iron out any bugs, and incorporate it into a future Smartnode release. The Oracle DAO would then have 2-3 weeks to update to it at their discretion.

The spec itself has been reviewed by some of the community members for about a week already, and they didn’t see any issues in moving forward with it so here we are.


This particular change doesn’t exactly fall under the “bugfix” category (though I suppose you could make an argument for it), but it doesn’t fall under the “massive changes” category either like the notion of prorating RPL rewards by attestation performance. I think it’s closer to the former than the latter, so I’m inclined to push it through.

That being said, I think it’s important to gauge everyone else’s thoughts on the matter before unilaterally doing it, so please discuss this change here (even if it’s just a simple thumbs up).

Thanks everyone!


Seems like a desirable and non-controversial improvement to me as well. :+1:

A few nitpicks:

Probably a transliteration error, but don’t you mean “if exit_epoch is after the checkpoint interval’s [starting] epoch” here for consistency’s sake? After all, the minipool is still active for the initial part of the interval in which it exits.

The simplified pseudocode refers to 16 (eth), so this will be revised again with Atlas, when a node can have any combination of 16 and 8 eth minipools? Or is this already accounted for in the actual code?

“if exit_epoch is after the checkpoint interval’s [starting] epoch” here for consistency’s sake? After all, the minipool is still active for the initial part of the interval in which it exits.

I am referring to the checkpoint interval’s [ending] epoch, not the start. If you exit halfway through the interval, that minipool is not eligible for RPL even though it was partially active during that interval - that’s the way the current reward system was designed, and in the name of making minimal changes I kept it that way intentionally. It does mean that you should wait until a checkpoint to exit so you are eligible for rewards if exiting would bring you below the min or above the max.

The simplified pseudocode refers to 16 (eth), so this will be revised again with Atlas, when a node can have any combination of 16 and 8 eth minipools?

This will be revised again with Atlas, as LEBs affect both RPL and ETH rewards so the whole thing needs to be overhauled to compensate for them. That’s on my radar and expected to become ruleset v5.


Agree non-controversial and a great idea. As with any change to rewards, timing should go into effect at the start of a new cycle rather at the end of cycle, so that implementation is not directed backwards retroactively at past performance.

Seems like a logical progression as we start to move into a more diversified set of minipools and modularization of minipool specific variables. Thumbs up!

Agree this is a strict improvement and support the update :slight_smile:

Thanks @jcrtp for the “some simplified pseudocode that shows how the effective stake is calculated” , for a non technical like me . Understanding clearly and agreed.

Anything that places less duties on the oDAO is good in my book.

Peteris and I have had consistent merkle trees for the past 2 weeks, so I consider this implementation to be good. I’ve released this update in Smartnode v1.7.4.