Search code examples
javascriptethereum

I am trying to execute a swap on Pancakeswap using web3, but I get an error when calculating transaction cost


I am new at ethereum development and I am working on a simple script to execute swaps using Pancakeswap. Everything seems to be working well up to the point where I am building my transaction and calculate the tx-cost. I am using the UniswapRouterV02 abi create my Pancakeswap contract. The code:

const init = async () => {

    const [WBNB, BUSD] = await Promise.all(
        [addresses.WBNB, addresses.BUSD].map(tokenAddress => (
            new Token(
                ChainId.MAINNET,
                tokenAddress,
                18
            )
        )));


    const pair = await Fetcher.fetchPairData(WBNB, BUSD, provider)
    const route = await new Route([pair], WBNB)
    const trade = await new Trade(route, new TokenAmount(WBNB, tradeAmount), TradeType.EXACT_INPUT)
    const executionPrice = trade.executionPrice.toSignificant(12)

    // Correct prices; everything seems correct up until here

    const slippageTolerance = new Percent('50', '10000')

    const amountOutMin = trade.minimumAmountOut(slippageTolerance).raw
    const path = [WBNB.address, BUSD.address]
    const to = MY_ADDRESS
    const deadline = Math.floor(Date.now() / 1000) + 60 * 20
    const value = trade.inputAmount.raw

    // Correct prices everything seems correct up until here

    const pancakeSwap = new web3.eth.Contract(
        abis.uniswapRouter.abi,
        addresses.PANCAKE_ROUTER //'0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'
    );

    let tx = pancakeSwap.methods.swapExactTokensForTokens(
        tradeAmount,
        web3.utils.toBN(amountOutMin.toString()),
        path,
        to,
        deadline
    )

    const [gasPrice, gasCost] = await Promise.all([
        web3.eth.getGasPrice(),
        tx.estimateGas({from: admin}),
    ]);


    console.log(`gasPrice: ${gasPrice}`)
    console.log(`gasCost: ${gasCost}`)
}

init()

The price calculation for the swap returns correct prices. However when I try to calculate the transaction costs the following error is thrown: Error: Returned error: gas required exceeds allowance (44038122) or always failing transaction

Any help would be greatly appreciated, please let me know if more of my code should be clarified!


Solution

  • In turns out that with web3 it is not possible to interact with the Pancakeswap contract. I found a solution using ethers.js. Following code worked for me to execute a swap on Pancake on mainnet. Before executing the first transaction Pancakeswap needs to be allowed, this code is commented out. I had to play around with the gasprice and gasLimit a bit to make it work.

    require("dotenv").config()
    const ethers = require('ethers')
    const {ChainId, Token, TokenAmount, Fetcher, Pair, Route, Trade, TradeType, Percent} = 
    require('@pancakeswap-libs/sdk');
    const Web3 = require('web3');
    const web3 = new Web3('wss://apis.ankr.com/wss/c40792ffe3514537be9fb4109b32d257/946dd909d324e5a6caa2b72ba75c5799/binance/full/main');
    const {JsonRpcProvider} = require("@ethersproject/providers");
    const provider = new JsonRpcProvider('https://bsc-dataseed1.binance.org/');
    const { address: admin } = web3.eth.accounts.wallet.add(process.env.PRIVATE_KEY)
    
    const addresses = {
        WBNB: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
        BUSD: '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56',
        PANCAKE_ROUTER: '0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'
    }
    
    const ONE_ETH_IN_WEI = web3.utils.toBN(web3.utils.toWei('1'))
    const tradeAmount = ONE_ETH_IN_WEI.div(web3.utils.toBN('1000'))
    
    const init = async () => {
    
    const [WBNB, BUSD] = await Promise.all(
        [addresses.WBNB, addresses.BUSD].map(tokenAddress => (
            new Token(
                ChainId.MAINNET,
                tokenAddress,
                18
            )
        )));
    
    const pair = await Fetcher.fetchPairData(WBNB, BUSD, provider)
    const route = await new Route([pair], WBNB)
    const trade = await new Trade(route, new TokenAmount(WBNB, tradeAmount), TradeType.EXACT_INPUT)
    
    const slippageTolerance = new Percent('50', '10000')
    
    // create transaction parameters
    const amountOutMin = trade.minimumAmountOut(slippageTolerance).raw
    const path = [WBNB.address, BUSD.address]
    const to = admin
    const deadline = Math.floor(Date.now() / 1000) + 60 * 20
    
    // Create signer
    const wallet = new ethers.Wallet(
        Buffer.from(
        process.env.PRIVATE_KEY, // paste your private key from metamask here
        "hex"
        )
    )
    const signer = wallet.connect(provider)
    
    // Create Pancakeswap ethers Contract
    const pancakeswap = new ethers.Contract(
        addresses.PANCAKE_ROUTER,
        ['function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)'],
        signer
    )
    
    // Allow Pancakeswap
    // let abi = ["function approve(address _spender, uint256 _value) public returns (bool success)"]
    // let contract = new ethers.Contract(WBNB.address, abi, signer)
    // await contract.approve(addresses.PANCAKE_ROUTER, ethers.utils.parseUnits('1000.0', 18), {gasLimit: 100000, gasPrice: 5e9})
    
    // Execute transaction
    const tx = await pancakeswap.swapExactTokensForTokens(
        ethers.utils.parseUnits('0.001', 18),
        ethers.utils.parseUnits(web3.utils.fromWei(amountOutMin.toString()), 18),
        path,
        to,
        deadline,
        { gasLimit: ethers.utils.hexlify(200000), gasPrice: ethers.utils.parseUnits("10", "gwei") }
    )
    
    console.log(`Tx-hash: ${tx.hash}`)
    
    const receipt = await tx.wait();
    
    console.log(`Tx was mined in block: ${receipt.blockNumber}`)
    }
    
    init()