Search code examples
ethereumsoliditysmartcontractsuniswap

How to fix 'TransferHelper: ETH_TRANSFER_FAILED' when interacting with Uni V2


I'm dealing with a strange issue with the safeTransferETH helper function in Uniswap V2's router contract.

I'm trying to swap tokens held by the contract to Uniswap for Ether, using the swapExactTokensForETH function provided by the Uniswap V2 router. (The function code is present on Uniswap's github in router1). The function being called is:

function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        override
        ensure(deadline)
        returns (uint[] memory amounts)
    {
        require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
        require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
        TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]);
        _swap(amounts, path, address(this));
        IWETH(WETH).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
    }

The only part of this code that is throwing an error is the TransferHelper.safeTransferETH function, which is:

function safeTransferETH(address to, uint value) internal {
    (bool success,) = to.call{value:value}(new bytes(0));
    require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
}

My code is:

function uniV2ReceiveETH(address _token0, uint _amount0) public payable returns (uint[] memory amountsReceived) {
        require(_amount0 > 0, "Must provide tokens if we want tokens in return!");
        address[] memory path = new address[](2);
        path[0] = _token0;
        path[1] = WETH;

        IERC20 token;
        token = IERC20(_token0);

        if (token.balanceOf(address(this)) > 0) {
            _amount0 = token.balanceOf(address(this));
        }

        require(token.approve(address(uniV2Router), _amount0 + 10000), "approval failed");

        // Swap logic
        uint amountOutMin = UniswapV2Library.getAmountsOut(address(uniV2Factory), _amount0, path)[1];
        amountsReceived = uniV2Router.swapExactTokensForETH(_amount0, amountOutMin, path, address(this), deadline);
        uint endBalance = address(this).balance;

        // Let everyone know we're done!
        emit Swap(msg.sender, _amount0, endBalance);
}

A few other notes are:

  1. The contract does receive ETH from other addresses without issue.
  2. I am using hardhat, and a forked version of the mainnet to test.
  3. The contract also works with the Uniswap router's other swap functions, including SwapExactETHForTokens and SwapExactTokensForTokens.

Solution

  • I solved this issue by having a payable fallback function by defining it as:

    fallback() external payable { }
    

    inside my smart contract.