Stratax Contracts

First Flight #57
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: medium
Likelihood: medium

Unwind leftovers supplied as debt token collateral to Aave instead of returned to user

Author Revealed upon completion

Root Cause + Impact

After flash loan repayment in _executeUnwindOperation(), excess tokens are supplied back to Aave as collateral (L591-595). But _asset in the unwind path is the debt token (the flash loan token), not the collateral token. Leftover debt tokens become a new, unintended collateral position in Aave under the Stratax contract, and the user never receives them.

Description

After repaying the flash loan, any excess tokens should be returned to the user. In the open path (L529-532), the leftover handling is correct because _asset is the collateral token. Supplying leftover collateral back to Aave improves the position.

In the unwind path (L591-595), the same pattern is copy-pasted, but _asset is the debt token (because the flash loan borrows the debt token to repay Aave debt):

// Stratax.sol:591-595 (unwind path)
if (returnAmount - totalDebt > 0) {
IERC20(_asset).approve(address(aavePool), returnAmount - totalDebt);
// @> aavePool.supply(_asset, returnAmount - totalDebt, address(this), 0);
}

For an ETH/USDC position (ETH collateral, USDC debt), the leftover USDC gets supplied to Aave as USDC collateral. This USDC is not part of the user's intended position and is stranded in the contract's Aave account.

The owner can technically recover via recoverTokens() on the aToken address, but this is a manual process that requires knowing the correct aToken address.

Risk

Likelihood: Medium

Leftovers occur whenever the DEX swap produces slightly more than needed to repay the flash loan. This is common because swap routes are quoted off-chain with slippage buffers. Not every unwind produces leftovers, but a significant portion will.

Impact: Medium

Leftover tokens are locked as collateral in Aave. The user never receives them. For a 10 ETH position unwind, even a 0.1% excess on the swap produces ~$30 of stranded USDC collateral. Over many unwinds across all users, this accumulates. Recovery requires admin intervention.

Proof of Concept

Trace the code path for unwinding an ETH/USDC position (ETH collateral, USDC debt):

  1. Flash loan borrows 5000 USDC (_asset = USDC)

  2. Repay 5000 USDC debt to Aave (L559-560)

  3. Withdraw ETH collateral from Aave (L579)

  4. Swap ETH to USDC via 1inch, receive 5010 USDC (L584)

  5. totalDebt = 5000 + 4.5 (premium) = 5004.5 USDC (L587)

  6. returnAmount - totalDebt = 5010 - 5004.5 = 5.5 USDC (L591)

  7. aavePool.supply(USDC, 5.5 USDC, address(this), 0) supplies USDC as collateral (L593-594)

  8. User receives nothing. 5.5 USDC is now aUSDC in the contract's Aave position.

Recommended Mitigation

Transfer leftover tokens to the user instead of supplying them as wrong-type collateral to Aave:

// Stratax.sol:590-595
if (returnAmount - totalDebt > 0) {
- IERC20(_asset).approve(address(aavePool), returnAmount - totalDebt);
- aavePool.supply(_asset, returnAmount - totalDebt, address(this), 0);
+ IERC20(_asset).transfer(user, returnAmount - totalDebt);
}

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!