Core Contracts

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

`getNormalizedDebt` in ReserveLibrary is wrongly implemented, returning `totalUsage` instead of `usageIndex` if `timeDelta < 1`.

Summary

getNormalizedDebt function in ReserveLibrary library is defined as follows:

function getNormalizedDebt(ReserveData storage reserve, ReserveRateData storage rateData)
internal
view
returns (uint256)
{
uint256 timeDelta = block.timestamp - uint256(reserve.lastUpdateTimestamp);
if (timeDelta < 1) {
return reserve.totalUsage;
}
return calculateCompoundedInterest(rateData.currentUsageRate, timeDelta).rayMul(reserve.usageIndex);
}

This function is supposed to return the normalised index for usage (debt). If current block timestamp is greater than the last update timestamp, getNormalizedDebt returns reserve.usageIndex multiplied by the compounded interests since last reserve update.

Vulnerability Details

The problem arises in the case where current block timestamp is the same as the last reserve update. Indeed, in this case, reserve.totalUsage is returned instead of reserve.usageIndex, which will lead to undesired behaviour in the protocol.

In comparison, getNormalizedIncome in ReserveLibrary is defined as follows:

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
);
}

We can see that it is correct, with reserve.liquidityIndex being returned, corresponding to the current liquidity index.

Impact

The impact of this issue is high. Indeed, in a context where the deposit function is correctly implemented apart from this error, getNormalizedDebt will return a wrong value (raw total borrowed amount) instead of a RAY usage index, breaking interest rate computation.

Tools Used

Manual review

Recommendations

Make sure to correctly implement getNormalizedDebt in ReserveLibrary:

function getNormalizedDebt(ReserveData storage reserve, ReserveRateData storage rateData)
internal
view
returns (uint256)
{
uint256 timeDelta = block.timestamp - uint256(reserve.lastUpdateTimestamp);
if (timeDelta < 1) {
return reserve.usageIndex;
}
return calculateCompoundedInterest(rateData.currentUsageRate, timeDelta).rayMul(reserve.usageIndex);
}
Updates

Lead Judging Commences

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

getNormalizedDebt returns totalUsage (amount) instead of usageIndex (rate) when timeDelta < 1, breaking interest calculations across the protocol

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

getNormalizedDebt returns totalUsage (amount) instead of usageIndex (rate) when timeDelta < 1, breaking interest calculations across the protocol

Support

FAQs

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