Core Contracts

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

Primate Rate is updated without updating the actual contract state.

Summary

Users will be charged a higher or lower fee than they should be when the Set prime rate is called this will affect the debt index in the contract.

Vulnerability Details

Lack of a state update before setting Primerate will cause issues

/**
* @notice Sets the prime rate of the reserve
* @param newPrimeRate The new prime rate (in RAY)
*/
function setPrimeRate(uint256 newPrimeRate) external onlyPrimeRateOracle { // bug UPDATE state first
ReserveLibrary.setPrimeRate(reserve, rateData, newPrimeRate);
}

see

/**
* @notice Sets a new prime rate for the reserve.
* @param reserve The reserve data.
* @param rateData The reserve rate parameters.
* @param newPrimeRate The new prime rate (in RAY).
*/
function setPrimeRate( ReserveData storage reserve,ReserveRateData storage rateData,uint256 newPrimeRate) internal {
if (newPrimeRate < 1) revert PrimeRateMustBePositive();
uint256 oldPrimeRate = rateData.primeRate;
if (oldPrimeRate > 0) {
@audit>>> uint256 maxChange = oldPrimeRate.percentMul(500); // Max 5% change
@audit>>> uint256 diff = newPrimeRate > oldPrimeRate ? newPrimeRate - oldPrimeRate : oldPrimeRate - newPrimeRate;
@audit>>> if (diff > maxChange) revert PrimeRateChangeExceedsLimit();
}
@audit>>> rateData.primeRate = newPrimeRate;
@audit>>>update before also updateInterestRatesAndLiquidity(reserve, rateData, 0, 0);
emit PrimeRateUpdated(oldPrimeRate, newPrimeRate);
}

Updating before will make the contract updated first before apply a change in fee, max deviation can be as high as 5% for the prime change

/**
* @notice Updates the interest rates and liquidity based on the latest reserve state.
* @dev Should be called after any operation that changes the liquidity or debt of the reserve.
* @param reserve The reserve data.
* @param rateData The reserve rate parameters.
* @param liquidityAdded The amount of liquidity added (in underlying asset units).
* @param liquidityTaken The amount of liquidity taken (in underlying asset units).
*/
function updateInterestRatesAndLiquidity(ReserveData storage reserve,ReserveRateData storage rateData,uint256 liquidityAdded,uint256 liquidityTaken) internal {
// Update total liquidity
if (liquidityAdded > 0) {
reserve.totalLiquidity = reserve.totalLiquidity + liquidityAdded.toUint128();
}
if (liquidityTaken > 0) {
if (reserve.totalLiquidity < liquidityTaken) revert InsufficientLiquidity();
reserve.totalLiquidity = reserve.totalLiquidity - liquidityTaken.toUint128();
}
uint256 totalLiquidity = reserve.totalLiquidity;
uint256 totalDebt = reserve.totalUsage;
uint256 computedDebt = getNormalizedDebt(reserve, rateData);
uint256 computedLiquidity = getNormalizedIncome(reserve, rateData);
// Calculate utilization rate
uint256 utilizationRate = calculateUtilizationRate(reserve.totalLiquidity, reserve.totalUsage); // what is happening here is usage a rate or usage is debt
// Update current usage rate (borrow rate)
rateData.currentUsageRate = calculateBorrowRate(
@audit>>> rateData.primeRate,
rateData.baseRate,
rateData.optimalRate,
rateData.maxRate,
rateData.optimalUtilizationRate,
utilizationRate
);
// Update current liquidity rate
rateData.currentLiquidityRate = calculateLiquidityRate(
utilizationRate,
rateData.currentUsageRate,
rateData.protocolFeeRate,
totalDebt
);
// Update the reserve interests
updateReserveInterests(reserve, rateData);
emit InterestRatesUpdated(rateData.currentLiquidityRate, rateData.currentUsageRate);
}
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) {
@audit>> uint256 rateSlope = primeRate - baseRate;
@audit>> uint256 rateIncrease = utilizationRate.rayMul(rateSlope).rayDiv(optimalUtilizationRate);
rate = baseRate + rateIncrease;
} else {
uint256 excessUtilization = utilizationRate - optimalUtilizationRate;
uint256 maxExcessUtilization = WadRayMath.RAY - optimalUtilizationRate;
@audit>> uint256 rateSlope = maxRate - primeRate;
@audit>> @audit>> uint256 rateIncrease = excessUtilization.rayMul(rateSlope).rayDiv(maxExcessUtilization);
@audit>> rate = primeRate + rateIncrease;
}
return rate;
}

Impact

This will affect the debt index of the lending contract

Tools Used

Manual Review

Recommendations

Update the state before changing the prime rate

Updates

Lead Judging Commences

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

setPrimeRate applies new rates retroactively by updating rates after changing primeRate, causing incorrect interest calculations for past periods

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

setPrimeRate applies new rates retroactively by updating rates after changing primeRate, causing incorrect interest calculations for past periods

Support

FAQs

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