0

I have a simple code using web3.py that sells BEP20 tokens:

deadline = int(time.time()) + 1200;
nonce = w3.eth.get_transaction_count(w3.eth.default_account) 
sellTxn = self.web3Context.routerContract.functions.swapExactTokensForETHSupportingFeeOnTransferTokens(amountToSell, 1000000000000000, 
           [self.tokenAddress, self.web3Context.wrappedEthAddress], 
            account.address, deadline).buildTransaction({
              'chainId': self.web3Context.chainId,
              'nonce': nonce,
              'gas': w3.toHex(500000),
              'gasPrice': w3.toHex(int(w3.eth.gasPrice*2)),
         })

signed_sellTxn = account.signTransaction(sellTxn)
sell_txid = w3.toHex(self.web3Context.w3.eth.sendRawTransaction(signed_sellTxn.rawTransaction))
sellReceipt = w3.eth.wait_for_transaction_receipt(sell_txid)
...

It's all uses pancakeswap V2 stuff, like router address, WBNB address, etc.

It does work with majority of tokens I tried it with. However, occasionally it fails with

Fail with error 'TransferHelper: TRANSFER_FROM_FAILED'

The token is approved before hand. This code is not called if a token is not approved, and I also checked that the transaction to approve it was confirmed before the above code was executed.

If it happens with a particular token, it will be failing no matter how many times I try to run it. However, if I sell manually using Pancakeswap, it works.

I compared the data I send with the data Pancakeswap sends, and I do not see any difference except in amountOutMin. When it works, however, I can even pass 0.

So, here is an example when the transaction fails:

Function: swapExactTokensForETHSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline)
0   amountIn        uint256     1001887138366157231924
1   amountOutMin    uint256     1000000000000000
2   path            address[]   ef155b9bcdbf4bd54143cf2ec7e47045cbbd2ebb
                                bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c
3   to              address     3c94454135e91ab01bb4c1b816d4d6f9399b9a7d

And this is the transaction when it's submitted from Pancakeswap, and it works:

Function: swapExactTokensForETHSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline)
0   amountIn        uint256     1004644755416763858179
1   amountOutMin    uint256     24525898036735865
2   path            address[]   ef155b9bcdbf4bd54143cf2ec7e47045cbbd2ebb
                                bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c
3   to              address     3c94454135e91ab01bb4c1b816d4d6f9399b9a7d

So, the only difference is the amountOutMin value.

What is the best way to troubleshoot or debug this? Is there anything specific to look for in the token's contract?

So far I've seen it with handful of tokens: https://bscscan.com/address/0x0952ddffde60786497c7ced1f49b4a14cf527f76 https://bscscan.com/address/0x35ca09d3c28c24f1f32742ab91d794d2a524519e https://bscscan.com/address/0xd55eb92c40b8e19f5ff97cbbbdb721889a7b034a https://bscscan.com/address/0xd9f21eb27474981f419a0845ddfccc14e3fd4ecc https://bscscan.com/address/0xe68efedf3e1c08af12de6bdec585f89794a75c52 https://bscscan.com/address/0xef155b9bcdbf4bd54143cf2ec7e47045cbbd2ebb

Thanks!

5
  • Try with more gas, better use estimateGas to calculate gas needed.
    – Ismael
    Jun 15, 2021 at 3:32
  • I do not think it's an issue with gas. When it fails, I can always sell manually via pancakeswap using was less gas. But it's a good suggestion. I will check the transactions that succeeded around the same time to see how much gas they used.
    – Peter L
    Jun 16, 2021 at 4:07
  • Have the exact same issue. Tried with using estimateGas to set the gas price as suggested above and still get the TRANSFER_FROM_FAILED error.
    – Bojorón
    Jun 16, 2021 at 21:53
  • Having the same issue tried hardcoding in the same exact amountIn and amountOutMin of a successful transaction which made me avoid the TRANSFER_FROM_FAILED but got Failed which is even more pricy on gas... Did you manage to find the solution or has anyone? Unfortunately, we need this method to trade tokens that have a transaction fee for example SafeMoon takes 10% of every buy and sell order so a normal method wouldn't work. If you have found any solutions would be nice to know!
    – Jesus
    Jul 5, 2021 at 21:52
  • No, I did not find a solution yet. I'm using double the gas price usually, so it's possible that some tokens can't be sold if you use higher gas price. I know that there are some tokens that you can't buy unless your gas price is 5. However, this is just a guess.
    – Peter L
    Jul 6, 2021 at 22:40

