The community and core team have had many discussions about Low ETH Bonded (LEB) minipools recently. Most of the discussion has been focused on the question of what value of ETH can we safely reduce the collateral requirement to and whether reducing the ETH collateral requires a change to the RPL staking parameters too. There is on-going debate and research about these questions and while they are the most important ones to answer, there are also other design questions and technical implementation implications that need to be considered and have not had much brain power spent on them.
As such, this post will focus on the other design and technical questions and leave the ETH and RPL collateral question to a future more concentrated post.
How to handle rewards up to the point of migrating from 16 ETH to LEB pool?
There is (currently) no mechanism on the Execution Layer (EL) for reading the current balance of a validator. It’s for this reason we use the oDAO to submit balances to the EL periodically. Therefore, when we allow Node Operators (NO) to migrate from their current 16 ETH collateralised minipool to a LEB, we do not have the ability to record what the current balance is on the Consensus Layer (CL) at that time. And so when the validator finally exits, we would have no way of calculating the correct pre-LEB rewards.
For example, a minipool has been operating for 12 months as a regular 16 ETH collateral minipool. It has accrued 1 ETH of beacon rewards up until this point. At 15% node commission the NOs entitlement is 0.575 ETH. If the NO was to migrate the minipool to an LEB, and for the sake of simplicity in this example we chose 4 ETH node collateral requirement and keep the 15% commission, without any process to capture the rewards up until this point, the NO would now only be entitled to 0.25625 ETH if the exited. This means the NO loses 0.31875 ETH by migrating.
It is also likely that node commission will be different between 16 ETH and LEB minipools which further complicates this migration.
Solutions
1. Don’t allow migrations
Only alow new minpools to be LEBs.
Pros:
- Least amount of complexity added to the protocol. Complexity can make it difficult to make changes later and also increases surface area of exploits.
Cons:
- Contrary to design goal of expanding our NO supply.
- NOs would have to wait for withdrawals, exit their minipool, wait in the exit queue, spin up a new LEB minipool. Wasting gas and time.
2. Allow migrations but don’t track rewards
Leave it up to the NO to decide if migrating is best for them based on their time frame. The additional yield achieved via LEB should exceed the rewards foregone after some period of time.
Here are some very rough numbers. Our longest running minipool has, at the time of writing, a 33.17687 ETH balance. This equates to 0.6767 ETH rewards for the node operator. If we dropped to 4 ETH and retained the 15% node commission rate, the NOs entitlement to the rewards would drop to 0.301573 ETH which is a loss of 0.375127 ETH. However, the NOs yield would increase from 4.98% to 8.88% (not counting MEV). This represents an increase of 1.248 ETH a year in rewards and approximately, 3.5 months to break even. The increase in MEV yield would make this even shorter.
If node commission is lowered so that rETH receives a portion of the additional yield, this problem is exacerbated.
Pros:
- Requires no extra complexity to minipool distribution logic.
Cons:
- Doesn’t handle slashed minipool balances (if minipool has been slashed significantly, there’s no protection against them migrating to LEB and getting a refund). For this reason, we believe this option is a nonstarter.
- NOs have to decide to forgo rewards they have already accured in exchange for the increased yield.
3. Line in the sand merkle tree
We pick a CL block number as close as possible to the time LEBs are going to be introduced and do a snapshot of all minipool CL balances on that block. A merkle tree is generated that contains the current balance and commission rate for each minipool. The root is published to the EL. When a NO migrates to an LEB, they submit a proof which is validated against the root. The node’s share of the rewards up until this point is stored in the minipool and it is then migrated to an LEB. On distribution, there is additional logic which takes the stored balance and commission rate at the snapshot block and calculates the appropriate rewards from those inputs.
Pros:
- NOs who migrate only forgo the delta in rewards between the line-in-the-sand block and when they migrate which will be insignificant for people who migrate sooner rather than later
- Solves the problem of how to handle slashed minipools (will go into more detail below)
Cons:
- The longer NOs wait to migrate the more rewards they are forgoing when they do finally migrate.
4. Multi-step migration process
NOs would submit a request to migrate which the oDAO would queue. The oDAO would then periodically snapshot the balance and commission of the queued minipools and submit it to chain (could also be a merkle tree and done in batches). NOs could then perform the second step in the migration process which would be another transaction. This transaction would contain the relevant proof and leaf node data that verifies their balance and stores it on the minipool contract along with the old commission rate. Then much like the line-in-the-sand option, there would be additional logic on distribution to calculate the rewards.
Pros:
- There is no immediate pressure for NOs to migrate to an LEB as they are not losing any reward opportunity.
Cons:
- Significantly more complicated then the other two options. More complexity makes it harder to make future changes and creates more surface area of attack.
- Adds another oDAO responsibility to the protocol.
5. Refund “lost” rewards
Instead of adding additional logic to the distribution process and storing balance and commission at time of migration, we could create a merkle tree that contains the calculated shortfall in ETH by migrating and then increase the capital refund to NOs by this amount. This would be done similarly to the line-in-the-sand merkle tree option.
E.g. In the example given earlier where our longest running minipool forgoes 0.31875 ETH in rewards by migrating, this value would be calculated for every minipool and stored in a merkle tree. When migrating instead of receiving a 12 ETH refund, the NO would receive a 12.31875 ETH refund. Then when the minipool exits and funds are distributed, the user portion is overestimated by 0.31875 so it equals out.
Pros:
- Same as option 3.
- There is no difference between new LEB minipools and minipools that have migrated which makes things slightly less complex.
- NOs get access to capital that would have otherwise been locked until withdrawals to put towards additional minipools.
Cons:
- Same as option 3.
- That ETH would have otherwise been locked and could be used as a buffer to protect rETH holders from that minipool being slashed.
- Creates more demand for liquidity from the rETH deposit pool.
Distribution logic
Some pseudo code for how distribution might work post-migration for option 3 and 4:
// balance_at_migration and commission_at_migration come from merkle tree
if (current_balance < user_capital) {
node_distribution = 0
user_distribution = current_balance
} else {
if (balance_at_migration > 32) {
pre_migration_rewards = (balance_at_migration - 32)
pre_migration_slashing = 0
pre_migration_rewards_node = (pre_migration_rewards / 2) + (pre_migration_rewards / 2 * commission_at_migration)
pre_migration_rewards_user = pre_migration_rewards - pre_migration_rewards_node
} else {
pre_migration_slashing = 32 - balance_at_migration
pre_migration_rewards_node = 0
pre_migration_rewards_user = 0
}
if (current_balance > balance_at_migration) {
post_migration_rewards = (current_balance - balance_at_migration)
post_migration_slashing = 0
post_migration_rewards_node = (post_migration_rewards / 2) + (post_migration_rewards / 2 * current_commission)
post_migration_rewards_user = post_migration_rewards - post_migration_rewards_node
} else {
post_migration_slashing = balance_at_migration - current_balance
post_migration_rewards_node = 0
post_migration_rewards_user = 0
}
total_slashing = pre_migration_slashing + post_migration_slashing
node_distribution = node_capital - total_slashing + pre_migration_rewards_node + post_migration_rewards_node
user_distribution = user_capital + pre_migration_rewards_user + post_migration_rewards_user
}
assert(node_distribution + user_distribution == current_balance)
How to handle penalised and/or slashed minipools who want to migrate?
Migrating to an LEB could be a way to bypass penalties or get around slashing. If a 16 ETH minipool had a 50% penalty (8 ETH), migrating to a 4 ETH pool would refund 12 ETH giving the NO access to funds that would otherwise been withheld from them on distribution. Or if a minipool has a significant slashing event and their balance drops well below 32 ETH, they could migrate to receive more funds than they are entitled to.
Solutions
1. Prevent penalised/slashed minipools from migrating
If a minipool has a penalty, it is prevented from being able to migrate. Unfortunately, CL balance is not available on EL. So in order to prevent a slashed minipool from migrating, we would need to adopt one of the two merkle-tree solutions to the rewards migration problem.
2. Allow slashed minipools to migrate and track their slashing
If we adopt one of the merkle tree solutions to migrating, we will have the balance and commission rate at the time of migration so it is possible to allow slashed minipools to migrate and just keep track of their slashing for distribution. The pseudocode shown above handles this scenario.
We would still need to ensure minipools with penalties greater than the LEB ETH collateral requirement are prevented from migrating.
How to handle the refund from migrating to an LEB?
When migrating to an LEB there is a portion of the NOs collateral which is freed up. The additional collateral is taken from the deposit pool and the freed up capital can then be used to create more minipools. The question is, do we refund the ETH back to the NO so they can do whatever they want with it, or do we restrict them to using the freed up collateral to running more minipools?
Solutions
1. Refund the ETH to the NOs withdrawal address
If LEBs required 8 ETH instead of 16, each migrated minipool would take 8 ETH from the deposit pool and it would be deposited into the NOs withdrawal address. The NO could then choose to spin up another minipool or simply take the ETH and do whatever they want with it.
Pros:
- NOs with multiple minipools can use the ETH to acquire more RPL which may be required operate more minipools.
Cons:
- Might result in NOs extracting collateral and then not putting it back into the protocol. This means the protocol has taken on more risk without receiving any benefit which is contrary to the design goal.
2. Apply a “credit” to the NOs account for the balance
When a minipool migrates to an LEB the difference in colleteral is added to a “credit” balance for that NO. When the NO spins up another minipool, their credit balance is deducted and they only need to send enough ETH to top up the difference in their credit and the amount required for a minipool.
e.g. NO has 5 minipools at 16 ETH. LEB collateral requirement is 6 ETH. They migrate all 5 minipools receiving 50 ETH credit balance. The NO can now spin up 8 more minpools without offering any additional ETH as collateral. Their credit balance is now 2 ETH so on their 9th minipool they must supply 4 ETH difference.
Pros:
- Freed up capital from migrating must be used to create additional minipools.
Cons:
- The freed up capital can’t be used to acquire additional RPL that might be required to spin up the additional minipools.
- Adds some UX complexity to the NO experience as NO has to keep track of their credit balance + their ETH balance when creating pools.
Are 16 ETH minipools still supported?
This question has some overlap with the ETH/RPL collateral question and some discussion has been had regarding this. See: Variable minipool size w/RPL stake based on protocol ETH
In the case we don’t proceed with variable minipool collateral sizes, the question remains as to whether 16 ETH minipools are still supported so I will keep this here for completeness.
Although this is another design decision, it is likely that node commission will change for LEBs. This is so rETH users receive a portion of the additional yield generated by the greater capital efficiency of LEBs. This means, in order to continue supporting 16 ETH minipools alongside LEBs, we would need to offer two different node commission rates. This adds some new user complexity to the protocol and requires additional decisions to be made by new NOs.
The consideration for why we would continue to support 16 ETH minipools is one of risk. Due to the way Rocket Pool uses NO collateral first to handle slashing loses, NOs take on additional risk when operating an LEB compared to a 16 ETH minipool. As to why we would not support them any more, LEBs provide greater capital efficiency and provide greater yield to NOs which allows us to share some of that additional yield with rETH holders and overall make Rocket Pool a more attractive product.
So the trade off is NOs assuming greater risk, receiving greater reward and also providing the protocol with increased capital efficiency. And the question is do we want to give NOs the option or force all future minipools to operate at the higher risk/higher capital efficiency model?
How to handle delegate rollbacks?
This section talks about some low level technical implementation details of Rocket Pool minipools and the implications it has on adding support for LEB migrations.
In order to add LEB support to Rocket Pool, we will need to make changes to the minipool delegate contract. This is the contract which minipools delegate all calls made to them to. Rocket Pool was designed to allow NOs to opt in to delegate upgrades. This is an insurance system so that if a malicious oDAO upgrades minipool logic, NOs can always chose to ignore it and they will be able to exit their validator and distribute their capital via the logic they agreed to when creating their minipool. It also acts as an insurance mechanism against bugs/exploits as it allows NOs to roll back to a previous delegate that does not contain the bug/exploit.
To allow minipools to migrate to an LEB they will need to perform a delegate upgrade to get the new functionality. They will then call a migrate function which will refund the difference in collateral between 16 ETH and the LEB ETH collateral requirement. This migrate method will also update the minipool state to record that the NO only has the reduced amount of capital so that when distribute is eventually called, the lesser collateral amount will be returned to them.
The issue is that a NO could upgrade to the LEB delegate, perform the migration, then rollback to the original delegate. The original delegate doesn’t know how to handle the reduced LEB collateral and simply splits rewards in half and calculates commission. So the NO can now get the benefits of posting less collateral but receiving rewards as if they have 16 ETH posted.
The idea I have to prevent this exploit is to essentially corrupt the state of the minipool on migration (and store the correct values in a new storage slot) such that if a rollback is performed after a migration, the previous delegate calculates the node share to be 0. Hence, neutering the attack. Technically, this would be implemented by setting userDepositBalance
to some high value during migration and storing the correct userDepositBalance
in a new blank storage slot. If a NO upgraded, migrated, rolled back, exited, and then distributed, all the ETH would return to rETH users and the NO would receive nothing.
This prevents the attack but breaks the intention of the rollback which is to allow NOs to rollback to a “safe” delegate. Therefore, there is a security trade off to be made to support LEB migrations. I don’t see this as a major issue as new minipools are created with the latest delegate and have no option to rollback so it’s the same decision an existing NO has to make as a new one.