Damaged Tiger Panther
High
The lack of liquidity constraints in LendingPool will cause interest rate manipulation for lenders and the protocol, as a borrower can take all available liquidity, preventing proper interest accrual and leading to unexpected borrowing incentives.
In LendingPool.sol:borrow() the function only checks whether the requested amount is less than or equal to reserve.availableLiquidity() without enforcing a reserve buffer.
require(amount <= reserve.availableLiquidity(), "not enough available liquidity");
This allows borrowers to drain all liquidity and potentially minimize interest costs, especially in low-liquidity scenarios.
- A borrower needs to initiate a borrow transaction with amount == reserve.availableLiquidity().
- The LendingPool contract needs to allow 100% utilization without additional interest penalties.
- No dynamic interest rate scaling is in place to deter full liquidity borrowing.
- Market conditions allow for high-leverage borrowing.
- The TWAP price feed does not significantly adjust rates when liquidity is reduced.
- No secondary mechanisms exist to prevent full liquidity borrowing.
- The borrower calls borrow() with amount = reserve.availableLiquidity().
- The contract approves the borrow since the require check only validates available liquidity.
- The borrower escapes higher interest costs by ensuring full utilization before interest scaling triggers.
- Other lenders and users face liquidity shortages, affecting protocol efficiency.
Lenders experience reduced interest earnings, as borrowing costs are artificially lowered. The protocol may suffer liquidity shortages, limiting future borrowing availability. The borrower gains access to full liquidity without facing increasing borrowing costs.
function test_InterestManipulation() public {
// Assume borrower and LendingPool are already set up
uint256 borrowAmount = lendingPool.availableLiquidity();
lendingPool.borrow(asset, borrowAmount);
// Verify that interest rates remain unchanged despite full liquidity depletion
uint256 newRate = lendingPool.getBorrowingRate(asset);
assertEq(newRate, initialRate, "Interest should have increased");
}
- Implement a Reserve Buffer by modifying the require check to always leave a portion of liquidity available:
require(
amount <= reserve.availableLiquidity() * 95 / 100,
"not enough available liquidity"
);
This ensures some liquidity remains, forcing interest accrual.
- Utilisation-based Interest adjustment by implementing a dynamic interest rate model where borrowing costs increase as liquidity decreases:
function calculateInterestRate(uint256 borrowedAmount, uint256 availableLiquidity) public view returns (uint256){
uint256 utilizationRate = (borrowedAmount * 1e18) / (availableLiquidity + borrowedAmount);
return baseInterestRate + (utilizationRate * variableInterestMultiplier / 1e18);
}
This does not allow borrowers from maxing out liquidity without penalty.