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
    – user253751
    Apr 17 at 17:42
  • Can you show where the error is outputting? What contract is emitting: "TransferHelper: TRANSFER_FROM_FAILED" Apr 19 at 17:45
  • The code that causing this error is: uRouter.swapExactTokensForToken()
    – Shadq
    Apr 19 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
    Apr 16 at 16:42
  • I tried everything but the error still shows up
    – Shadq
    Apr 20 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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