The contract computes collateralToWithdraw and calls aavePool.withdraw(collateralToken, collateralToWithdraw, address(this)).
Aave's withdraw can return less than the requested amount (rounding, dust, interest accrual, or protocol-specific behavior).
The returned withdrawnAmount is used for the 1inch swap. If it is less than expected, returnAmount may be below totalDebt, causing a revert at the flash loan repayment check.
Likelihood (medium):
Aave V3 withdraw can round down; low-decimal tokens or dust can produce smaller amounts.
The owner sets minReturnAmount based on expected collateral; they may not account for withdraw rounding.
Impact (medium):
Unwind reverts inside the flash loan callback.
Position cannot be unwound; user/protocol may be stuck until conditions improve.
Severity (medium):
Aave's withdraw returns the actual amount transferred, which can be less than the requested amount. This happens because Aave converts between underlying and aToken shares with rounding that favors the protocol (rounds down when burning aTokens). For low-decimal tokens or positions with dust, the difference can be non-trivial. The contract assumes withdrawnAmount equals collateralToWithdraw when building the swap and setting minReturnAmount; it does not account for Aave returning less.
The chain of effects: less collateral withdrawn → less input to the 1inch swap → less debt token received → returnAmount < totalDebt → revert at the flash loan repayment check. The owner typically sets minReturnAmount based on the formula-derived collateralToWithdraw (or calculateUnwindParams), not on the actual withdrawnAmount. If the swap passes minReturnAmount (because the owner used a conservative buffer) but still returns less than totalDebt, the unwind reverts. The revert occurs inside the flash loan callback, so the entire transaction fails and the position remains leveraged.
Option 1: Scale minReturnAmount by actual withdrawn amount (on-chain fix)
When withdrawnAmount is less than collateralToWithdraw, scale the owner's minReturnAmount proportionally so the swap does not revert with "Insufficient return amount from swap" when the swap would have returned enough. This helps when the swap overperforms (e.g. better routing) despite less collateral input. Note: collateralToWithdraw is scoped inside the block, so it must be moved out or the scaling done inside the block. The require(returnAmount >= totalDebt) still enforces repayment—if the swap returns less than totalDebt, the unwind will still revert.
Option 2: Add helper to compute recommended minReturnAmount
The owner must set minReturnAmount to at least the amount needed to repay the flash loan (totalDebt). Setting it lower would allow swaps that return less than totalDebt, causing a revert at the repayment check. The helper should return the estimated totalDebt so the owner sets minReturnAmount correctly (e.g. minReturnAmount = recommendedMinReturn or slightly lower for swap slippage—but not so low that we accept less than totalDebt).
Option 3: Document owner responsibility
Document that the owner must set minReturnAmount to at least the estimated totalDebt (debt + flash loan premium). Setting it lower risks accepting a swap that returns insufficient funds, causing a revert at "Insufficient funds to repay flash loan". The owner cannot fix withdraw rounding by lowering minReturnAmount—if Aave returns less collateral, the swap will return less, and the unwind will revert. This relies on the owner and is not enforced on-chain.
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.