When Aave pool utilization is high, available liquidity is low. withdraw() returns only the amount actually available (which can be less than requested), and borrow() can revert or fail when liquidity is insufficient.
The protocol does not check utilization before createLeveragedPosition or unwindPosition. In the unwind flow, aavePool.withdraw() returns withdrawnAmount, which may be less than collateralToWithdraw when utilization is high. The swap then receives less input than expected, and require(returnAmount >= totalDebt) reverts. The flash loan callback fails; the user cannot unwind.
Likelihood (low):
High utilization occurs during stress (e.g., mass liquidations, large withdrawals, or concentrated borrowing). Mainnet Aave pools for USDC/WETH typically have deep liquidity.
Impact (medium):
Users cannot open or unwind positions. Unwind reverts inside the flash loan callback, leaving positions stuck until utilization drops. Open flow can revert at borrow() or supply().
Severity (low):
Aave USDC reserve reaches 99% utilization. User calls unwindPosition to unwind a USDC-collateralized position. aavePool.withdraw(USDC, collateralToWithdraw, ...) returns only 10% of the requested amount due to lack of liquidity. The 1inch swap receives far less USDC than needed to repay the flash loan. require(returnAmount >= totalDebt) reverts. The entire transaction fails; the user cannot unwind.
Use aaveDataProvider (IProtocolDataProvider) before initiating operations. Obtain reserve token addresses via getReserveTokensAddresses(asset), then compute utilization from aToken and debt token totalSupply():
For unwindPosition, add the same utilization check for both collateral and debt tokens before initiating the flash loan. Use aaveDataProvider.getReserveTokensAddresses as above.
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.
The contest is complete and the rewards are being distributed.