When The overplus is called it returns depositTokenContractBalance_ - totalDepositedInPublicPools;
, which happens as a result of the lido stETH rebasing system, but the difference in these two balances, might be miniscule, as a result some of the subtraction may return small amounts like 0.0001stETH, which will actually pass the require(overplus_ > 0, "DS: overplus is zero");
check, and then the function will attempt to bridge that token, but in the L1Sender
contract when it callssendDepoditTokens
the wrap function it calls from theIWstETH
according to lido, wrapping stETH is done according to an exchange rate which is updated every day, this means for example, 1 stETH - 0.98WstETH, with this conversion rate the small amount stETH will definatley round down to zero, which will cause loss of tokens and Bridging a zero amount WstETH.
The Following Steps will describe how this vunerability will occur
Owner tries to Bridge the overplus as shown below
Inside that function it checks the returned overplus value, which is actually just depositTokenContractBalance_ - totalDepositedInPublicPools
, this calculation may not always return the value the protocol expects, it may return a very small amount , like 0.0001stETH, shown below the protocol tries to make sure that the overplus is greater than zero as shown below
This check will pass and it will call to bridge the token,
Now Inside the sendDepositFunction
we will explore where the issue will happen
https://github.com/Cyfrin/2024-01-Morpheus/blob/07c900d22073911afa23b7fa69a4249ab5b713c8/contracts/L1Sender.sol#L99C5-L123C1
Here when the wrap method is called on the unwrapped amount, it will round down to zero if the amount you are about to wrap is a very small amount, due to lido daily set conversion rate
This Conversion rate system is linked here below
https://stake.lido.fi/wrap
At any given time, anyone holding wstETH can convert any amount of it to stETH at a fixed rate, and vice versa. Normally, the rate gets updated once a day, when stETH undergoes a rebase.
from Lido site
But the main issue is that the function does not consider that kind of situation and tries to bridge the wrapped amount like that, leading to a situation of bridging zero amount meanwhile spending expensive gas for computation on the destination chain leading to losing funds on both chains
Loss Of Funds When trying to perform expensive Cross chain action that is sure to revert on the destination chain
Manual Review
In the sendDepositToken
function before bridging the follow checks should be implemented
require(amount_ > 0, 'cant bridge zero WstETH')
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.