Core Contracts

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

Amount precision inconsistencies result in users borrowing more than intended and miscalculation of `scaledDebtBalance`.

Summary

The LendingPool aligns with Aave protocol contracts so they are similar in operations, but since RAAC protocol stores values in RAY precision instead, it introduces vulnerabilities in the contract.

Vulnerability Details

The amount argument that LendingPool::borrow takes is in wad precision. It's used in the following places without getting converted to RAY precision first,

  1. To calculate userTotalDebt - The userTotalDebt is a ray-precision value calculated by adding two values,

    • Actual debt so far (scaledDebtBalance * usageIndex). This is ray precision value since it uses rayMul which multiplies 2 ray values.

    • amount - wad precision value that user wants to borrow.

    @> uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount; // @audit amount not converted to ray!
    // Ensure the user has enough collateral to cover the new debt
    if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
    revert NotEnoughCollateralToBorrow();
    }
    // Update user's scaled debt balance
    @> uint256 scaledAmount = amount.rayDiv(reserve.usageIndex); // @audit amount should be converted to ray!

    Adding wad value to ray value would result in a lower userTotalDebt value. This gets compared to collateralValue to check if user has enough collateral in value to borrow more. A lower userTotalDebt would allow user to borrow more than what their collateral is worth.

  2. To update scaledDebtBalance - This requires normalizing the amount first before storing it. The issue is,
    amount is directly used with rayDiv without getting converted to RAY first.

    @> uint256 scaledAmount = amount.rayDiv(reserve.usageIndex); // @ WAD
    // Mint DebtTokens to the user (scaled amount)
    (bool isFirstMint, uint256 amountMinted, uint256 newTotalSupply) = IDebtToken(reserve.reserveDebtTokenAddress).mint(msg.sender, msg.sender, amount, reserve.usageIndex);
    // Transfer borrowed amount to user
    IRToken(reserve.reserveRTokenAddress).transferAsset(msg.sender, amount);
    @> user.scaledDebtBalance += scaledAmount; // @ WAD amount being added to RAY

    This results in an incorrect scaledDebtBalance in wad precision when it should be in RAY precision. Furthermore, this issue of amount being directly used is present in the following functions with following problems,

    • LendingPool::_repay - wad amount is compared against userScaledDebt (ray) leading to the actualRepayAmount always being the
      complete debt of user resulting in only the complete repayment even if user intended to repay partially
      because amount (wad) would always be less than scaledDebtBalance(supposed to be a ray value)

Impact

Not converting wad values to ray would lead to tiny rounding errors that can snowball into massive inaccuracies over time and it's important to maintain the precision of the values in a protocol that deals with linear and compounded interest and records values in RAY precision. In addition, it introduces other problems, such as full repay of debt even when repay was triggered for partial repayment.

Tools Used

Manual Review

Recommendations

In LendingPool::borrow, LendingPool::withdraw and other parts of the protocol, convert the amount argument to ray first before using with rayDiv or rayMul,

LendingPool::borrow:

- uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount;
+ uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount.wadToRay();
- uint256 scaledAmount = amount.rayDiv(reserve.usageIndex);
+ uint256 scaledAmount = amount.wadToRay().rayDiv(reserve.usageIndex);
user.scaledDebtBalance += scaledAmount;

LendingPool::_repay:

+ uint256 rayAmount = amount.wadToRay();
- uint256 actualRepayAmount = amount > userScaledDebt ? userScaledDebt : amount;
+ uint256 actualRepayAmount = rayAmount > userScaledDebt ? userScaledDebt : rayAmount;
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!