DittoETH

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

The `withdrawalFee()` function and the `unstakeFee()` function would not be implemented in the both Bridge contract, which lead to missing to collect the withdrawal/unstakeing fees

Summary

Within the both Bridge contract (BridgeReth.sol and BridgeSteth.sol), the following functions to subtract the fees would not be implemented:

  • The withdrawalFee() function

  • The unstakeFee() function

As a result,

  • Within the BridgeRouterFacet#withdraw(), 0 would always be stored into the withdrawalFee because the Bridge#withdrawalFee() would be 0.

  • Within the BridgeRouterFacet#unstakeEth(), 0 would always be stored into the fee because the Bridge#unstakeFee() would be 0.

This lead to that the DittoETH protocol would always be missing to collect the fees when the BridgeRouterFacet#withdraw() or the BridgeRouterFacet#unstakeEth() would be called.

Vulnerability Details

Within the BridgeRouterFacet#withdraw(), the Bridge#withdrawalFee() would be called to get the withdrawalFee like this:
https://github.com/Cyfrin/2023-09-ditto/blob/main/contracts/facets/BridgeRouterFacet.sol#L96

function withdraw(address bridge, uint88 zethAmount)
external
nonReentrant
onlyValidBridge(bridge)
{
if (zethAmount == 0) revert Errors.ParameterIsZero();
uint88 fee;
uint256 withdrawalFee = bridge.withdrawalFee(); ///<------- @audit
uint256 vault;
...
if (withdrawalFee > 0) {
fee = zethAmount.mulU88(withdrawalFee);
zethAmount -= fee;
s.vaultUser[vault][address(this)].ethEscrowed += fee;
}
uint88 ethAmount = _ethConversion(vault, zethAmount);
vault.removeZeth(zethAmount, fee);
IBridge(bridge).withdraw(msg.sender, ethAmount);
...
}

Within the BridgeRouterFacet#unstakeEth(), the Bridge#unstakeFee() would be called to get the fee like this:
https://github.com/Cyfrin/2023-09-ditto/blob/main/contracts/facets/BridgeRouterFacet.sol#L123

function unstakeEth(address bridge, uint88 zethAmount)
external
nonReentrant
onlyValidBridge(bridge)
{
...
uint88 fee = zethAmount.mulU88(bridge.unstakeFee()); ///<------------@audit
...
if (fee > 0) {
zethAmount -= fee;
s.vaultUser[vault][address(this)].ethEscrowed += fee;
}
uint88 ethAmount = _ethConversion(vault, zethAmount);
vault.removeZeth(zethAmount, fee);
IBridge(bridge).unstake(msg.sender, ethAmount);
...

As we can see the both functions above, within the both Bridge contract (BridgeReth.sol and BridgeSteth.sol), the following functions to subtract the fees are supposed to be implemented:

  • The withdrawalFee() function

  • The unstakeFee() function

However, within the both Bridge contract (BridgeReth.sol and BridgeSteth.sol), the both functions (the withdrawalFee() and the unstakeFee()) to subtract the fees would not be implemented.

As a result,

  • Within the BridgeRouterFacet#withdraw(), 0 would always be stored into the withdrawalFee because the Bridge#withdrawalFee() would be 0.
    https://github.com/Cyfrin/2023-09-ditto/blob/main/contracts/facets/BridgeRouterFacet.sol#L96

  • Within the BridgeRouterFacet#unstakeEth(), 0 would always be stored into the fee because the Bridge#unstakeFee() would be 0.
    https://github.com/Cyfrin/2023-09-ditto/blob/main/contracts/facets/BridgeRouterFacet.sol#L123

This lead to missing to collect the fees when the BridgeRouterFacet#withdraw() or the BridgeRouterFacet#unstakeEth() would be called.

Impact

The DittoETH protocol would always be missing to collect the fees when the BridgeRouterFacet#withdraw() or the BridgeRouterFacet#unstakeEth() would be called.

Tools Used

  • Foundry

Recommendations

Within the both Bridge contract (BridgeReth.sol and BridgeSteth.sol), consider implementing (adding) the withdrawalFee() function and the unstakeFee() function.

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.