In _executeOpenOperation(), the swap converts borrowToken to collateralToken. But _call1InchSwap is called with borrowToken as the _asset parameter (L514). When the 1inch router returns empty data (the fallback path), the function checks the balance of borrowToken instead of collateralToken. Since all borrowTokens were consumed by the swap, this returns ~0, and the subsequent require(returnAmount >= totalDebt) at L522 reverts.
_executeOpenOperation() borrows tokens from Aave, swaps them via 1inch to get collateral tokens, then uses the proceeds to repay the flash loan. _call1InchSwap handles the swap and return value decoding.
At L513-514, _call1InchSwap is called with flashParams.borrowToken:
Inside _call1InchSwap, when the 1inch router's low-level call returns no data (result.length == 0), the fallback at L624-625 checks the balance of _asset:
Since _asset is the borrow token and the swap consumed all borrow tokens, the balance is ~0. Then at L522, require(returnAmount >= totalDebt) fails because 0 < totalDebt.
The primary path (when result.length > 0) works correctly. The unwind path (L584) is also correct because _asset there is the debt token, which IS the swap output.
Likelihood: Medium
The fallback path triggers when the 1inch router doesn't return data. The standard swap() function returns data, but unoswap() and some legacy router functions may not. 1inch has multiple router versions deployed on different chains.
Impact: Medium
When triggered, createLeveragedPosition() always reverts for the affected swap route. Users cannot open leveraged positions through that path. No funds are lost (the whole transaction reverts atomically), but core protocol functionality is broken for those routes.
Code path trace for createLeveragedPosition(WETH, 2e18, 1e18, USDC, 5000e6, swapData, 4990e6):
Flash loan: 2 WETH received (_asset = WETH)
Supply 3 WETH to Aave as collateral
Borrow 5000 USDC from Aave
Swap: 5000 USDC -> ~2.05 WETH via 1inch
_call1InchSwap(swapData, USDC, 4990e6) called with _asset = USDC
If 1inch returns empty data: returnAmount = IERC20(USDC).balanceOf(this) = 0 (all USDC swapped away)
require(0 >= totalDebt) -> reverts
The fix is one word: pass _asset (the collateral/flash loan token) instead of flashParams.borrowToken.
Pass _asset (the collateral/flash loan token) instead of flashParams.borrowToken. In _executeOpenOperation, _asset is the swap output token, so the fallback balance check will read the correct balance:
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.