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

TransferHelper: TRANSFER_FROM_FAILED

Open aress31 opened this issue 3 years ago • 11 comments

image

react_devtools_backend.js:2557 Error: cannot estimate gas; transaction may fail or may require manual gas limit (error={"code":-32603,"message":"execution reverted: TransferHelper: TRANSFER_FROM_FAILED","data":{"originalError":{"code":3,"data":"0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000245472616e7366657248656c7065723a205452414e534645525f46524f4d5f4641494c454400000000000000000000000000000000000000000000000000000000","message":"execution reverted: TransferHelper: TRANSFER_FROM_FAILED"}}}, method="estimateGas", transaction={"from":"0x7F87bBc2Eb7911c8961c8506032d31d624894725","to":"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D","value":{"type":"BigNumber","hex":"0x0de0b6b3a7640000"},"data":"0xf305d719000000000000000000000000c936c5a29f55006f2b1c84480bf2d77286e5944300000000000000000000000000000000000027716b6a0adc2d677c080000000000000000000000000000000000000000000027716b6a0adc2d677c08000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000007f87bbc2eb7911c8961c8506032d31d6248947250000000000000000000000000000000000000000000000000000000060a5149c","accessList":null}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.1.1)
    at e.value (index.ts:205)
    at e.value (index.ts:217)
    at E (json-rpc-provider.ts:76)
    at n.<anonymous> (json-rpc-provider.ts:508)
    at c (runtime.js:63)
    at Generator._invoke (runtime.js:293)
    at Generator.throw (runtime.js:118)
    at s (4.1d476024.chunk.js:2)

Help?

aress31 avatar May 19 '21 13:05 aress31

Thanks aress31, justin is working on this in https://github.com/Uniswap/uniswap-interface/issues/1582

willhennessy avatar May 20 '21 05:05 willhennessy

@aress31 does the token take fee on transfer or anything special?

justindomingue avatar May 20 '21 21:05 justindomingue

Also, can you give the address of that token on mainnet?

justindomingue avatar May 20 '21 21:05 justindomingue

@justindomingue, yes it does indeed take a fee on transaction, to be more precise a liquidity fee.

  • Contract address: 0xA22E26fdD6c982f884fBF70C18FB9bc292025cA7
  • Testnet: Ropsten
  • Relevant code:
function _transfer(
        address sender,
        address recipient,
        uint256 amount
    )
        internal
        virtual
        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
            ) {
                uint256 ethReceived = _swap(_balances[address(this)] / 2);
            }

            updatedAmount -= liquidityFee;
        }

        return updatedAmount;
    }

receive() external payable {
        emit Received(_msgSender(), msg.value);
    }

    function _swap(uint256 amountIn) internal returns (uint256) {
        uint256 initialBalance = address(this).balance;

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

        // Change back to address(this)
        _approve(address(this), address(_uniswapV2Router), amountIn);

        // uint256 allowed = _allowances[address(this)][address(_uniswapV2Router)];

        // emit Test(amountIn, path, allowed, (block.timestamp + 5 minutes)); <- amountIn  is OK and allowance is OK

        // The problem is here
        // 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 + 5 minutes)
        );

        // Check: https://github.com/Uniswap/uniswap-v2-periphery/issues/92
        return address(this).balance - initialBalance;
    }

aress31 avatar May 20 '21 21:05 aress31

What slippage are you using? Is your token taking 50% before transfer? Maybe try setting to 80% slippage for debugging?

justindomingue avatar May 20 '21 22:05 justindomingue

The liquidity fee is of 5% only that is definitely not a slippage issue, if I comment:

      _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 + 5 minutes)
        );

Then everything works but obviously I want my contract to swap tokens. No one so far has been able to help me with this issue, I do not see anything wrong with this code, if you do PLEASE point out the bug.

aress31 avatar May 20 '21 22:05 aress31

Transferred to uniswap-v2-periphery which should have better context to help you @aress31.

justindomingue avatar May 20 '21 22:05 justindomingue

Thanks @justindomingue also just realised I mixed up two different issues for the adding liquidity bug, I can confirm that your latest fix resolved the issue!

This second issue remains however still open and happens when swapping a token on the front-end, my contract fails due to:

      _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 + 5 minutes)
        );

aress31 avatar May 20 '21 22:05 aress31

Hello, what was the solution for your bug @aress31 please ? we are in the same situation… thank you for your answer

ihannad avatar Dec 03 '21 22:12 ihannad

Hello, what was the solution for your bug @aress31 please ? we are in the same situation… thank you for your answer

Below is the solution to this famous error:

https://medium.com/@italo.honorato/how-to-resolve-transferhelper-error-transfer-from-failed-fb4c8bf6488c

Basically the error happens because it is not possible to approve a permission for the UNISWAP_V2_ROUTER to spend tokens from the ContractSwapOnUniswap address, why by default the ERC20 approve function will not recognize ContractSwapOnUniswap as msg.sender . That is, the TransferHelper: TRANSFER_FROM_FAILED error occurs because the UNISWAP_V2_ROUTER does not have the necessary permission in the token contract to spend balance of ContractSwapOnUniswap .

It concludes that this is a design error of the ERC20 standard itself applied to contracts that make exchanges from other accounts. If I'm wrong, please correct me.

Does anyone know how to guide me on how to open a discussion on the subject, for possible improvements in this?

italoHonoratoSA avatar May 02 '22 21:05 italoHonoratoSA

Pretty sure this error is caused by the swap and liquify feature that you seem to be using. Had same exact issue, and I know how to handle token approval which the dude above is mentioning, for eg. in newer Openzeppelin ERC20 standard you can approve infinite amount by approving "type(uint).max" amount in the constructor of the code, and allowance never gets reduced. Tried running swap and liquify separately and it worked, tried swapping with S&L turned off and swaps worked, same thing with regular token transfers, they worked with S&L. However Uni doesn't seem to support swaps and S&L in the same call

Tr0ublesh00ter avatar Jan 23 '23 18:01 Tr0ublesh00ter