Core Contracts

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

Double division by usage index in LendingPool._repay() causes users to repay only a fraction of intended amount

Relevant GitHub Links

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L398-L431

Summary

The LendingPool._repay() function incorrectly scales down the repayment amount twice by dividing it by the usage index, causing users to only repay a fraction of their intended debt repayment amount.

Vulnerability Details

In LendingPool._repay(), the repayment amount is incorrectly scaled down twice:

function _repay(uint256 amount, address onBehalfOf) internal {
// First division by usage index
uint256 userDebt = IDebtToken(reserve.reserveDebtTokenAddress).balanceOf(onBehalfOf);
uint256 userScaledDebt = userDebt.rayDiv(reserve.usageIndex);
uint256 actualRepayAmount = amount > userScaledDebt ? userScaledDebt : amount;
// Second incorrect division by usage index
uint256 scaledAmount = actualRepayAmount.rayDiv(reserve.usageIndex);
// Use incorrectly scaled amount for burn
(uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned, uint256 balanceIncrease) =
IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, amount, reserve.usageIndex);
}

For example:

  • User has 100 tokens of debt with 10% interest (usageIndex = 1.1)

  • Total debt with interest = 110 tokens

  • First division: 110/1.1 = 100 (scaled debt)

  • Second division: 100/1.1 ≈ 90.9

  • User ends up repaying only ~90.9 tokens instead of intended 110

Impact

This incorrect scaling causes:

  1. Users' debt to persist when it should be fully repaid

  2. Continued interest accrual on the remaining debt

  3. Users paying more in interest than they should

Tools Used

Manual review

Recommendations

Remove the second division by usage index since actualRepayAmount is already a scaled amount:

function _repay(uint256 amount, address onBehalfOf) internal {
uint256 userDebt = IDebtToken(reserve.reserveDebtTokenAddress).balanceOf(onBehalfOf);
uint256 userScaledDebt = userDebt.rayDiv(reserve.usageIndex);
uint256 actualRepayAmount = amount > userScaledDebt ? userScaledDebt : amount;
- uint256 scaledAmount = actualRepayAmount.rayDiv(reserve.usageIndex);
+ uint256 scaledAmount = actualRepayAmount;
(uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned, uint256 balanceIncrease) =
IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, amount, reserve.usageIndex);
}
Updates

Lead Judging Commences

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

LendingPool::_repay double scales the debt

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

LendingPool::_repay double scales the debt

Support

FAQs

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

Give us feedback!