Core Contracts

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

`DebtToken::totalSupply()` incorrectly scales the already scaled amount

Summary

The RAAC protocol takes from AAVE's self-indexing tokens, and as AAVE overwrites the RToken and DebtToken internal balanceOf() and totalSupply() functions, so that when queried they return the normalized amount (i.e. including the interest), instead of showing the scaled one. However, in DebtToken, the protocol incorrectly double-scales the return value. This leads to setting an incorrect reserve.totalUsage value, which in turn leads to calculating incorrect liquidity and borrowing rates.

Vulnerability Details

Whenever new loans are taken, DebtTokens are being minted, and each mint, the reserve.totalUsage variable is updated with the scaled amount of circulating tokens. The same happens when, the tokens are being burned, the new totalSupply is returned and is again set as the reserve.totalUsage

function borrow(uint256 amount) external nonReentrant whenNotPaused onlyValidAmount(amount) {
__SNIP__
// 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;
// reserve.totalUsage += amount;
reserve.totalUsage = newTotalSupply;
// Update liquidity and interest rates
ReserveLibrary.updateInterestRatesAndLiquidity(reserve, rateData, 0, amount);
// Rebalance liquidity after borrowing
_rebalanceLiquidity();
emit Borrow(msg.sender, amount);
}
function _repay(uint256 amount, address onBehalfOf) internal {
__SNIP__
// Burn DebtTokens from the user whose debt is being repaid (onBehalfOf)
// is not actualRepayAmount because we want to allow paying extra dust and we will then cap there
(uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned, uint256 balanceIncrease) =
IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, amount, reserve.usageIndex);
// Transfer reserve assets from the caller (msg.sender) to the reserve
IERC20(reserve.reserveAssetAddress).safeTransferFrom(msg.sender, reserve.reserveRTokenAddress, amountScaled);
reserve.totalUsage = newTotalSupply;
user.scaledDebtBalance -= amountBurned;
// Update liquidity and interest rates
ReserveLibrary.updateInterestRatesAndLiquidity(reserve, rateData, amountScaled, 0);
emit Repay(msg.sender, onBehalfOf, actualRepayAmount);
}

However, when returning totalSupply(), the DebtToken incorrectly uses rayDIv, instead of rayMul, which returns a lower value of tokens than what is currently in the system:

function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
uint256 scaledSupply = super.totalSupply();
@> return scaledSupply.rayDiv(ILendingPool(_reservePool).getNormalizedDebt()); // @audit - double scaling
}

This means that the reserve.totalUsage is set to an incorrect value, meaning that afterward all interest rate calculations in the ReserveLibrary will be incorrectly set, leading to wrong protocol accounting.

Impact

Invalid reserve.totalUsage, leading to invalid calculations of interest rates in the protocol

Tools Used

Manual review

Recommendations

Change the rayDiv in DebtToken::totalSupply() to rayMul as done in the RToken function.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

DebtToken::totalSupply incorrectly uses rayDiv instead of rayMul, severely under-reporting total debt and causing lending protocol accounting errors

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

DebtToken::totalSupply incorrectly uses rayDiv instead of rayMul, severely under-reporting total debt and causing lending protocol accounting errors

Support

FAQs

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

Give us feedback!