DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: low
Valid

`BridgeReth.sol::unstake()` is unreliable and depends on excess RocketDepositPool balance which can lead to DoS

Summary

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.

Vulnerability Details

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.

Similar past bug report with context

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:

Impact

BridgeReth.sol::unstake() becomes unusable for every user.

Tools Used

Manual inspection.

Recommendations

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.

Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-503

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.