3

I want to swap some eth (that i receive from a flashloan) for x amount of a token. Then swap the token for some eth, pay back the flashloan and keep profit. For the flashloan i used dYdX and to swap i used Uniswap and Sushiswap. When i run the code i receive this message: Reason provided by the contract: "TransferHelper: TRANSFER_FROM_FAILED". I'm trying to figure out where does the error comes from. Here's the code i will separate it because its a very big contract:

Flashloan

interface Structs {
    struct Val {
        uint256 value;
    }

    enum ActionType {
        Deposit, // supply tokens
        Withdraw, // borrow tokens
        Transfer, // transfer balance between accounts
        Buy, // buy an amount of some token (externally)
        Sell, // sell an amount of some token (externally)
        Trade, // trade tokens against another account
        Liquidate, // liquidate an undercollateralized or expiring account
        Vaporize, // use excess tokens to zero-out a completely negative account
        Call // send arbitrary data to an address
    }

    enum AssetDenomination {
        Wei // the amount is denominated in wei
    }

    enum AssetReference {
        Delta // the amount is given as a delta from the current value
    }

    struct AssetAmount {
        bool sign; // true if positive
        AssetDenomination denomination;
        AssetReference ref;
        uint256 value;
    }

    struct ActionArgs {
        ActionType actionType;
        uint256 accountId;
        AssetAmount amount;
        uint256 primaryMarketId;
        uint256 secondaryMarketId;
        address otherAddress;
        uint256 otherAccountId;
        bytes data;
    }

    struct Info {
        address owner; // The address that owns the account
        uint256 number; // A nonce that allows a single address to control many accounts
    }

    struct Wei {
        bool sign; // true if positive
        uint256 value;
    }
}

abstract contract DyDxPool is Structs {
    function getAccountWei(Info memory account, uint256 marketId)
        public
        view
        virtual
        returns (Wei memory);

    function operate(Info[] memory, ActionArgs[] memory) public virtual;
}

contract DyDxFlashLoan is Structs {
    DyDxPool pool = DyDxPool(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);

    address public WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    mapping(address => uint256) public currencies;

    constructor() {
        currencies[WETH] = 1;
    }

    modifier onlyPool() {
        require(
            msg.sender == address(pool),
            "FlashLoan: could be called by DyDx pool only"
        );
        _;
    }

    function tokenToMarketId(address token) public view returns (uint256) {
        uint256 marketId = currencies[token];
        require(marketId != 0, "FlashLoan: Unsupported token");
        return marketId - 1;
    }

    // the DyDx will call `callFunction(address sender, Info memory accountInfo, bytes memory data) public` after during `operate` call
    function flashloan(
        address token,
        uint256 amount,
        bytes memory data
    ) internal {
        IERC20(token).approve(address(pool), amount + 1);
        Info[] memory infos = new Info[](1);
        ActionArgs[] memory args = new ActionArgs[](3);

        infos[0] = Info(address(this), 0);

        AssetAmount memory wamt = AssetAmount(
            false,
            AssetDenomination.Wei,
            AssetReference.Delta,
            amount
        );
        ActionArgs memory withdraw;
        withdraw.actionType = ActionType.Withdraw;
        withdraw.accountId = 0;
        withdraw.amount = wamt;
        withdraw.primaryMarketId = tokenToMarketId(token);
        withdraw.otherAddress = address(this);

        args[0] = withdraw;

        ActionArgs memory call;
        call.actionType = ActionType.Call;
        call.accountId = 0;
        call.otherAddress = address(this);
        call.data = data;

        args[1] = call;

        ActionArgs memory deposit;
        AssetAmount memory damt = AssetAmount(
            true,
            AssetDenomination.Wei,
            AssetReference.Delta,
            amount + 1
        );
        deposit.actionType = ActionType.Deposit;
        deposit.accountId = 0;
        deposit.amount = damt;
        deposit.primaryMarketId = tokenToMarketId(token);
        deposit.otherAddress = address(this);

        args[2] = deposit;

        pool.operate(infos, args);
    }
}

The execution code:

contract Arbitrage is DyDxFlashLoan {
    IUniswapV2Router02 public immutable uRouter;
    IUniswapV2Router02 public immutable sRouter;

    address public owner;

    constructor(address _uRouter, address _sRouter) {
        uRouter = IUniswapV2Router02(_uRouter);
        sRouter = IUniswapV2Router02(_sRouter);
        owner = msg.sender;
    }

    function executeTrade(
        address _tokenA,
        address _tokenB,
        uint256 _flashAmount,
        bool _startOnUniswap
    ) external {
        uint balanceBefore = IERC20(_tokenA).balanceOf(address(this));
        bytes memory data = abi.encode(
            _startOnUniswap,
            _tokenA,
            _tokenB,
            _flashAmount,
            balanceBefore
        );
        flashloan(_tokenA, _flashAmount, data);
    }

    function callFunction(
        address,
        Info calldata,
        bytes calldata data
    ) external onlyPool {
        (
            bool _startOnUniswap,
            address _tokenA,
            address _tokenB,
            uint256 flashAmount,
            uint256 balanceBefore
        ) = abi.decode(data, (bool, address, address, uint256, uint256));

        uint balanceAfter = IERC20(_tokenA).balanceOf(address(this));

        require(
            balanceAfter - balanceBefore == flashAmount,
            "Didn't receive flash loan"
        );

        address[] memory tokens = new address[](2);

        tokens[0] = _tokenA;
        tokens[1] = _tokenB;

        if (_startOnUniswap) {
            swapOnUniswap(flashAmount, 0, tokens);

            tokens[0] = _tokenB;
            tokens[1] = _tokenA;

            swapOnSushiswap(
                IERC20(tokens[0]).balanceOf(address(this)),
                10,
                tokens
            );
        } else {
            swapOnSushiswap(flashAmount, 0, tokens);

            tokens[0] = _tokenB;
            tokens[1] = _tokenA;

            swapOnUniswap(
                IERC20(tokens[0]).balanceOf(address(this)),
                10,
                tokens
            );
        }
    }

    function swapOnUniswap(
        uint _amountIn,
        uint _amountOut,
        address[] memory _path
    ) internal {
        require(
            IERC20(_path[0]).approve(address(uRouter), _amountIn),
            "Uniswap failed the approval"
        );

        uint[] memory amounts = uRouter.swapExactTokensForTokens(
            _amountIn,
            _amountOut,
            _path,
            address(this),
            (block.timestamp + 1200)
        );
        require(
            amounts[1] == IERC20(_path[0]).balanceOf(address(this)),
            "Didn't receive tokens from swap"
        );
    }

    function swapOnSushiswap(
        uint _amountIn,
        uint _amountOut,
        address[] memory _path
    ) internal {
        require(
            IERC20(_path[0]).approve(address(sRouter), _amountIn),
            "Sushiswap failed the approval"
        );

        uint[] memory amounts = uRouter.swapExactTokensForTokens(
            _amountIn,
            _amountOut,
            _path,
            address(this),
            (block.timestamp + 1200)
        );

        require(
            amounts[1] == IERC20(_path[0]).balanceOf(address(this)),
            "Didn't receive tokens from swap"
        );
    } 
}

If you have any suggestion please reply it would be very helpful.

3
  • if you have your own node you can use the debug RPC to show how the contract executes in excruciating detail Commented Apr 17, 2023 at 17:42
  • Can you show where the error is outputting? What contract is emitting: "TransferHelper: TRANSFER_FROM_FAILED" Commented Apr 19, 2023 at 17:45
  • The code that causing this error is: uRouter.swapExactTokensForToken()
    – Shadq
    Commented Apr 19, 2023 at 18:33

2 Answers 2

1

First you could verify the allowance of token by checking that the contract has enough allowance to execute the swaps. You can check the sufficient of the _path[0] token for the uRouter and sRouter, then check that the token contract you are working with dont have non-standard implementations and are proper ERC20, then check when you call approve() if the corresponding approval events are emitted, you can also add event logging or double check token balances.

2
  • thank you very much, i will try it.
    – Shadq
    Commented Apr 16, 2023 at 16:42
  • I tried everything but the error still shows up
    – Shadq
    Commented Apr 20, 2023 at 13:03
0

So the error was very very dumb and i apologies for anyone that spend time trying to figure out what the problem was. The problem was that i used the uRouter to swap on Sushiswap in the swapOnSushiswap function.

Not the answer you're looking for? Browse other questions tagged or ask your own question.