1

I deployed a smart contract on polygon mainnet and want to estiamte gas costs before calling the function. 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
        );
    }
}

The contract is deployed here. If I try to estimate gas like this:

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 gas = await flash_contract.estimateGas.flashLoan(
  [tokenIn_address],
  [amountIn_wei],
  data
);
    console.log(`Estimated gas cost: ${gasAmount}`);
  } catch (e) {
    console.log(e);
  }

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

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();

Can someone help me? I am getting this error:

 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/../'
  }
}

Thank you!!!

1 Answer 1

0

I apologize for being wrong in my previous answer which is why I have deleted it. The TRANSFER_FROM error would be generated from a router contract and not the vault. In the swapper function you always set approval for quickRouter instead of using the dex_router parameter. Hence other routers would not have allowance to spend the tokens.

5
  • Oh yes, what a mistake... Thank you! But it still gives me an error, which says, I dont have enough balance. But I will only calculate gas costs for a potetntial trade. Why is this not working? I updated my smart contract in the question
    – Dani S
    Commented Apr 9, 2023 at 8:15
  • Calculating the estimated gas cost will involve the node trying to run the transaction. In this case if the amount of tokens received after the swap if less than amount , it will revert when trying to transfer. Hence the node will not be able to estimate the gas.
    – Maanas O T
    Commented Apr 9, 2023 at 17:41
  • ok, so I have tried to fund my contract with weth and trade with weth. So it should be no problem to repay the loan even if the trade is not positive. But it still fails... I actualized the error in the question
    – Dani S
    Commented Apr 10, 2023 at 15:32
  • require( _amountIn >= balance(_tokenIn), "Error: Actual amount received is less than expected" ); Then this condition would fail right. Why is such a condition required? From the test it seems like you are using the mainnet while testing. If that is the case why don't you consider using a fork so that you could save money?
    – Maanas O T
    Commented Apr 10, 2023 at 17:21
  • Yes you are right, I setted the ">=" the wrong way. It is "<=" now. It is there to check, if that the contract has enough funds to do the trade. I tried with a fork, there is everything working fine, I can estimate gas. But if I deploy, it fails... Don't know why... If it is helpful if I show my hardhat test file, let me know, I can add it. Thank you so much for helping me, I don't know what to do here anymore...
    – Dani S
    Commented Apr 11, 2023 at 20:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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