4

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.
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • why on the line when you call swapExactTokensForETH you write amountOutMin fixed to 1? you calculated it just before, use it. it can give error if the actual amountOut is less than 1 – Jacopo Mosconi Dec 21 '21 at 22:49
  • Apologies, it is supposed to be AmountOutMin, I was just testing to see if that had anything to do with it. I've updated it in the provided code. – computersAreHard Dec 22 '21 at 06:29
  • By the way, it's probably easier to get answers to questions like these on [ethereum.se]. – CherryDT Feb 19 '22 at 19:56

3 Answers3

9

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

fallback() external payable { }

inside my smart contract.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Neucleophile
  • 106
  • 2
0

Add to your contract.

receive() external payable {}
fallback() external payable {}
Richard
  • 325
  • 7
  • 23
0

I solved this issue by having a payable receive() function because the contract isn't be able to receive ether without this function:

receive() external payable {}

"If you are using Solidity 0.6.0 or later, it is recommended to use the receive() function to explicitly handle plain Ether transfers. If you are working with an older version of Solidity, you can use the fallback() function to handle both Ether transfers and calls with data."

fallback() external payable {}