https://github.com/rocket-pool/rocketpool/compare/master...v1.2
- LEB8s
- Migration to LEB8
- Deposit credit system
- Solo staker migration
- Simplified minipool queue
- Use queued minipool ETH
- Dynamic deposit pool limit
- Cleanup effective RPL stake
- Improve minipool gas efficiency
- Reward interval on chain
- Minipool distribution logic change
LEB stands for Lower ETH Bonded. It refers to a reduction in the bonding amount that node operators provide when creating a minipool. Previously, this was hardcoded to 16 ETH, but with Atlas we are changing this to be user selectable between 16 and 8 ETH.
- On deposit, node operators can select between 8 and 16 ETH bonding amounts. This is done with the new
_bondValue
parameter thatrocketNodeDeposit.deposit
takes. - When calculating user and node share on withdrawal, we now take into account the proportion of user and node capital. Previously, we assumed half and half.
RocketNodeDistributorDelegate.distribute
has been updated to take into account the node operator's ETH collateralisation ratio. This was previously assumed to always be 50%. Note: beacuse all priority fees and MEV pool into a single distributor per node operator, this is not a 100% accurate calculation. We are not taking into account the collateralisation of each minipool individually. Instead we are taking an overall collateralisation of each node operator.RocketNodeManager.getAverageNodeFee
has been updated to be a weighted average of the different collateral ratios. This is once again an approximation as we are not calculating the node fee on a per minipool basis.
We have included a way for existing 16 ETH bonded minipools to migrate to an LEB8.
- Node operators call
beginReduceBondAmount()
on their minipool. - There is a 7 day wait period where the oDAO can vote to cancel the reduction. This is to prevent slashed validators from attempting to bypass their slashing by migrating.
- oDAO members can call
voteCancelReduction()
on the minipool. If 51% of oDAO members call this, the pool is flagged as not able to reduce. - If after 7 days pass, the node operator has 2 days to then call
reduceBondAmount(uint256 _amount)
which adjusts the user and node balances to their new value and increases the node operators "deposit credit" by the same amount. - In order to prevent an exploit where a node operator could upgrade their delegate to the latest version, reduce their bond amount, then rollback their delegate to the previous version which still thinks the node operator has provided 16 ETH, we've included an intentional corrupting of state during the bond reduction process. During
reduceBondAmount(uint256 _amount)
,userDepositBalanceLegacy
(which is in the storage slot ofuserDepositBalance
for the previous delegate) is set to 2**256-1. If a node operator rolls back after reducing bond, then the previous delegate will seeuserDepositBalance
as a very large number. This results in any calls to distribute sending the entire balance to the deposit pool.
In order to support migration to LEB8s, we've added a deposit credit system which node operators can use to create new minipools without posting their own ETH. ETH in the deposit credit is taken from the deposit pool when available.
- When a minipool is migrated to an LEB8, 8 ether is added to a node's deposit credit ("node.deposit.credit.balance").
- During
RocketNodeDeposit.deposit
, if a node operator has a deposit credit, this credit is deducted and ETH is taken from the deposit pool. - Deposit credit is not withdrawable. It may only be used to create new minipools.
In the next Ethereum consensus layer hard fork, validators will have the ability to change from BLS withdrawal credentials to execution credentials. This offers Rocket Pool an amazing opportunity to onboard solo stakers who still have BLS withdrawal credentials and want to run Rocket Pool minipools without having to go through the exit and entry queues.
- A zombie minipool is created which references a solo stakers public key. It is in "Initialised" state but is not added to the usual minipool queue.
- The node operator then signs and broadcasts a BLSToExecutionChange message which sets their validator's withdrawal credentials to the zombie minipool.
- The oDAO monitors these zombie minipools and detects when the withdrawal change is complete. If after 7 days the withdrawal credentials are not set correctly, the oDAO votes to scrub the minipool.
- If after 14 days the zombie minipool has not been scrubbed (meaning the oDAO is happy for the migration to proceed), the node operator can call "promote()" to promote the minipool to a bonafide minipool and the status is changed to "staking".
With the removal of full and empty minipools, we've redesigned the minipool queue to be a single queue that supports multiple types (16 ETH or 8 ETH) as opposed to three separate queues that worked in series. i.e. Previously, half pools were prioritised over refunding full minipools.
- The existing queue remains and is designated "Legacy".
RocketMinipoolQueue
features both the legacy queue and the new queue.RocketDepositPool.assignDeposits()
will continue processing the legacy queue until it is empty then switch over to servicing the new single queue.- All new minipools are now of type
MinipoolType.Variable
to distinguish them from the legacyFull
andHalf
types. - All new minipools now prestake 1 ETH on creation (instead of 16 previously). This means that regardless of the bonding amount (e.g. 16 or 8 ETH), all minipools require 31 additional ETH once they are popped from the queue to then go to the "Prestake" state.
Previously, when there is a number of minipools waiting in the queue for paired ETH from the deposit pool, a significant amount of ETH may be sitting there unproductive. This change aims to make use of that ETH by making it available to minipools earlier on in the queue.
- As part of the simplified minipool queue, only 1 ETH is immediately staked on creation of a minipool. The remaining 15 or 7 ETH is made available to the deposit pool.
- Importantly, rETH is not generated when the ETH is added to the deposit pool and a separate variable "deposit.pool.node.balance" is used to keep track of this amount so that it is not included in balance submissions.
- When funds are taken from the deposit pool to pair with minipools popping off the queue, this variable is deducted.
The overall result is that ETH that was previously sitting there doing nothing can be used to bring minipools in the queue online faster. Node operators will start earning yield sooner. rETH holders benefit from the additional yield of the preivously unproductive ETH.
Currently the deposit pool has a fix limit as determined by a pDAO configurable parameter. Even if the minipool queue has capacity in excess of the deposit pool, the maximum deposit is defined by this fixed limit. This change makes it so whne the minipool queue capacity exceeds the deposit limit, the deposit limit grows to match it.
- This is implemented in
RocketDepositPool.deposit()
which now queriesRocketMinipoolQueue.getEffectiveCapacity()
during its capacity calculation.
The old reward system required keeping track of effective RPL stake per node operator. This value depended on the price of RPL which is oracled to chain once a day. This required an annoying block which prevented people from creating minipools, or staking RPL, between a price checkpoint block and when the oracle executed a price change. All of this functionality can now be removed since we've moved to an off-chain RPL reward calculation.
- Removed all mentions of effective RPL stake across the codebase.
Every minipool currently requires the deployment of a smart contract which handles delegate upgrades and rollbacks and delegates all other calls to its current delegate contract. This change adds another layer of indirection by sliding in another lightweight proxy. This saves a significant amount of gas for every minipool deployed.
- Minipool creation now deploys a new
RocketMinipoolProxy
which simply delegates all calls to a new contractRocketMinipoolBase
that is burnt in at time of creation.RocketMinipoolBase
handles delegate upgrade and rollbacks and then delegates all other calls to its respective delegate.
In Redstone we changed to an offchain reward generation process that used a merkle tree to distribute rewards. We've since discovered a scaling issue which makes it difficult to scan over events to find the reward snapshot event efficiently. This change simply adds a mapping on chain that maps reward indices to the block in which the event was submitted it so that off chain indexer can find the event more efficiently.
RocketRewardsPool
now does asetUint(keccak256("rewards.pool.interval.execution.block", _submission.rewardIndex), block.number);
We now know how withdrawals are going to happen from the beaconchain. Rewards will be skimmed every so often back to the execution layer withdrawal credential address. With this knowledge, we redesigned the distribution logic to handle skimmed rewards as well as final withdrawal.
We have removed the Withdrawable
status from minipools. Previously, the oDAO would come to consensus that a validator is withdrawable and the minipool would be changed into this new state.
Now we only use the minipool balance for distribution logic. The logic is as follows:
Balance >= 32 ETH
- The 32 ETH portion is distributed back to the user/node based on the collateral ratio
- Anything over 32 ETH is distributed per the rewards distribution logic (taking into account commission)
- No RPL penalties are applied
- If NO calling this, finalise the pool (RPL stake unlocked)
- If user calling, they must call
beginUserDistribute
and wait a configurable period of time before distributing
Balance < 32 ETH && >= 8 ETH
- ETH is distributed back to the user with any shortfall being taken out of the node's share
- Anything remaining is sent to node
- If NO calling, finalise the pool (RPL is slashed if required or unlocked)
- If user calling, they must call
beginUserDistribute
and wait a configurable period of time before distributing
Balance < 8 ETH
- ETH is treated as rewards and distributed based on the reward distribution logic (taking into account commission)
- NO or user can freely call