Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: medium
Invalid

Incorrect decimals handling in conversion functions

Summary

The conversion functions (convertTokenAmountToUd60x18, convertUd60x18ToTokenAmount, etc.) in the Collateral library assume that self.decimals is always ≤ SYSTEM_DECIMALS. However, this assumption is not explicitly validated during initialization or updates. If self.decimals exceeds SYSTEM_DECIMALS, it could lead to incorrect calculations, resulting in financial losses or system malfunctions.


Vulnerability Details

Code Analysis

The following conversion functions are defined in the Collateral library:

convertTokenAmountToUd60x18

function convertTokenAmountToUd60x18(Data storage self, uint256 amount) internal view returns (UD60x18) {
return Math.convertTokenAmountToUd60x18(self.decimals, amount);
}

convertUd60x18ToTokenAmount

function convertUd60x18ToTokenAmount(Data storage self, UD60x18 amountX18) internal view returns (uint256) {
return Math.convertUd60x18ToTokenAmount(self.decimals, amountX18);
}

Key Observations:

  1. Assumption on self.decimals : The functions assume that self.decimals is always ≤ SYSTEM_DECIMALS. This assumption is documented but not enforced programmatically.

  2. Potential Overflow/Underflow : If self.decimals exceeds SYSTEM_DECIMALS, the conversion logic may produce incorrect results due to improper scaling.

  3. No Validation : The decimals field is not validated when initializing or updating the Collateral.Data struct.

Attack Scenario

  1. An attacker or malicious admin sets an invalid value for self.decimals (e.g., self.decimals = 20 while SYSTEM_DECIMALS = 18).

  2. The conversion functions (convertTokenAmountToUd60x18 and convertUd60x18ToTokenAmount) are called with valid input values.

  3. Due to the invalid self.decimals, the functions produce incorrect results:

    • convertTokenAmountToUd60x18 over-scales the input amount, leading to inflated values.

    • convertUd60x18ToTokenAmount under-scales the normalized amount, leading to deflated values.

  4. These incorrect conversions propagate through the system, causing financial losses or operational failures.


Impact

  • Financial Losses : Incorrect pricing or accounting could lead to improper debt/reward distributions, affecting users and the protocol.

  • System Malfunctions : Invalid conversions could disrupt critical operations like collateral valuation, liquidation, and trading.

  • Trust Erosion : Users may lose confidence in the protocol due to inconsistent or inaccurate calculations.


Tools Used

  1. Manual Code Review : Analyzed the conversion functions and their assumptions about self.decimals.

  2. Slither : Static analysis tool used to identify missing validations and potential overflow/underflow risks.

  3. MythX : Security analysis platform used to verify vulnerabilities in the smart contract.


Recommendations

Short-Term Fix

Add explicit validation for self.decimals during initialization or updates to ensure it adheres to the required constraints. For example:

function setDecimals(Data storage self, uint8 decimals) internal {
require(decimals <= SYSTEM_DECIMALS, "Invalid decimals");
self.decimals = decimals;
}

Long-Term Fix

  1. Centralized Validation : Implement a centralized validation mechanism for all fields in the Collateral.Data struct to prevent invalid configurations.

  2. Event Logging : Emit events whenever self.decimals is updated to provide transparency and enable monitoring.

    event DecimalsUpdated(address asset, uint8 oldDecimals, uint8 newDecimals);
    function setDecimals(Data storage self, uint8 decimals) internal {
    require(decimals <= SYSTEM_DECIMALS, "Invalid decimals");
    emit DecimalsUpdated(self.asset, self.decimals, decimals);
    self.decimals = decimals;
    }
  3. Unit Tests : Add unit tests to verify the behavior of the conversion functions for edge cases (e.g., self.decimals = 0, self.decimals = SYSTEM_DECIMALS + 1).

Example Fix Implementation

Here’s how the convertTokenAmountToUd60x18 function can be updated to include runtime checks:

function convertTokenAmountToUd60x18(Data storage self, uint256 amount) internal view returns (UD60x18) {
require(self.decimals <= SYSTEM_DECIMALS, "Invalid decimals");
return Math.convertTokenAmountToUd60x18(self.decimals, amount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!