2

I'm rying to swap some BNB -> Token, then swap it back, all in one solidity function. Here's my code:

interface IERC20 {
    function approve(address spender, uint256 value) external returns (bool);
}

interface IPancakeSwapRouter {
    function swapExactETHForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function swapExactTokensForETH(
        uint256 amountIn, 
        uint256 amountOutMin, 
        address[] calldata path, 
        address to, 
        uint256 deadline
    ) external returns (uint[] memory amounts);

    function WETH() external pure returns (address);
    function factory() external pure returns (address);
}

interface IPancakeFactory {
    function getPair(address tokenA, address tokenB) external view returns (address pair);
}

contract Test {

    receive() external payable { }
    fallback() external payable { }

    function BuySell(address token_out, uint value) public payable {

        address swap_router_addr = address(0x10ED43C718714eb63d5aA57B78B54704E256024E); //Pancake router
        IPancakeSwapRouter swap = IPancakeSwapRouter(swap_router_addr);

        uint deadline = block.timestamp + 1200;

        address[] memory path_buy = new address[](2);
        path_buy[0] = swap.WETH();
        path_buy[1] = token_out;

        // 1. try buy 0.001
        uint256[] memory amounts;

        amounts = swap.swapExactETHForTokens { value: msg.value } (
            0, path_buy, address(this), deadline
        );

        // 2. try to sell 
        // approve first
        IERC20 token_dest = IERC20(token_out);
        address pair = IPancakeFactory(swap.factory()).getPair(swap.WETH(), token_out);
        token_dest.approve(pair, amounts[1]); // approve WBNB/Token pair
        token_dest.approve(swap_router_addr, amounts[1]); // approve PancakeSwap Router

        // sell
        address[] memory path_sell = new address[](2);
        path_sell[0] = path_buy[1];
        path_sell[1] = path_buy[0];

        swap.swapExactTokensForETH(
            amounts[1], 0, path_sell, address(this), deadline);
    }
}

I can confirm the error occurs at the last line swapExactTokensForETH, it says "TRANSFER_FROM_FAILED", I guess it's about approve(), but I already approved the token for PancakeRoute before the swap.

Some failed tx sample: https://bscscan.com/tx/0x3feaa9c746ee4458d073a0311b90fb8658d45ed7bac4877bc2934f7e05c4c538

Why the error?

2 Answers 2

4

The issue is that you need to approve the pair and not the router. Looking at the transaction you linked you can see that it revert on the transfer from 0xa89C6f52Ce3Ce1BFF6CAac1E1c98b4f9b3AA5f63 (your contract) to 0x4a1a7f263b49dA21fBeD0907317233AA06AdF0dF (PancakePair). Therefore this is also the account that you need to approve.

The address of the PancakePair contract can be loaded from the PancakeFactory by calling getPair(tokenA, tokenB). The factory address can be loaded from the router via factory().

Edit:

The token that you are using enforces a transfer fee. For this you cannot use swapExactTokensForETH, you need to use swapExactTokensForTokensSupportingFeeOnTransferTokens.

9
  • I tried approve both Router and the Pair, but same error, the code updated. BTW how do you know it reverted when transferring token to 0x4a1a7f263b49dA21fBeD0907317233AA06AdF0dF(PancakePair) ? I don't see this in the page, from the "Internal Txns" there are only two lines, and it's transferring BNB from my contract to PancakeRouter. The failed sample tx: bscscan.com/tx/…
    – aj3423
    Jan 7 at 9:25
  • 1
    You can check the trace on bscscan. (the three dots in the top right and select "Geth Debug Trace_2")
    – Richard
    Jan 7 at 12:43
  • 1
    MSTART is a token that enforces a fee on transfer, these need to be handled differently. You need to call swapExactTokensForTokensSupportingFeeOnTransferTokens.
    – Richard
    Jan 7 at 12:55
  • 1
    Yeah, that's what I mean with "it might not be that simple". You cannot transfer the full amount, but I would have to look into the details of the MSTART contract to figure out how to best calculate how much you can send. In their contract they have a _getRate that they use for this, but this is all very specific to their token, so I don't know the details.
    – Richard
    Jan 7 at 17:02
  • 1
    Finally I worked it out. If a token uses fee and you buy it with amounts = swapExactETHForTokens(), the amounts[1] is LESS than what you expected. For example, the MSTART token takes fee at 4%, if the amounts[1] returns 100, the actual balanceOf() is only 96. So when you try to sell 100 MSTART, it shows error "TRANSFER_FROM_FAILED". I guess this is why swapExactETHForTokensSupportingFeeOnTransferTokens not returning amounts. My solution is use balanceOf() to get the token actually bought.
    – aj3423
    Jan 7 at 18:58
0

Your code probably has design errors and needs some changes.

Exchanging native BNB for tokens directly is not possible. You need to call the WBNB contract using withdrawal and deposit for that.

After that exchanges are in WBNB and tokens.

If the token already has a liquidity pool, just call the exchange function directly in the pancake router V2 contract.

On the other hand, if your token has fees you should call the fee-supported exchange function on the transfer: https://docs.uniswap.org/protocol/V2/reference/smart-contracts/router-02

Remember to give the proper approvals on the token and WBNB contracts.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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