Core Contracts

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

A `newProtocolFeeRate` can be applied retroactively

Summary

The LendingPool.setProtocolFeeRate function sets the protocol fee rate but does not update the reserve applying the reserve interests for the period since the last update. This way the next update will apply the new protocolFeeRate retroactively.

Vulnerability Details

The LendingPool.setProtocolFeeRate does not update the reserve:

function setProtocolFeeRate(uint256 newProtocolFeeRate) external onlyOwner {
rateData.protocolFeeRate = newProtocolFeeRate;
}

The protocolFeeRate is used in the rateData.currentLiquidityRate value calculation.
ReserveLibrary.sol:

function updateInterestRatesAndLiquidity(ReserveData storage reserve,ReserveRateData storage rateData,uint256 liquidityAdded,uint256 liquidityTaken) internal {
<...>
// Update current liquidity rate
rateData.currentLiquidityRate = calculateLiquidityRate(
utilizationRate,
rateData.currentUsageRate,
>> rateData.protocolFeeRate,
totalDebt
);
// Update the reserve interests
>> updateReserveInterests(reserve, rateData);

Then the new rateData.currentLiquidityRate is used in the updateReserveInterests function which updates the reserve interests and can be applied retroactively.
ReserveLibrary.sol:

function updateReserveInterests(ReserveData storage reserve,ReserveRateData storage rateData) internal {
>> uint256 timeDelta = block.timestamp - uint256(reserve.lastUpdateTimestamp);
if (timeDelta < 1) {
return;
}
uint256 oldLiquidityIndex = reserve.liquidityIndex;
if (oldLiquidityIndex < 1) revert LiquidityIndexIsZero();
// 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
);
// Update the last update timestamp
>> reserve.lastUpdateTimestamp = uint40(block.timestamp);
emit ReserveInterestsUpdated(reserve.liquidityIndex, reserve.usageIndex);
}

Impact

Unintended behavior due to retroactive application of the new protocolFeeRate, assets losses.

Tools used

Manual Review

Recommendations

Consider updating the reserve interests before setting a new protocolFeeRate:

function setProtocolFeeRate(uint256 newProtocolFeeRate) external onlyOwner {
+ ReserveLibrary.updateReserveState(reserve, rateData);
rateData.protocolFeeRate = newProtocolFeeRate;
}
Updates

Lead Judging Commences

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

ReserveLibrary fails to update reserve state before changing rate parameters (prime rate, protocol fee rate), causing new rates to be applied retroactively to interest since last update

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

ReserveLibrary fails to update reserve state before changing rate parameters (prime rate, protocol fee rate), causing new rates to be applied retroactively to interest since last update

Support

FAQs

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

Give us feedback!