DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: medium
Invalid

Lack of the validation to check whether or not the amount of native ETH deposited (`msg.value`) would exceed a `daily staking limit` of Lido (stETH) protocol

Summary

According to the "Staking rate limits" in the Lido's documentation, the Lido protocol implements a daily staking limit for stETH that is currently set at 150,000 ether.

However, Within the BridgeSteth#depositEth(), there is no validation to check whether or not the amount (msg.value) of native ETH deposited by the user (msg.sender) would exceed the daily staking limit of Lido (stETH) protocol.

As a result, a transaction of the BridgeSteth#depositEth() would be reverted if a user (msg.sender) call the BridgeSteth#depositEth() with a large amount (msg.value) of native ETH, which exceed the daily staking limit of Lido (stETH) protocol.

Vulnerability Details

When a user deposit the amount of native ETH, the user call the BridgeRouterFacet#depositEth().
Within the BridgeRouterFacet#depositEth(), the BridgeSteth#depositEth()or BridgeReth#depositEth() would be called like this:
https://github.com/Cyfrin/2023-09-ditto/blob/main/contracts/facets/BridgeRouterFacet.sol#L82

function depositEth(address bridge)
external
payable
nonReentrant
onlyValidBridge(bridge)
{
if (msg.value < Constants.MIN_DEPOSIT) revert Errors.UnderMinimumDeposit();
uint256 vault;
if (bridge == rethBridge || bridge == stethBridge) {
vault = Vault.CARBON;
} else {
vault = s.bridge[bridge].vault;
}
uint88 zethAmount = uint88(IBridge(bridge).depositEth{value: msg.value}()); // Assumes 1 ETH = 1 ZETH ///<--------@audit
vault.addZeth(zethAmount);
maybeUpdateYield(vault, zethAmount);
emit Events.DepositEth(bridge, msg.sender, zethAmount);
}

Within the BridgeSteth#depositEth(), the amount (msg.value) of native ETH would be deposited into the Lido in exchange for receiving the equal amount of stETH like this:
https://github.com/Cyfrin/2023-09-ditto/blob/main/contracts/bridges/BridgeSteth.sol#L74

// Deposit ETH and mint stETH (to system) and credit zETH to user
function depositEth() external payable onlyDiamond returns (uint256) {
uint256 originalBalance = steth.balanceOf(address(this));
// @dev address(0) means no fee taken by the referring protocol
steth.submit{value: msg.value}(address(0)); ///<------------------ @audit
uint256 netBalance = steth.balanceOf(address(this)) - originalBalance;
if (netBalance == 0) revert NetBalanceZero();
return netBalance;
}

According to the "Staking rate limits" in the Lido's documentation, the Lido protocol implements a daily staking limit both for stETH and WstETH like this:

In order to handle the staking surge in case of some unforeseen market conditions, the Lido protocol implemented staking rate limits aimed at reducing the surge's impact on the staking queue & Lido’s socialized rewards distribution model. There is a sliding window limit that is parametrized with _maxStakingLimit and _stakeLimitIncreasePerBlock. This means it is only possible to submit this much ether to the Lido staking contracts within a 24-hours timeframe. Currently, the daily staking limit is set at 150,000 ether.

You can picture this as a health globe from Diablo 2 with a maximum of _maxStakingLimit and regenerating with a constant speed per block. When you deposit ether to the protocol, the level of health is reduced by its amount and the current limit becomes smaller and smaller. When it hits the ground, the transaction gets reverted.

To avoid that, you should check if getCurrentStakeLimit() >= amountToStake, and if it's not you can go with an alternative route. The staking rate limits are denominated in ether, thus, it makes no difference if the stake is being deposited for stETH or using the wstETH shortcut, the limits apply in both cases.

However, Within the BridgeSteth#depositEth(), there is no validation to check whether or not the amount (msg.value) of native ETH deposited by the user (msg.sender) would exceed the daily staking limit of Lido (stETH) protocol that is currently set at 150,000 ether.

As a result, a transaction of the BridgeSteth#depositEth() would be reverted if a user (msg.sender) call the BridgeSteth#depositEth() with a large amount (msg.value) of native ETH, which exceed the daily staking limit of Lido (stETH) protocol.

Impact

A transaction of the BridgeSteth#depositEth() would be reverted if a user (msg.sender) call the BridgeSteth#depositEth() with a large amount (msg.value) of native ETH, which exceed the daily staking limit of Lido (stETH) protocol.

Tools Used

  • Foundry

Recommendations

Within the BridgeSteth#depositEth(), consider a validation to check whether or not the amount (msg.value) of native ETH deposited by the user (msg.sender) would exceed the daily staking limit of Lido (stETH) protocol (which is currently set at 150,000 ether).

// Deposit ETH and mint stETH (to system) and credit zETH to user
function depositEth() external payable onlyDiamond returns (uint256) {
uint256 originalBalance = steth.balanceOf(address(this));
+ require(msg.value <= 150000 * 1e18, "The amount of native ETH deposited would exceed the daily staking limit of Lido (stETH) protocol");
// @dev address(0) means no fee taken by the referring protocol
steth.submit{value: msg.value}(address(0)); ///<------------------ @audit
uint256 netBalance = steth.balanceOf(address(this)) - originalBalance;
if (netBalance == 0) revert NetBalanceZero();
return netBalance;
}
Updates

Lead Judging Commences

0xnevi Lead Judge
almost 2 years ago
0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

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