DittoETH

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

BridgeStEth.sol treats stETH : ETH as 1:1

Summary

Core idea is to peg zETH to ETH. However on deposit/withdraw/unstake protocol treats that stETH is equal to ETH.
In this chart you can see that price deviates. As a result depeg of stETH will cause depeg of zETH

Vulnerability Details

Let's take for example deposit flow (but similar situation with withdraw and unstake too). Contract accepts amount of stETH and locks it, and must return locked amount of ETH. Such that zethAmount must be in terms of ETH, not stETH
https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/BridgeRouterFacet.sol#L46-L65

function deposit(address bridge, uint88 amount)
external
nonReentrant
onlyValidBridge(bridge)
{
if (amount < Constants.MIN_DEPOSIT) revert Errors.UnderMinimumDeposit();
// @dev amount after deposit might be less, if bridge takes a fee
@> uint88 zethAmount = uint88(IBridge(bridge).deposit(msg.sender, amount)); // @dev(safe-cast)
uint256 vault;
if (bridge == rethBridge || bridge == stethBridge) {
vault = Vault.CARBON;
} else {
vault = s.bridge[bridge].vault;
}
vault.addZeth(zethAmount);
maybeUpdateYield(vault, zethAmount);
emit Events.Deposit(bridge, msg.sender, zethAmount);
}

But BridgeStEth returns amount of stETH, not ETH:
https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/bridges/BridgeSteth.sol#L58-L68

// Bring stETH to system and credit zETH to user
function deposit(address from, uint256 amount)
external
onlyDiamond
returns (uint256)
{
// Transfer stETH to this bridge contract
// @dev stETH uses OZ ERC-20, don't need to check success bool
steth.transferFrom(from, address(this), amount);
@> return amount;
}

Impact

Deviation of stETH can cause depeg of zETH

Tools Used

Manual Review

Recommendations

Firstly convert ETH amount to stETH via Chainlink price feed. Add this logic to functions deposit(), depositEth(), withdraw(), unstake(). And also in function getZethVaule()

Updates

Lead Judging Commences

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

finding-579

Support

FAQs

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