The setProtocolFeeRate() updates the protocol fee rate but fails to invoke the updateInterestRatesAndLiquidity() function afterward. This oversight result in incorrect calculations for interest rates and liquidity downstream.
The setProtocolFeeRate() function in LendingPool is implemented as follows:
This function directly updates the rateData.protocolFeeRate to the given newProtocolFeeRate but fails to invoke updateInterestRatesAndLiquidity() immediately after.
This omitted updateInterestRatesAndLiquidity() is used update the interest rates and liquidity based on the latest reserve state.
As seen, it internally calls calculateLiquidityRate() to determine the portion of the gross liquidity rate allocated to the protocol.
However, when the rateData.protocolFeeRate is updated, the rateData.currentLiquidityRate remains stale as it is not updated.
Now when a user calls a function such as deposit(), they will provide more liquidity to the protocol. Notice that the deposit() function inernally invokes updateInterestRatesAndLiquidity() to update the interest rates and liquidity based on the latest reserve state:
Since liquidity is increased, the reserve.totalLiquidity will be increased in updateInterestRatesAndLiquidity(). Then after, utilizationRate will be calculated based on this new liquidity amount and finally, the rateData.currentLiquidityRate will be calculated using this utilizationRate and protocolFeeRate.
But, remember that rateData.protocolFeeRate was also updated. Only that, it was not used to adjust the rateData.currentLiquidityRate based on liquidity at that time. Therefore, when the calculateLiquidityRate() is invoked with this new protocolFeeRate, the obtained currentLiquidityRate does not accurately reflect the state of the contract.
Also notice that after calculateLiquidityRate() has been called and rateData.currentLiquidityRate set, the function proceeds to invoke updateReserveInterests(reserve, rateData) which uses this rateData.currentLiquidityRate to set a new liquidity index using linear interest:
In contrast to this, notice how setPrimeRate() works:
It calls ReserveLibrary.setPrimeRate() which does the following:
This final call after setting a new primeRate ensures that the contract state is harmonized to reflect this new change so that subsequent operations may operate with updated values.
When the protocol fee rate is updated, the interest rates and liquidity calculations do not reflect this change, leading to potential inaccuracies in user transactions.
Manual Review
Modify setProtocolFeeRate() to include a call to updateInterestRatesAndLiquidity() immediately after updating the protocolFeeRate.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.