Core Contracts

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

Incorrect implementation of getNormalizedIncome() and getNormalizedDebt() in lending pool

Summary

Incorrect implementation of getNormalizedIncode() and getNormalizedDebt() in lending pool

Vulnerability Details

getNormalizedIncode() and getNormalizedDebt() are used for get real time liquidity index and debt index of reserve. However these two are implemented incorrectly in lending pool, which return last updated index instead of accumulating time colapsed since last update.

These two functions are referenced in multi places in protocol to get real time liquidity or debt index in reserve such as RRoken.sol, DebtToken.sol or StabilityPool.sol.

function getNormalizedIncome() external view returns (uint256) {
return reserve.liquidityIndex;
}
function getNormalizedDebt() external view returns (uint256) {
return reserve.usageIndex;
}

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

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

Impact

These will break protocol functionality in many places, such as in StabilityPool:liquidateBorrower() will results less debt than users accumulated up to now.

e.g: In Rtoken::_update() function, scaledAmount will be a little larger when mint(), burn() or transfer()

function _update(address from, address to, uint256 amount) internal override {
// Scale amount by normalized income for all operations (mint, burn, transfer)
uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
super._update(from, to, scaledAmount);
}

Tools Used

manual

Recommendations

consider changing to something like this:

function getNormalizedIncome(ReserveData storage reserve, ReserveRateData storage rateData) internal view returns (uint256) {
uint256 timeDelta = block.timestamp - uint256(reserve.lastUpdateTimestamp);
if (timeDelta < 1) {
return reserve.liquidityIndex;
}
return calculateLinearInterest(rateData.currentLiquidityRate, timeDelta, reserve.liquidityIndex).rayMul(reserve.liquidityIndex);
}

similar should be implemented to getNormalizedDebt()

Updates

Lead Judging Commences

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

LendingPool::getNormalizedIncome() and getNormalizedDebt() returns stale data without updating state first, causing RToken calculations to use outdated values

Support

FAQs

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