v2-periphery icon indicating copy to clipboard operation
v2-periphery copied to clipboard

Can't call UniswapRouterV2.swapExactTokensForTokens from another contract

Open santiac89 opened this issue 3 years ago • 2 comments

I'm trying to integrate Uniswap into my own contract, I have given the UniswapRouterV2 contract allowance to transfer tokens on my behalf but still I'm getting TRANSFER_FROM_FAILED. I have been testing in Ropsten. The weird stuff is that calling the router contract directly (from Etherscan for example) with the exact same parameters does not fail at all and correctly swaps the token and deposits the target token in the target account.

I'm posting here my contract code, is a really simple Test contract.

pragma solidity ^0.8.1;
pragma experimental ABIEncoderV2;

import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";

contract PaymentGateway {
    address internal constant UNISWAP_ROUTER_ADDRESS = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    address internal constant UNISWAP_FACTORY_ADDRESS = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;

    IUniswapV2Router02 public uniswapRouter;
    IUniswapV2Factory public uniswapFactory;


    constructor() {
        uniswapRouter = IUniswapV2Router02(UNISWAP_ROUTER_ADDRESS);
        uniswapFactory = IUniswapV2Factory(UNISWAP_FACTORY_ADDRESS);
    }

    function swapTokensForExactTokens(
        uint _amount,
        uint  _min,
        address _sourceToken,
        address _targetToken,
        address _recipient,
        uint  _deadline
    ) external returns (uint[] memory) {
        address[] memory path = new address[](2);
        path[0] = _sourceToken;
        path[1] = _targetToken;

        uint[] memory amounts = uniswapRouter.swapTokensForExactTokens(
            _amount,
            _min,
            path,
            _recipient,
            _deadline
        );

        return amounts;
    }

    function swapExactTokensForTokens(
        uint _amount,
        uint _min,
        address _sourceToken,
        address _targetToken,
        address _recipient,
        uint _deadline
    ) external returns (uint[] memory) {
        address[] memory path = new address[](2);
        path[0] = _sourceToken;
        path[1] = _targetToken;

        uint[] memory amounts = uniswapRouter.swapExactTokensForTokens(
            _amount,
            _min,
            path,
            _recipient, 
            _deadline
        );

        return amounts;
    }
}

Has anyone experienced something similar?

Thanks in advance

santiac89 avatar Mar 23 '21 21:03 santiac89

Same issue and trying to get help for weeks, the support and documentation is very poor. Here is my relevant code:


    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual override lock(_isTransferLocked) notNull(amount) {
        require(
            _balances[sender] >= amount,
            "ERC20: transfer amount exceeds balance"
        );
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        require(
            sender != recipient,
            "_transfer: 'sender' cannot also be 'recipient'"
        );

        if (!_isExcludedFromFees[sender] && !_isExcludedFromFees[recipient]) {
            uint256 updatedAmount = _beforeTokenTransfer(sender, amount);
            amount = updatedAmount;
        }

        _balances[sender] -= amount;
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);
    }

    function _beforeTokenTransfer(address sender, uint256 amount)
        internal
        returns (uint256)
    {
        uint256 updatedAmount = amount;

        if (_liquidityTax != 0) {
            uint256 liquidityFee = (amount * _liquidityTax) / 100;

            _balances[sender] -= liquidityFee;
            _balances[address(this)] += liquidityFee;

            emit Transfer(sender, address(this), liquidityFee);

            if (
                _balances[address(this)] >= _minTokensRequiredBeforeSwap &&
                _isAutoSwapAndLiquify
                // && sender != _uniswapV2Pair // not sure why this is needed... 🤔
            ) {
                _swap(_balances[address(this)] / 2);
                // uint256 ethReceived = _swap(_balances[address(this)] / 2);
                // (uint256 amountETH, uint256 amountToken) =
                //     _liquify(halfContractBalance, ethReceived);
                // _totalLiquidityETH += amountETH;
                // _totalLiquidity += amountToken;
            }

            updatedAmount -= liquidityFee;
        }

        return updatedAmount;
    }

    /*
     * (Uni|Pancake)Swap functions
     */
    // Required to recieve ETH from uniswapV2Router on swaps
    receive() external payable {}

    function _swap(uint256 amountIn) internal returns (uint256) {
        // contract's current BNB/ETH balance
        uint256 initialBalance = address(this).balance;

        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = _uniswapV2Router.WETH();

        _approve(address(this), address(_uniswapV2Router), type(uint256).max);

        // Swap tokens for ETH/BNB
        _uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            amountIn,
            0, // TODO: use an Oracle
            path,
            address(this), // this contract will receive the ETH that were swapped from the token
            block.timestamp
        );

        return address(this).balance - initialBalance;
    }

When commenting:

                _swap(_balances[address(this)] / 2);

Everything works, when not commenting this happens:

image

Could anyone from the UniSwap team take time to look at this and add documentation to their website because so far as I said it is pretty poor!

aress31 avatar May 19 '21 00:05 aress31

@santiac89 @aress31 did either of you find a way to resolve this issue? I'm trying to call swapExactTokensForTokens from a contract, and I'm getting the same error 'TransferHelper: TRANSFER_FROM_FAILED'.

stuartwk avatar Jul 07 '22 05:07 stuartwk