Stratax Contracts

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

Strict Equality Check On Post-Swap Borrow Token Balance Blocks Valid Operations

Author Revealed upon completion

The _executeOpenOperation() function in Stratax.sol records the borrow token balance before borrowing from Aave, borrows tokens, swaps them via the 1inch router, and then requires the post-swap borrow token balance to exactly equal the pre-borrow balance. This == comparison is a recognised anti-pattern in DeFi protocols that interact with DEX aggregators.

When the 1inch swap is executed with the PARTIAL_FILL flag enabled, the router may consume less than the full desc.amount and return unused tokens to the caller. In this case, the strict equality check fails and the entire flash loan transaction reverts. The 1inch API may return swap data with partial fill enabled depending on market conditions, particularly for illiquid pairs or large amounts.

// src/Stratax.sol, _executeOpenOperation()
uint256 prevBorrowTokenBalance = IERC20(flashParams.borrowToken).balanceOf(address(this));
aavePool.borrow(
flashParams.borrowToken,
flashParams.borrowAmount,
2,
0,
address(this)
);
IERC20(flashParams.borrowToken).approve(address(oneInchRouter), flashParams.borrowAmount);
uint256 returnAmount =
_call1InchSwap(flashParams.oneInchSwapData, flashParams.borrowToken, flashParams.minReturnAmount);
// @audit Strict equality (==) requires exact balance match after swap
// @audit If 1inch partial fill leaves any tokens, the entire flash loan reverts
uint256 afterSwapBorrowTokenbalance = IERC20(flashParams.borrowToken).balanceOf(address(this));
require(afterSwapBorrowTokenbalance == prevBorrowTokenBalance, "Borrow token left in contract");

Notably, the _executeUnwindOperation() function does not impose an equivalent strict equality check on its post-swap balance. It only verifies that the return amount covers the flash loan debt using a tolerant >= comparison. This asymmetry between the two operations suggests the strict equality in the open operation is overly restrictive rather than an intentional design choice.

// src/Stratax.sol, _executeUnwindOperation()
uint256 returnAmount = _call1InchSwap(unwindParams.oneInchSwapData, _asset, unwindParams.minReturnAmount);
uint256 totalDebt = _amount + _premium;
// @audit Tolerant >= comparison used here, unlike the == in _executeOpenOperation()
require(returnAmount >= totalDebt, "Insufficient funds to repay flash loan");

The contract already has a separate minReturnAmount check inside _call1InchSwap() that provides slippage protection. The strict balance check adds an additional, more fragile constraint on top of this existing safeguard.

This issue has a low impact as no funds are at risk since the transaction reverts atomically. The effect is a denial-of-service on position creation for swap routes where partial fills occur. This issue has a low likelihood as the primary failure scenario requires the 1inch API to return swap data with the PARTIAL_FILL flag, which is not the default behaviour for most token pairs. The likelihood increases for illiquid pairs, large swap amounts, or tokens with unusual decimal counts.

recommendation

Replace the strict equality check with a tolerant comparison that permits a small dust threshold of remaining borrow tokens. Define a constant DUST_THRESHOLD (for example, 100 wei) and require that the remaining borrow tokens after the swap do not exceed this threshold. Alternatively, remove the strict equality check entirely and rely on the existing minReturnAmount check inside _call1InchSwap() for economic protection, since it already ensures the swap produced sufficient output tokens.

Support

FAQs

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

Give us feedback!