Assessment reports>Safety Module>Medium findings>Fees could be dripped in any state
Category: Coding Mistakes

Fees could be dripped in any state

Medium Severity
Medium Impact
Low Likelihood

Description

According to Cozy's design, fees could be dripped when SafetyModules's state is SafetyModuleState.ACTIVE.

In FeesHandler, the function _dripFeesFromReservePool is used to accumulate dripped fees from the reserve pool. This function is called from dripFees, dripFeesFromReservePool, claimFees, _executeReserveDeposit, and redeem.

But only two functions — dripFees and dripFeesFromReservePool — check state. The others do not check that SafetyModules's state is SafetyModuleState.ACTIVE.

This causes fees to drip in any state which means that the fees may be overcharged.

  function dripFees() public override {
!   if (safetyModuleState != SafetyModuleState.ACTIVE) return;
    IDripModel dripModel_ = cozySafetyModuleManager.getFeeDripModel(ISafetyModule(address(this)));

    uint256 numReserveAssets_ = reservePools.length;
    for (uint8 i = 0; i < numReserveAssets_; i++) {
!     _dripFeesFromReservePool(reservePools[i], dripModel_);
    }
  }

  function dripFeesFromReservePool(uint8 reservePoolId_) external {
!   if (safetyModuleState != SafetyModuleState.ACTIVE) return;
    IDripModel dripModel_ = cozySafetyModuleManager.getFeeDripModel(ISafetyModule(address(this)));

!   _dripFeesFromReservePool(reservePools[reservePoolId_], dripModel_);
  }

  function claimFees(address owner_) external {
    // ...

    uint256 numReservePools_ = reservePools.length;
    for (uint8 i = 0; i < numReservePools_; i++) {
      ReservePool storage reservePool_ = reservePools[i];
!     _dripFeesFromReservePool(reservePool_, dripModel_);

      // ...
    }
  }

! function _dripFeesFromReservePool(ReservePool storage reservePool_, IDripModel dripModel_) internal override {
    uint256 drippedFromDepositAmount_ = _getNextDripAmount(
      reservePool_.depositAmount - reservePool_.pendingWithdrawalsAmount, dripModel_, reservePool_.lastFeesDripTime
    );

    if (drippedFromDepositAmount_ > 0) {
      reservePool_.feeAmount += drippedFromDepositAmount_;
      reservePool_.depositAmount -= drippedFromDepositAmount_;
    }

    reservePool_.lastFeesDripTime = uint128(block.timestamp);
  }

Impact

The function _dripFeesFromReservePool subtracts fees from reservePool_.depositAmount. This means a decrease in the asset price of depositing users.

A malicious user could call claimFees repeatedly no matter SafetyModule's state. This causes unexpected fees to be charged from SafetyModule.

Recommendations

We recommend adding the check for SafetyModuleState.ACTIVE to all points before calling _dripFeesFromReservePool. This could prevent SafetyModule from dripping fees when the state is not active.

Remediation

Zellic © 2024Back to top ↑