burn()
as used here for rETH, targets Rocketpool which is not always solvent. The issue with this is that the RocketPool's burn() function function only allows for excess balance to be withdrawn i.e. ETH that has been deposited by stakers but that is not yet staked on the Ethereum beacon chain. So Rocketpool allows users to burn rETH and withdraw ETH as long as the excess balance is sufficient.
The issue is: If there is no excess balance because enough users burn rETH or the Minipool capacity increases, Ditto protocol rETH users can't unstake.
In Rocketpool, users stake 16 eth and node operators stake the remaining 16 eth. A minimum quantity of eth is left in the contract for withdrawals, but the majority of eth are tied up in staking. So users cannot redeem all rETH for eth, since most are controlled by node operators.
Even after the Shanghai upgrade, rETH still remains insolvent. This is because when the liquid eth in the contract dries up, the contract has to wait for a node operator to shut down their node and free up more eth. This is profitable for node operators since an insolvent rETH contract drops the rETH price, allowing node operators to profit from the price drop. Stakers are still expected to wait until enough eth is freed up before burning rETH. Users who don't want to wait are thus encouraged to swap their rETH for eth in LPs, reducing load from the withdrawal mechanism.
In this protocol, however, the only method for unstaking of rETH is the burn mechanism, which can be insolvent. Thus all users of the protocol have to wait until rETH contract has liquidity, in order to unstake.
Context & similarity with current scenario: The Asymmetry protocol promises that a user can call SafETH.unstake
at all times, allowing a user to burn their SafETH
tokens and receive ETH
in return. This requires that the derivatives held by the protocol can be withdrawn (i.e. converted to ETH
). The Reth
derivative works differently than the WstETH
and SfrxETH
derivatives. Withdrawals are made by calling the RocketTokenRETH.burn
function, but the issue is that the RocketTokenRETH.burn
function only allows for excess balance to be withdrawn. If there is no excess balance, the Asymmetry protocol is unable to operate. The solution for this issue is to have an alternative withdrawal mechanism in case the excess balance in the RocketDepositPool is insufficient to handle the withdrawal. This can be done by swapping the rETH
tokens via the Uniswap pool. The bug was confirmed by Asymmetry.
Link to past bug reports:
BridgeReth.sol::unstake()
becomes unusable for every user.
Manual inspection.
The solution for this issue is to have an alternative withdrawal mechanism in case the excess balance in the RocketDepositPool is insufficient to handle the withdrawal.
The alternative withdrawal mechanism is to sell the rETH tokens via an external pool like Uniswap.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.