Summary
The LendingPool
constructor has a flaw where it does not make sure the prime rate is RAY based which if not, there is no recovery to increase it without redeployment.
Vulnerability Details
[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L185)
The constructor is only checking that it is not 0:
constructor(
address _reserveAssetAddress,
address _rTokenAddress,
address _debtTokenAddress,
address _raacNFTAddress,
address _priceOracleAddress,
uint256 _initialPrimeRate
) Ownable(msg.sender) {
if (
_reserveAssetAddress == address(0) ||
_rTokenAddress == address(0) ||
_debtTokenAddress == address(0) ||
_raacNFTAddress == address(0) ||
_priceOracleAddress == address(0) ||
_initialPrimeRate == 0
) revert AddressCannotBeZero();
If at deployment you don't pass in a RAY amount you are forced to redeploy due to the 5% limit on change of primeRate
which is not acheivable:
[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L679)
We call LendingPool::setPrimeRate
to change the primeRate
which calls the ReserveLibrary::setPrimeRate
function setPrimeRate(uint256 newPrimeRate) external onlyPrimeRateOracle {
ReserveLibrary.setPrimeRate(reserve, rateData, newPrimeRate);
}
[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/libraries/pools/ReserveLibrary.sol#L404C2-L408C10)
Inside ReserveLibrary.setPrimeRate
there is a check that you can only change primeRate
by 5%:
if (oldPrimeRate > 0) {
@> uint256 maxChange = oldPrimeRate.percentMul(500);
uint256 diff = newPrimeRate > oldPrimeRate ? newPrimeRate - oldPrimeRate : oldPrimeRate - newPrimeRate;
@> if (diff > maxChange) revert PrimeRateChangeExceedsLimit();
}
Impact
If the initial prime rate is set to a non-RAY value (not 1e27) during deployment, there is no recovery mechanism since:
The prime rate can only be changed by 5% at a time through setPrimeRate
To reach RAY (1e27) from a much lower value would require a massive amount incremental changes
The only solution would be to redeploy the entire lending pool contract
This causes significant operational overhead and requires complex migration procedures
Tools Used
Foundry
Recommendations
Add a validation check in the constructor to ensure the initial prime rate is RAY-based:
constructor(
address _reserveAssetAddress,
address _rTokenAddress,
address _debtTokenAddress,
address _raacNFTAddress,
address _priceOracleAddress,
uint256 _initialPrimeRate
) Ownable(msg.sender) {
if (
_reserveAssetAddress == address(0) ||
_rTokenAddress == address(0) ||
_debtTokenAddress == address(0) ||
_raacNFTAddress == address(0) ||
_priceOracleAddress == address(0) ||
+ _initialPrimeRate == 0 ||
+ _initialPrimeRate < 1e27 // Add check for RAY-based value
) revert AddressCannotBeZero();
// ... rest of constructor
}
Additionally, consider updating the ReserveLibrary.setPrimeRate function to include the same validation:
function setPrimeRate(
ReserveData storage reserve,
ReserveRateData storage rateData,
uint256 newPrimeRate
) internal {
- if (newPrimeRate < 1) revert PrimeRateMustBePositive();
+ if (newPrimeRate < 1e27) revert PrimeRateMustBePositive();
// ... rest of function
}