Core Contracts

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

Excessive liquidity and usage Index growth in LendingPool

Summary

In LendingPool contract, uncontrolled growth of liquidityIndex and **usageIndex **will be higher when after many txs happened and totalDebt = totalLiquidity = 0. This results in an inflated index over time, leading to unintended financial consequences for borrowers and lenders.

Vulnerability Details

function updateReserveInterests(ReserveData storage reserve,ReserveRateData storage rateData) internal {
...
// Update liquidity index using linear interest
==> reserve.liquidityIndex = calculateLiquidityIndex(
rateData.currentLiquidityRate,
timeDelta,
reserve.liquidityIndex
);
// Update usage index (debt index) using compounded interest
--> reserve.usageIndex = calculateUsageIndex(
rateData.currentUsageRate,
timeDelta,
reserve.usageIndex
);
...
}
function calculateLinearInterest(uint256 rate, uint256 timeDelta, uint256 lastIndex) internal pure returns (uint256) {
uint256 cumulatedInterest = rate * timeDelta;
cumulatedInterest = cumulatedInterest / SECONDS_PER_YEAR;
return WadRayMath.RAY + cumulatedInterest;
}
==> function calculateLiquidityIndex(uint256 rate, uint256 timeDelta, uint256 lastIndex) internal pure returns (uint128) {
uint256 cumulatedInterest = calculateLinearInterest(rate, timeDelta, lastIndex);
return cumulatedInterest.rayMul(lastIndex).toUint128();
}
function calculateCompoundedInterest(uint256 rate,uint256 timeDelta) internal pure returns (uint256) {
if (timeDelta < 1) {
return WadRayMath.RAY;
}
uint256 ratePerSecond = rate.rayDiv(SECONDS_PER_YEAR);
uint256 exponent = ratePerSecond.rayMul(timeDelta);
// Will use a taylor series expansion (7 terms)
return WadRayMath.rayExp(exponent);
}
--> function calculateUsageIndex(uint256 rate, uint256 timeDelta ,uint256 lastIndex) internal pure returns (uint128) {
uint256 interestFactor = calculateCompoundedInterest(rate, timeDelta);
return lastIndex.rayMul(interestFactor).toUint128();
}

As seen above, reserve.liquidityIndex and reserve.usageIndex are always increasing, due to initial values are 1e27.

reserve.liquidityIndex = (1e27 + XXX) * reserve.liquidityIndex / 1e27

reserve.usageIndex = (1e27 + ...) * reserve.usageIndex / 1e27

After many txs such as deposit(), withdraw(), depositNFT(), borrow(), repay(), assume totalDebt=totalLiquidity=0, these index values will be much higher. But this will be the same as initial state and only index values are high.

There's no condition to prevent index growth and will let the protocol disrutpion.

Impact

If liquidityIndex and usageIndex increase without bound, future deposits and debt calculations will be skewed, potentially making lending and borrowing infeasible.

Users may exploit this by making micro-deposits and withdrawals repeatedly to manipulate the index values artificially, causing economic losses for future users.

Tools Used

manual

Recommendations

Stop index growth when reserve is empty.

Apply rebalance method for index control.

Updates

Lead Judging Commences

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

LendingPool's usageIndex compounds at baseRate even when no debt exists, creating artificially high starting interest for future borrowers and discouraging protocol adoption

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

LendingPool's usageIndex compounds at baseRate even when no debt exists, creating artificially high starting interest for future borrowers and discouraging protocol adoption

Support

FAQs

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

Give us feedback!