Core Contracts

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

prime rate is static throughout whole protocol.

Summary

The prime rate is being initialized in LendingPool.sol constructor via -

rateData.primeRate = uint256(_initialPrimeRate);

and throughout the whole protocol, same value is being used. In ReserveLibrary.sol::calculateBorrowRate() function too -

function calculateBorrowRate(
uint256 primeRate,
uint256 baseRate,
uint256 optimalRate,
uint256 maxRate,
uint256 optimalUtilizationRate,
uint256 utilizationRate
) internal pure returns (uint256) {
if (primeRate <= baseRate || primeRate >= maxRate || optimalRate <= baseRate || optimalRate >= maxRate) {
revert InvalidInterestRateParameters();
}
uint256 rate;
if (utilizationRate <= optimalUtilizationRate) {
uint256 rateSlope = primeRate - baseRate;
uint256 rateIncrease = utilizationRate.rayMul(rateSlope).rayDiv(optimalUtilizationRate);
rate = baseRate + rateIncrease;
} else {
uint256 excessUtilization = utilizationRate - optimalUtilizationRate;
uint256 maxExcessUtilization = WadRayMath.RAY - optimalUtilizationRate;
uint256 rateSlope = maxRate - primeRate;
uint256 rateIncrease = excessUtilization.rayMul(rateSlope).rayDiv(maxExcessUtilization);
rate = primeRate + rateIncrease;
}
return rate;
}

As per RAACPrimeRateOracle doc [https://docs.raac.io/core/oracles/RAACPrimeRateOracle] -

Purpose

  • Fetch real-time prime rate data from off-chain sources

  • Securely update prime rates in the LendingPool contract

  • Provide a trusted prime rate feed for the lending protocol

  • Track historical prime rate updates with timestamps

But the prime rate being used in ReserveLibrary.sol is constant.

Vulnerability Details

  • in current implementation prime rate is not adjusting according to market supply/demand.

  • more importantly, it can adjust to discourage over-borrowing. In case of liquidity shortage.

Impact

This can lead to plenty of issues -

  1. In case of high volatile NFT or RWA assets, the static value of prime rate can give incorrect borrow rate amount, causing loss to protocol or user.

  2. dyanamic prime rates , adjusts very well with risk-free rate changes, but in current implemetation prime rate is static.

  3. LendingPool.sol can be drained out, due to liquidity shortage. If dynamic prime rate is being used it will discourage borrower to borrow further hence preventing draining.

Tools Used

Manual

Recommendations

There is getPrimeRate() function in RAACPrimeRateOracle.sol unused, that can be leveraged to make improvemnt in current implementation :-

RAACPrimeRateOracle.sol::getPrimeRate() ->

function getPrimeRate() external view returns (uint256) {
return lastPrimeRate;
}

Improved code ->

function calculateBorrowRate(
uint256 primeRate,
uint256 baseRate,
uint256 optimalRate,
uint256 maxRate,
uint256 optimalUtilizationRate,
uint256 utilizationRate
) internal pure returns (uint256) {
+ uint256 primeRate = primeRate > raacPrimeRateOracle.getPrimeRate()
+ ? primeRate
+ : raacPrimeRateOracle.getPrimeRate();
if (primeRate <= baseRate || primeRate >= maxRate || optimalRate <= baseRate || optimalRate >= maxRate) {
revert InvalidInterestRateParameters();
}
uint256 rate;
if (utilizationRate <= optimalUtilizationRate) {
uint256 rateSlope = primeRate - baseRate;
uint256 rateIncrease = utilizationRate.rayMul(rateSlope).rayDiv(optimalUtilizationRate);
rate = baseRate + rateIncrease;
} else {
uint256 excessUtilization = utilizationRate - optimalUtilizationRate;
uint256 maxExcessUtilization = WadRayMath.RAY - optimalUtilizationRate;
uint256 rateSlope = maxRate - primeRate;
uint256 rateIncrease = excessUtilization.rayMul(rateSlope).rayDiv(maxExcessUtilization);
rate = primeRate + rateIncrease;
}
return rate;
}

In the proved code the prime is being fetched from RAACPrimeRateOracle, if getPrimeRate() is greater than primeRate use fetched value, and if it's less then use the initial primeRate value.

Updates

Lead Judging Commences

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

Support

FAQs

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

Give us feedback!