Skip to content

Latest commit

 

History

History
126 lines (90 loc) · 3.98 KB

File metadata and controls

126 lines (90 loc) · 3.98 KB

Jovial Teal Butterfly

Medium

Stale value of lastFeeClaimTime in Pool.sol::simulateCreate function can lead to DOS.

Summary

Apart from initialization, the state variable lastFeeClaimTime can be only be updated via function claimFees().

The poolReserves variable in Pool.sol::simulateCreate function is calculated as -

https://github.com/sherlock-audit/2024-12-plaza-finance/blob/14a962c52a8f4731bbe4655a2f6d0d85e144c7c2/plaza-evm/src/Pool.sol#L273

poolReserves = poolReserves - (poolReserves * fee * (block.timestamp - lastFeeClaimTime)) / (PRECISION * SECONDS_PER_YEAR);

the poolReserves value can be 0, if 2 subtraction parts are equal i.e. poolReserves = (poolReserves * fee * (block.timestamp - lastFeeClaimTime)) / (PRECISION * SECONDS_PER_YEAR)

after simplification it's - block.timestamp = (PRECISION * SECONDS_PER_YEAR)/ fee + lastFeeClaimTime

Consider the situation feeBenficiary haven't called the claimFee function for a long time, which means lastFeeClaimTime havn't been updated for a long time.

If that's the case then there will be a situation - block.timestamp >= (PRECISION * SECONDS_PER_YEAR)/ fee + lastFeeClaimTime.

And if this situation arises, simulateCreate function will always revert as poolReserves will be 0 or tends to less than 0.

The simulateCreate further calls getCreateAmount function, passing poolReserves-

    return getCreateAmount(
      tokenType,
      depositAmount,
      bondSupply,
      levSupply,
@->   poolReserves,
      getOraclePrice(reserveToken, USD),
      getOracleDecimals(reserveToken, USD)
    ).normalizeAmount(COMMON_DECIMALS, assetDecimals);

If poolReserves is 0 getCreateAmount will revert. because -

  1. tvl will be 0.
  2. collateralLevel will be 0.
  3. If collateralLevel is 0, then code inside (collateralLevel <= COLLATERAL_THRESHOLD) will execute.
  4. means creationRate is 0, as - creationRate = (tvl * multiplier) / assetSupply;
  5. If creationRate is 0, function will revert due to, division by 0 -
return ((depositAmount * ethPrice * PRECISION) / creationRate).toBaseUnit(oracleDecimals);
  function getCreateAmount(
    TokenType tokenType,
    uint256 depositAmount,
    uint256 bondSupply, 
    uint256 levSupply, 
    uint256 poolReserves, 
    uint256 ethPrice,
    uint8 oracleDecimals) public pure returns(uint256) {
    if (bondSupply == 0) {
      revert ZeroDebtSupply();
    }

    uint256 assetSupply = bondSupply;
    uint256 multiplier = POINT_EIGHT;
    if (tokenType == TokenType.LEVERAGE) {
      multiplier = POINT_TWO;
      assetSupply = levSupply;
    }

@-> uint256 tvl = (ethPrice * poolReserves).toBaseUnit(oracleDecimals);
@-> uint256 collateralLevel = (tvl * PRECISION) / (bondSupply * BOND_TARGET_PRICE);
    uint256 creationRate = BOND_TARGET_PRICE * PRECISION; // PRECISION = 1000000;

    if (collateralLevel <= COLLATERAL_THRESHOLD) {
      if (tokenType == TokenType.LEVERAGE && assetSupply == 0) {
        revert ZeroLeverageSupply();
      }
@->    creationRate = (tvl * multiplier) / assetSupply;
    } else if (tokenType == TokenType.LEVERAGE) {
      if (assetSupply == 0) {
        revert ZeroLeverageSupply();
      }

      uint256 adjustedValue = tvl - (BOND_TARGET_PRICE * bondSupply);
      creationRate = (adjustedValue * PRECISION) / assetSupply;
    }
    
@-> return ((depositAmount * ethPrice * PRECISION) / creationRate).toBaseUnit(oracleDecimals);
  }

The conclusion is create will revert due to DOS, if the above situation arises.

Root Cause

calling claimFee() function after long time, or only single point of updation for lastFeeClaimTime

Internal Pre-conditions

No response

External Pre-conditions

No response

Attack Path

if situation arises as same in summary section.

Impact

DOS, user will not be able to create ethBOND and levETH. it means supply of wstETH to pool can be affected.

PoC

No response

Mitigation

Create a check to ensure that claimFee function is updated regularly.