Core Contracts

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

Stale Reserve State in `setPrimeRate` Leading to Incorrect Maximum Change Validation

Summary

The setPrimeRate function in the ReserveLibrary does not update the reserve state (updateReserveInterests) before accessing the oldPrimeRate. This oversight can result in the use of stale data for the maximum change validation, potentially allowing invalid primeRate updates if interest has accrued since the last state update. This issue undermines the integrity of the interest rate adjustment mechanism.

Vulnerability Details

The setPrimeRate function is responsible for updating the primeRate, a critical parameter influencing borrowing and lending rates. Before updating the primeRate, the function checks whether the new rate exceeds the maximum allowed change (5% of the oldPrimeRate). However, the function does not call updateReserveInterests to refresh the reserve state before accessing oldPrimeRate.

function setPrimeRate( ReserveData storage reserve,ReserveRateData storage rateData,uint256 newPrimeRate) internal {
if (newPrimeRate < 1) revert PrimeRateMustBePositive();
uint256 oldPrimeRate = rateData.primeRate;
if (oldPrimeRate > 0) {
uint256 maxChange = oldPrimeRate.percentMul(500); // Max 5% change
uint256 diff = newPrimeRate > oldPrimeRate ? newPrimeRate - oldPrimeRate : oldPrimeRate - newPrimeRate;
if (diff > maxChange) revert PrimeRateChangeExceedsLimit();
}
rateData.primeRate = newPrimeRate;
updateInterestRatesAndLiquidity(reserve, rateData, 0, 0);
emit PrimeRateUpdated(oldPrimeRate, newPrimeRate);
}

If interest has accrued since the last state update, the oldPrimeRate used for validation may not reflect the current state of the reserve.

This can lead to two scenarios:

  1. Overly Permissive Validation: If the primeRate has increased due to accrued interest but the state is not updated, the maximum change check may allow a larger-than-intended adjustment.

  2. Overly Restrictive Validation: If the primeRate has decreased due to accrued interest, the maximum change check may incorrectly reject valid adjustments.

  • primeRate = 1e26 (RAY)

  • Last reserve state update timestamp: t0

  • Current block timestamp: t1 (where t1 > t0)

  • Due to accrued interest between t0 and t1, the effective primeRate increases to 1.1e26.

  • Admin calls setPrimeRate with newPrimeRate = 1.15e26 (a 15% increase from the initial primeRate).

  • The function uses the stale oldPrimeRate = 1e26 for validation.

  • maxChange = 1e26 * 5% = 5e24.

  • diff = 1.15e26 - 1e26 = 1.5e25.

  • Since 1.5e25 > 5e24, the function incorrectly allows the update, despite the effective primeRate already being 1.1e26.

// Initial primeRate = 1e26
// Accrued interest increases effective primeRate to 1.1e26
// Admin calls setPrimeRate with newPrimeRate = 1.15e26
function setPrimeRate(...) internal {
// Missing: updateReserveInterests(reserve, rateData);
uint256 oldPrimeRate = rateData.primeRate; // oldPrimeRate = 1e26 (stale)
uint256 maxChange = oldPrimeRate.percentMul(500); // maxChange = 5e24
uint256 diff = newPrimeRate > oldPrimeRate ? newPrimeRate - oldPrimeRate : oldPrimeRate - newPrimeRate; // diff = 1.5e25
if (diff > maxChange) revert PrimeRateChangeExceedsLimit(); // Does not revert (incorrect)
rateData.primeRate = newPrimeRate; // Updates to 1.15e26 (invalid)
}

Impact

Incorrect primeRate adjustments can lead to unfair borrowing costs or liquidity provider returns, undermining protocol trust.

Tools Used

Manual Review

Recommendations

Call updateReserveInterests at the start of setPrimeRate to ensure oldPrimeRate reflects the current state:

function setPrimeRate(...) internal {
updateReserveInterests(reserve, rateData); // Refresh reserve state
uint256 oldPrimeRate = rateData.primeRate; // Use updated primeRate
// Proceed with validation and update
uint256 maxChange = oldPrimeRate.percentMul(500);
uint256 diff = newPrimeRate > oldPrimeRate ? newPrimeRate - oldPrimeRate : oldPrimeRate - newPrimeRate;
if (diff > maxChange) revert PrimeRateChangeExceedsLimit();
rateData.primeRate = newPrimeRate;
updateInterestRatesAndLiquidity(reserve, rateData, 0, 0);
}
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!