Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Invalid

Incorrect Token Conversion Due to Improper Handling of Decimals in `calculateDeCRVUSDAmount` and `calculateRcrvUSDAmount` Functions

Summary

The formulas for calculating the amount of deToken (DeCRVUSD) to receive when sending rToken (rcrvUSD) do not correctly account for tokens with different decimal places. From the implementation, it intends to account for tokens with different decimals. While the implementation works for tokens with the same decimals (e.g., both 18 decimals), it fails for tokens with different decimals (e.g., 6 and 18). This leads to incorrect conversions, as the scaling factor does not properly normalize the decimal differences. The current implementation attempts to handle tokens with different decimals but is flawed, resulting in inaccurate calculations. This issue also affects the withdraw function, causing users to experience reverts due to insufficient balances when attempting to withdraw funds.

Affected Code: StabilityPool::calculateDeCRVUSDAmount & calculateRcrvUSDAmount


Vulnerability Details

The current implementation of the conversion formulas is as follows:

function calculateDeCRVUSDAmount(uint256 rcrvUSDAmount) public view returns (uint256) {
uint256 scalingFactor = 10**(18 + deTokenDecimals - rTokenDecimals);
return (rcrvUSDAmount * scalingFactor) / getExchangeRate();
}
function calculateRcrvUSDAmount(uint256 deCRVUSDAmount) public view returns (uint256) {
uint256 scalingFactor = 10**(18 + rTokenDecimals - deTokenDecimals);
return (deCRVUSDAmount * getExchangeRate()) / scalingFactor;
}

Issues:

  1. Incorrect Scaling Factor:

    • The scaling factor 10**(18 + deTokenDecimals - rTokenDecimals) is arbitrary and does not correctly account for the difference in decimals between the two tokens.

    • For tokens with different decimals, this leads to incorrect conversions.

  2. Assumption of 1:1 Conversion:

    • The implementation assumes a 1:1 conversion for tokens with different decimals, which is incorrect.

  3. Impact on withdraw Function:

    • The incorrect conversion formulas affect the withdraw function, causing users to experience reverts due to insufficient balances when attempting to withdraw funds.

    • For example, if a user deposits rToken and receives an incorrect amount of deToken, they may not have enough deToken to cover the withdrawal, leading to the InsufficientBalance revert.

Example: Tokens with 6 and 18 Decimals

Let’s assume:

  • rToken (rcrvUSD) has 6 decimals.

  • deToken (DeCRVUSD) has 18 decimals.

  • getExchangeRate() returns 1e18 (1:1 exchange rate in ray math).

Case 1: Converting 10 rToken (rcrvUSD) to deToken (DeCRVUSD)

  • Current Implementation:

    • scalingFactor = 10**(18 + 18 - 6) = 10**30.

    • calculateDeCRVUSDAmount(10 * 1e6) = (10 * 1e6 * 1e30) / 1e18 = 10 * 1e18.

Case 2: Converting 10 deToken (DeCRVUSD) to rToken (rcrvUSD)

  • Current Implementation:

    • scalingFactor = 10**(18 + 6 - 18) = 10**6.

    • calculateRcrvUSDAmount(10 * 1e18) = (10 * 1e18 * 1e18) / 1e6 = 10 * 1e30.

    This result is incorrect because it does not account for the difference in decimals.


Impact

  • Incorrect Conversions: Users will receive incorrect amounts of tokens when converting between rToken and deToken if the tokens have different decimals.

  • Financial Loss: Users may lose funds due to inaccurate calculations.

  • Protocol Integrity: The protocol’s functionality is compromised, leading to a loss of trust among users.

  • Withdraw Function Reverts: Users may experience reverts due to insufficient balances when attempting to withdraw funds, as incorrect conversion formulas lead to mismatched balances.


Tools Used

  • Manual code review


Recommendations

To fix the issue, the scaling factor must correctly account for the difference in decimals between the two tokens. Here’s the corrected implementation:

function calculateDeCRVUSDAmount(uint256 rcrvUSDAmount) public view returns (uint256) {
if (deTokenDecimals >= rTokenDecimals) {
uint256 scalingFactor = 10**(deTokenDecimals - rTokenDecimals);
return (rcrvUSDAmount * scalingFactor * 1e18) / getExchangeRate();
} else {
uint256 scalingFactor = 10**(rTokenDecimals - deTokenDecimals);
return (rcrvUSDAmount * 1e18) / (scalingFactor * getExchangeRate());
}
}
function calculateRcrvUSDAmount(uint256 deCRVUSDAmount) public view returns (uint256) {
if (rTokenDecimals >= deTokenDecimals) {
uint256 scalingFactor = 10**(rTokenDecimals - deTokenDecimals);
return (deCRVUSDAmount * getExchangeRate()) / (scalingFactor * 1e18);
} else {
uint256 scalingFactor = 10**(deTokenDecimals - rTokenDecimals);
return (deCRVUSDAmount * getExchangeRate() * scalingFactor) / 1e18;
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Incorrect scaling factor formula in StabilityPool::calculateRcrvUSDAmount function

Both tokens have 18 decimals. Info

Support

FAQs

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