Stratax Contracts

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

Unsafe `transfer()` and `transferFrom()` without SafeERC20 wrapper breaks USDT and other non-standard ERC20 tokens

Root + Impact

Stratax.sol uses bare IERC20.transfer(), transferFrom(), and approve() calls which expect a bool return. USDT returns void from these functions. Solidity ^0.8.13 ABI-decodes 0 bytes as bool → decode failure → revert. USDT is completely unusable in Stratax.

Description

  • Stratax imports IERC20 from forge-std/interfaces/IERC20.sol (L4), which defines transfer() as returning bool. USDT's transfer() returns void.

  • Affected locations: transfer() at L283, transferFrom() at L325, and approve() at L495, L510, L530, L534, L559, L583, L593, L597 — covering both open and unwind flows.

  • USDT meets all README token requirements: Aave V3 supported, Chainlink feed, not fee-on-transfer, not rebasing, not exotic, 1inch supported.

// L283: IERC20(_token).transfer(owner, _amount); // @> USDT reverts
// L325: IERC20(_flashLoanToken).transferFrom(...); // @> USDT reverts
// L495+: IERC20(_asset).approve(address(aavePool), ...); // @> USDT reverts
// Root cause: forge-std IERC20 expects bool, USDT returns void → ABI decode failure

Risk

Likelihood: USDT is the 3rd largest cryptocurrency. Any interaction with USDT deterministically reverts — not conditional or edge-case.

Impact: createLeveragedPosition(), recoverTokens(), and all flash loan callbacks revert with USDT. Core protocol functionality broken for a major supported token.

Proof of Concept

USDT's mainnet contract (0xdAC17F958D2ee523a2206206994597C13D831ec7) defines transfer(address, uint) without returns (bool). The forge-std IERC20 interface expects bool. Solidity ^0.8.13 receives 0 bytes, expects 32 bytes for bool decode → reverts.

// USDT: function transfer(address _to, uint _value) public { ... } ← no returns
// IERC20: function transfer(address, uint256) external returns (bool); ← expects bool
// Result: 0 bytes returned → ABI decoder expects 32 bytes → REVERT

Recommended Mitigation

Use OpenZeppelin's SafeERC20 which handles tokens returning void by checking return data length. Use forceApprove() for approve() calls, which also handles USDT's require-zero-before-nonzero allowance pattern.

+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract Stratax is Initializable {
+ using SafeERC20 for IERC20;
- IERC20(_token).transfer(owner, _amount);
+ IERC20(_token).safeTransfer(owner, _amount);
- IERC20(_flashLoanToken).transferFrom(...);
+ IERC20(_flashLoanToken).safeTransferFrom(...);
- IERC20(_asset).approve(address(aavePool), totalCollateral);
+ IERC20(_asset).forceApprove(address(aavePool), totalCollateral);
}
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!