2 Answers 2

0

I had the same issue, the only solutions that I could think of are:

  1. Get the price of the token and calculate the slippage on the fly
  2. Set amountOutMin to 0, at first I thought it would give me 0 bnb but on all the tokens that I have used it, it only took the tax specified on the contract.
    token_tx = pancake.functions.swapExactTokensForETHSupportingFeeOnTransferTokens(tokens_amount,0 , path, reciever[0],expire ).buildTransaction(
        {
            'chainId':56,
            'gas': 500000,
            'gasPrice': web3.toWei(gas,'gwei'),
            'nonce':nonce,
        }
    )

My guess is that when you try to sell and the tokens received < amountOutMin it fails

0

I reported the problem in the following article: https://medium.com/@italo.honorato/how-to-resolve-transferhelper-error-transfer-from-failed-fb4c8bf6488c

In it I put the solution of the case.


I went through this problem. The error only happens when selling tokens through a smart swap contract using the above function. If you interact directly through the Uniswap V2 router contract on Etherscan you will have no problem as msg.sender is your external account.

For some reason the error doesn't seem to happen with other functions like swapExactTokensForTokens in fee-free contracts.

Remember to first provide approvals for the exchange agreement to use WETH tokens and your token 2. This must be done before executing exchanges.

The exchange function code is as follows:


contract ContractSwapOnUniswap{

   function swap(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _amountOutMin, address _to) external {
      
    IERC20(_tokenIn).transferFrom(msg.sender, address(this), _amountIn);
    
    //next we need to allow the uniswapv2 router to spend the token we just sent to this contract
    //by calling IERC20 approve you allow the uniswap contract to spend the tokens in this contract
    IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);

    address[] memory path = new address[](2);
    path[0] = _tokenIn;
    path[1] = _tokenOut;

        IUniswapV2Router(UNISWAP_V2_ROUTER).swapExactTokensForTokens(_amountIn, _amountOutMin, path, _to, block.timestamp);
    }
}

Read the comments for the approve function of the contract above.

Below is the approve function of the ERC20 standard:

    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;

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 is not possible for ContractSwapOnUniswap to be msg.sender. The msg.sender is always the account that calls the contract function and sends the transaction.

I concluded that this is a design error of the ERC20 standard itself applied to contracts that swap other accounts. If I'm wrong, please correct me.

That is, swapExactTokensForTokensSupportingFeeOnTransferTokens apparently doesn't work very well if used in this way for both buying and selling.

The problem here is mixing transferFrom with approve in the same function logic.

The solution I found for this is the following code:


   function buy(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _amountOutMin) public {
      
    IERC20(_tokenIn).transferFrom(msg.sender, address(this), _amountIn);
    
    IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);
  
    address[] memory path = new address[](2);
    path[0] = _tokenIn;
    path[1] = _tokenOut;

        IUniswapV2Router02(UNISWAP_V2_ROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(_amountIn, _amountOutMin, path, address(this), block.timestamp);
    }


    
   function sell(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _amountOutMin) public {
      
    //No `transferFrom` statement needed here
    //Sell only works if this line below exists
    //approve below is giving permission for UNISWAP_V2_ROUTER to spend msg.sender tokens
    //But who has the tokens to be spent is addres(this)
    //Looks like the EVM still needs `msg.sender` to be approved
    //Precisely because the transaction involves the manipulation of the token, even if it is not spent by `msg.sender`
    IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);

   
    address[] memory path = new address[](2);
    path[0] = _tokenIn;
    path[1] = _tokenOut;

        IUniswapV2Router02(UNISWAP_V2_ROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(_amountIn, _amountOutMin, path, msg.sender, block.timestamp);
    }

Remember that it is recommended to insert a function to withdraw ERC20 or ETH from your contract, as without this there is a risk of losing them forever within the contract.

strong text

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