Stratax Contracts

First Flight #57
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Impact: high
Likelihood: low
Invalid

Unchecked transferFrom Return Value Allows Flash Loan Without Collateral

Root + Impact

Location: src/Stratax.sol:325

Description

  • createLeveragedPosition pulls user collateral via transferFrom before initiating the flash loan. The return value is not checked.

  • If the token returns false instead of reverting, the contract records _collateralAmount in the flash loan params, but never received the tokens. The flash loan callback then attempts to supply _amount + flashParams.collateralAmount to Aave with only the flash-loaned funds present.

// src/Stratax.sol:325
IERC20(_flashLoanToken).transferFrom(msg.sender, address(this), _collateralAmount); // @> return value ignored
// ...
bytes memory encodedParams = abi.encode(OperationType.OPEN, msg.sender, params); // @> collateralAmount encoded as if received
aavePool.flashLoanSimple(address(this), _flashLoanToken, _flashLoanAmount, encodedParams, 0);

Risk

Likelihood:

  • Non-reverting ERC20 tokens return false on failed transfers — USDT, BNB among others exhibit this behaviour

  • The collateral transfer is the first action in the function, before any further validation

Impact:

  • Flash loan is initiated with inflated collateral params; Aave supply() call reverts mid-callback with the flash loan unrepaid

  • Depending on prior contract token balances, the callback could partially succeed with incorrect position accounting

Proof of Concept

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {Stratax} from "../../src/Stratax.sol";
contract NonRevertingToken {
mapping(address => uint256) public balanceOf;
function transferFrom(address, address, uint256) external pure returns (bool) {
return false; // user has no balance — returns false instead of reverting
}
function approve(address, uint256) external pure returns (bool) { return true; }
}
contract UncheckedTransferFromPoCTest is Test {
Stratax stratax;
NonRevertingToken token;
function test_flashLoanInitiatedWithoutCollateral() public {
token = new NonRevertingToken();
address owner = stratax.owner();
vm.prank(owner);
// transferFrom returns false — collateral never received
// flash loan is initiated as if 1000e6 collateral was deposited
// _executeOpenOperation then tries to supply flashAmount + 1000e6
// Aave supply reverts because only flashAmount is in the contract
vm.expectRevert(); // reverts inside flash loan callback
stratax.createLeveragedPosition(
address(token), 500e6, 1000e6, address(0), 0, "", 0
);
}
}

Recommended Mitigation

Replace the bare transferFrom with SafeERC20.safeTransferFrom() which reverts immediately if the transfer fails, preventing the flash loan from being initiated with unverified collateral amounts.

- IERC20(_flashLoanToken).transferFrom(msg.sender, address(this), _collateralAmount);
+ SafeERC20.safeTransferFrom(IERC20(_flashLoanToken), msg.sender, address(this), _collateralAmount);
Updates

Lead Judging Commences

izuman Lead Judge 16 days ago
Submission Judgement Published
Invalidated
Reason: Out of scope
Assigned finding tags:

WEIRD ERC20 Tokens

Currently there is no support for weird ERC20 tokens i.e. FOT tokens, missing return values, reentrancy etc.

Support

FAQs

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

Give us feedback!