Skip to content

Instantly share code, notes, and snippets.

@kanewallmann
Last active November 15, 2022 07:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kanewallmann/cace4e37a19b14e1dc0335389503ee7d to your computer and use it in GitHub Desktop.
Save kanewallmann/cace4e37a19b14e1dc0335389503ee7d to your computer and use it in GitHub Desktop.

https://github.com/rocket-pool/rocketpool/compare/master...v1.2

Change List

LEB8s

Description

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.

Implementation

  • On deposit, node operators can select between 8 and 16 ETH bonding amounts. This is done with the new _bondValue parameter that rocketNodeDeposit.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.

Migration to LEB8

Description

We have included a way for existing 16 ETH bonded minipools to migrate to an LEB8.

Implementation

  • 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 of userDepositBalance for the previous delegate) is set to 2**256-1. If a node operator rolls back after reducing bond, then the previous delegate will see userDepositBalance as a very large number. This results in any calls to distribute sending the entire balance to the deposit pool.

Deposit credit system

Description

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.

Implementation

  • 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.

Solo staker migration

Description

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.

Implementation

  • 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".

Simplified minipool queue

Description

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.

Implementation

  • 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 legacy Full and Half 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.

Use queued minipool ETH

Description

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.

Implementation

  • 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.

Note

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.

Dynamic deposit pool limit

Description

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.

Implementation

  • This is implemented in RocketDepositPool.deposit() which now queries RocketMinipoolQueue.getEffectiveCapacity() during its capacity calculation.

Cleanup effective RPL stake

Description

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.

Implementation

  • Removed all mentions of effective RPL stake across the codebase.

Improve minipool gas efficiency

Description

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.

Implementation

  • Minipool creation now deploys a new RocketMinipoolProxy which simply delegates all calls to a new contract RocketMinipoolBase that is burnt in at time of creation. RocketMinipoolBase handles delegate upgrade and rollbacks and then delegates all other calls to its respective delegate.

Reward interval on chain

Description

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.

Implementation

  • RocketRewardsPool now does a setUint(keccak256("rewards.pool.interval.execution.block", _submission.rewardIndex), block.number);

Minipool distribution logic change

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.

Implementation

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment