Smart contract fails with “Cannot estiamte gas”

Hello all,

I wrote a smart contract, that can borrow a flashloan on balancer, make trades and repay the loan. If I run it on hardhat it works just fine, but if I deploy to the mainnet, I cannot even estimate gas for calling the function. It fails every time. So this is my smart contract:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IFlashLoanRecipient.sol";
import "./IBalancerVault.sol";
import "./interfaces/IUniswapV2Router02.sol";

contract FlashLoanArbitrage {
    address payable private owner;
    address private vault;
    address private sushiRouter;
    address private quickRouter;
    address private apeRouter;
    address public matic = 0x0000000000000000000000000000000000001010;

    constructor(
        address _vault,
        address _sushiRouter,
        address _quickRouter,
        address _apeRouter
    ) {
        owner = payable(msg.sender);
        sushiRouter = _sushiRouter;
        quickRouter = _quickRouter;
        apeRouter = _apeRouter;
        vault = _vault;
    }

    event FlashLoanResult(
        address indexed token,
        uint256 amountBorrowed,
        uint256 amountProfit,
        uint256 timestamp
    );

    modifier onlyOwner() {
        require(msg.sender == owner, "Only Owner can call this function!");
        _;
    }

    receive() external payable {}

    function withdraw(IERC20 token) private {
        //If contract needs to be funded, then compare here, that everytime there is enough matic here
        uint256 amount = 0;
        require(address(token) != address(0), "Invalid token address");
        if (matic == address(token)) {
            amount = address(this).balance;
            require(amount > 0, "Not enough MATIC token to Withdraw");
            owner.transfer(amount);
        } else {
            amount = token.balanceOf(address(this));
            require(amount > 0, "Not enough token to Withdraw");
            token.transfer(owner, amount);
        }
    }

    function withdrawOnlyOwner(address token) external payable onlyOwner {
        uint256 amount = 0;
        require(address(token) != address(0), "Invalid token address");
        if (matic == token) {
            amount = address(this).balance;
            require(amount > 0, "Not enough MATIC token to WithdrawOnlyOwner");
            payable(msg.sender).transfer(amount);
        } else {
            IERC20 token_transfer = IERC20(token);
            amount = token_transfer.balanceOf(address(this));
            require(amount > 0, "Not enough token to WithdrawOnlyOwner");
            token_transfer.transfer(payable(msg.sender), amount);
        }
    }

    function changeAddresses(
        address _quickRouter,
        address _sushiRouter,
        address _apeRouter,
        address _vault
    ) external onlyOwner {
        sushiRouter = _sushiRouter;
        quickRouter = _quickRouter;
        apeRouter = _apeRouter;
        vault = _vault;
    }

    function balance(address token_address) public view returns (uint256) {
        uint256 Balance = IERC20(token_address).balanceOf(address(this));
        return Balance;
    }

    function swapper(
        address dex_router,
        uint256 _amountIn,
        uint256 _amountOutMin,
        address _tokenIn,
        address _tokenOut,
        uint256 _deadline
    ) private returns (uint256) {
        //Zum testen muss das womöglich auf public gesetzt werden
        require(
            IERC20(_tokenIn).approve(dex_router, _amountIn),
            "Approve for Swapping failed."
        );
        require(
            _amountIn <= balance(_tokenIn),
            "Error: Actual amount received is less than expected"
        ); // check if actual amount is less than expected
        address[] memory path;
        path = new address[](2);
        path[0] = _tokenIn;
        path[1] = _tokenOut;
        uint256 deadline = block.timestamp + _deadline;
        uint256[] memory amounts = IUniswapV2Router02(dex_router)
            .swapExactTokensForTokens(
                _amountIn,
                _amountOutMin,
                path,
                address(this),
                deadline
            );
        require(amounts[1] > 0, "Output amount should be greater than 0");
        return amounts[1];
    }

    function receiveFlashLoan(
        IERC20[] memory tokens,
        uint256[] memory amounts,
        uint256[] memory feeAmounts,
        bytes memory data
    ) external {
        address dexA;
        address dexB;
        address[] memory trade_tokens;
        uint24 fees;
        uint256[] memory outputs;
        (dexA, dexB, trade_tokens, fees, outputs) = abi.decode(
            data,
            (address, address, address[], uint24, uint256[])
        );
        for (uint256 i = 0; i < tokens.length; ++i) {
            IERC20 token = tokens[i];
            uint256 amount = amounts[i] + feeAmounts[i];
            uint256 amountOut0 = 0;
            uint256 amountOut1 = 0;

            amountOut0 = swapper(
                dexA,
                outputs[0],
                outputs[1],
                trade_tokens[0],
                trade_tokens[1],
                3000
            );
            amountOut1 = swapper(
                dexB,
                balance(trade_tokens[1]),
                0,
                trade_tokens[1],
                trade_tokens[0],
                3000
            );

            emit FlashLoanResult(
                trade_tokens[0],
                amounts[i],
                balance(trade_tokens[1]),
                (block.timestamp + 3000)
            );

            token.transfer(vault, amount);
            withdraw(tokens[i]);
        }
    }

    function flashLoan(
        IERC20[] memory tokens,
        uint256[] memory amounts,
        bytes memory userData
    ) external onlyOwner {
        IBalancerVault(vault).flashLoan(
            IFlashLoanRecipient(address(this)),
            tokens,
            amounts,
            userData
        );
    }
}

and that is how I am estimating the gas I need:

require("dotenv").config();
const { ethers } = require("ethers");
const flashloanABI = require("./abis/flashloan_abi.json");
const abiCoder = ethers.utils.defaultAbiCoder;
const FLASHLOAN = process.env.CONTRACT_ADDRESS;
const POLYGON_MAINNET_QUICKNODE= process.env.POLYGON_MAINNET_QUICKNODE;
const provider = new ethers.providers.JsonRpcProvider(
  POLYGON_MAINNET_QUICKNODE
);
const flash_contract = new ethers.Contract(FLASHLOAN, flashloanABI, provider);
async function estimateGas(amountIn_wei, tokenIn_address, data) {
  console.log("We are calculating");
  try {
    const functionData = flash_contract.interface.encodeFunctionData(
      "flashLoan",
      [[tokenIn_address], [amountIn_wei], data]
    );
    const gasAmount = await provider.estimateGas({
      from: "0x5C42F93937389e73A7317e2108cbcFdECf658002",
      to: FLASHLOAN,
      data: functionData,
    });
    console.log(`Estimated gas cost: ${gasAmount}`);
  } catch (e) {
    console.log(e);
  }

  console.log("Estimated gas limit:", gasEstimate.toString());
  return gasLimit;
}

async function testingGas() {
  const tokenIn = "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619";
  const amountIn = ethers.utils.parseUnits("1", 18);
  const data = abiCoder.encode(
    ["address", "address", "address[]", "uint24", "uint256[]"],
    [
      router_addr_quickswap,
      router_addr_sushiswap,
      [tokenIn, "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270"],
      0,
      [amountIn, 0, 0, 0, 0],
    ]
  );
  const gas = await estimateGas(amountIn, tokenIn, data);
}
testingGas();

The error is this:

Error: cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (reason="execution reverted", method="estimateGas", transaction={"from":"0x55A535B67Baa225101fbb859f1609dFD3970af68","to":"0xd4FbfF04437e6FdE782796401bbEd8c4b20FCA72","data":"0x788fb484000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000055a535b67baa225101fbb859f1609dfd3970af68","accessList":null}, error={"reason":"processing response error","code":"SERVER_ERROR","body":"{\"jsonrpc\":\"2.0\",\"id\":67,\"error\":{\"code\":-32000,\"message\":\"execution reverted\"}}\n","error":{"code":-32000},"requestBody":"{\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"0x55a535b67baa225101fbb859f1609dfd3970af68\",\"to\":\"0xd4fbff04437e6fde782796401bbed8c4b20fca72\",\"data\":\"0x788fb484000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000055a535b67baa225101fbb859f1609dfd3970af68\"}],\"id\":67,\"jsonrpc\":\"2.0\"}","requestMethod":"POST","url":"https://skilled-sly-dream.matic.discover.quiknode.pro/.../"}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.7.1)
    at Logger.makeError (c:\Users\danis\arb_gpt\node_modules\@ethersproject\logger\lib\index.js:238:21)
    at Logger.throwError (c:\Users\danis\arb_gpt\node_modules\@ethersproject\logger\lib\index.js:247:20)
    at checkError (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:122:20)
    at JsonRpcProvider.<anonymous> (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:751:47)
    at step (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:48:23)
    at Object.throw (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:29:53)
    at rejected (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:21:65)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  reason: 'execution reverted',
  code: 'UNPREDICTABLE_GAS_LIMIT',
  method: 'estimateGas',
  transaction: {
    from: '0x55A535B67Baa225101fbb859f1609dFD3970af68',
    to: '0xd4FbfF04437e6FdE782796401bbEd8c4b20FCA72',
    data: '0x788fb484000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000055a535b67baa225101fbb859f1609dfd3970af68',
    accessList: null
  },
  error: Error: processing response error (body="{\"jsonrpc\":\"2.0\",\"id\":67,\"error\":{\"code\":-32000,\"message\":\"execution reverted\"}}\n", error={"code":-32000}, requestBody="{\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"0x55a535b67baa225101fbb859f1609dfd3970af68\",\"to\":\"0xd4fbff04437e6fde782796401bbed8c4b20fca72\",\"data\":\"0x788fb484000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000055a535b67baa225101fbb859f1609dfd3970af68\"}],\"id\":67,\"jsonrpc\":\"2.0\"}", requestMethod="POST", url="https://skilled-sly-dream.matic.discover.quiknode.pro/.../", code=SERVER_ERROR, version=web/5.7.1)
      at Logger.makeError (c:\Users\danis\arb_gpt\node_modules\@ethersproject\logger\lib\index.js:238:21)
      at Logger.throwError (c:\Users\danis\arb_gpt\node_modules\@ethersproject\logger\lib\index.js:247:20)
      at c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:313:32
      at step (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:33:23)
      at Object.next (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:14:53)
      at fulfilled (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:5:58)
      at processTicksAndRejections (node:internal/process/task_queues:96:5) {
    reason: 'processing response error',
    code: 'SERVER_ERROR',
    body: '{"jsonrpc":"2.0","id":67,"error":{"code":-32000,"message":"execution reverted"}}\n',
    error: Error: execution reverted
        at getResult (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:191:21)
        at processJsonFunc (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:356:22)
        at c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:288:46
        at step (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:33:23)
        at Object.next (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:14:53)
        at fulfilled (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:5:58)
        at processTicksAndRejections (node:internal/process/task_queues:96:5) {
      code: -32000,
      data: undefined
    },
    requestBody: '{"method":"eth_estimateGas","params":[{"from":"0x55a535b67baa225101fbb859f1609dfd3970af68","to":"0xd4fbff04437e6fde782796401bbed8c4b20fca72","data":"0x788fb484000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000055a535b67baa225101fbb859f1609dfd3970af68"}],"id":67,"jsonrpc":"2.0"}',
    requestMethod: 'POST',
    url: 'https://skilled-sly-dream.matic.discover.quiknode.pro/.../'
  }
}

The strange thing is, when I fork polygon mainnet and do tests with hardhat, everything is working fine, but on mainnet it is not working...

I hope someone can help me. Thank you!

is there even enough money to repay the loan? If you do a trade you pay fees and there is slippage also. Also there is a borrowing fee.

Yes there should be enough, there are no fees from balancer for the flashloan right? And for the rest I funded the contract with the token I borrow, so there should be enough at the end of the trade. So at least estimating gas should work right? And what I think is really strange, the programm works fine on hardhat mainnet fork, but does not work on mainnet itself...

I have had that error before and what I had to do was increase my gas limits in MetaMask.
Don't know if that will help you, but if you are deploying using MetaMask wallet to the main net I think that's your problem.
The default limits are set way too low for a contract with that much gas required and it will revert with 3200 errors.
Good luck