How to estimate the gas fees a transaction will cost using Wagmi

In this tutorial, we are going to learn how to estimate the gas fees that a transaction will cost using Wagmi by estimating the amount of gas needed and multiplying it by the gas price.

In this tutorial, we are going to learn how to estimate the gas fees that a transaction will cost using Wagmi by estimating the amount of gas needed and multiplying it by the gas price.

Here is an example of how to estimate the gas fees for sending Ethereum or calling a write function on a smart contract (in this example it's an NFT smart contract and we estimate the gas fee to call the mint function):

import { useState, useEffect } from 'react';
import { useFeeData, useProvider, useContract, useSigner } from 'wagmi';
import { utils } from 'ethers';

// The exact same data that you would pass to the request property of usePrepareSendTransaction or useSendTransaction
const sendEthRequest = {
  to: '0x5F70Ddd9908B04f952b9cB2A6F8E4D451725ceDC',
  value: utils.parseEther('0.1'),
};

export const Home = () => {
  const [sendEthGasFee, setSendEthGasFee] = useState();
  const [callFuncGasFee, setCallFuncGasFee] = useState();

  // get an Ethers JS provider from the Wagmi client
  const provider = useProvider();

  // The signer is the connected wallet.
  // We need it in the contract to simulate sending transactions from the connected wallet
  const { data: signer } = useSigner();

  // get the gas price
  const { data: feeData, isError: feeDataError, isLoading: feeDataLoading } = useFeeData();

  // get an instance of the contract we are going to interact with
  // here it's an NFT smart contract
  const contract = useContract({
    address: '0xBE86EB679C791D0b4dC98A4fEbbbE86C74Eed872',
    abi: ['function mint(uint256 amount) payable'],
    // provider or signer because if the user has not connected any wallet, signer is null
    signerOrProvider: signer ?? provider,
  });

  useEffect(() => {
    // use Ethers JS to estimate the amount of gas needed for the transaction

    async function estimateGasFee() {
      if (!contract || !provider || !feeData?.gasPrice || !signer || (callFuncGasFee && sendEthGasFee)) return;

      // estimate the amount of gas needed to send ETH
      const sendEthGas = await provider.estimateGas(sendEthRequest);

      // multiply the gas amount values by the gas price to get the gas fee to pay
      setSendEthGasFee(sendEthGas.mul(feeData.gasPrice));

      try {
        // estimate the amount of gas needed to call the contract function
        const callFuncGas = await contract.estimateGas.mint(1, { value: utils.parseEther('0.1') });

        // multiply the gas amount values by the gas price to get the gas fee to pay
        setCallFuncGasFee(callFuncGas.mul(feeData.gasPrice));
      } catch (error) {
        // if it throws an error, it means the transaction will fail
        console.log('Could not estimate gas fees because the transaction will fail');
      }
    }

    estimateGasFee();
  }, [feeData, contract, provider, callFuncGasFee, sendEthGasFee, signer]);

  if (feeDataLoading) return <div>Fetching gas price...</div>;
  if (feeDataError) return <div>Error fetching gas price</div>;

  // display the gas fees and use formatEther to convert the values from Wei to Ether

  return (
    <div>
      {sendEthGasFee && (
        <p>Sending 0.1 ETH to the address you want will cost you {utils.formatEther(sendEthGasFee)} ETH</p>
      )}

      {callFuncGasFee && (
        <p>Calling the contract function you want will cost you {utils.formatEther(callFuncGasFee)} ETH</p>
      )}
    </div>
  );
};

We need to go through 3 steps to estimate the gas fees for a transaction:

  1. Get an estimation of the current gas price. It's never the exact gas price because it can change but it's close enough
  2. Compute the amount of gas that the transaction will consume
  3. Multiply the gas price by the gas amount to get the gas fee the transaction will cost in Ether

Let's go through all the steps

How to get the gas price

To get the gas price using Wagmi, we use the useFeeData hook. That hook returns an object and the data property of that object contains the gas price.

The object returned by useFeeData looks like this (there are more properties but these are the ones that we use the most):

  • data: The data about the gas price:

      •   gasPrice: a BigNumber containing the gas price in Wei

      •   maxFeePerGas: a BigNumber containing the maximum gas price a transaction can pay

      •   maxPriorityFeePerGas: same as above but for priority transactions

      •   formatted: the formatted version of this object. It contains the gasPrice, the maxFeePerGas and the maxPriorityFeePerGas as strings in Ether instead of Wei.
  • isLoading: true if the data is loading and false otherwise
  • isSuccess: true if the data was fetched successfully and false otherwise
  • isError: true if fetching the gas price failed and false otherwise
  • error: the error that was thrown when trying to fetch the gas price. It will be null if there was no error.

To see the whole list properties that the useFeeData hook returns, go to this part of the documentation.

You can also configure the hook to behave differently like return the data in different units, return the data for a different chain, add caching options and many other things.

Check out the documentation to see the whole list of config values you can pass to the useFeeData hook.

How to estimate the amount of gas needed for a transaction

Depending on if you want to estimate the gas amount of sending Ethereum or calling a smart contract function, the estimation will be different.

For sending Ethereum

To estimate the amount of gas needed to send Ethereum, we need an Ethers JS provider because we need to call the estimateGas function. To get that object, we use the useProvider hook.

Then we can call the estimateGas method of the provider. That function takes in parameter an object containing the details of the transaction to send.

That object, is the exact same object that you would pass to the useSendTransaction or the usePrepareSendTransaction hook. It's the information about the transaction you want to send.

Check out the documentation to see all the options you can pass to that object.

Since estimateGas is an asynchronous function, you need to put it in a useEffect and inside an async function.

In the example below, I estimate the amount of gas needed to send 0.1 ETH to a specific address and then I display it.

import { useState, useEffect } from 'react';
import { useProvider } from 'wagmi';
import { utils } from 'ethers';

function MyComponent() {
    const [gasAmount, setGasAmount] = useState()
    const provider = useProvider()
    
    useEffect(() => {
        async function estimateGasAmount() {
            const amount = await provider.estimateGas({
                to: '0x5F70Ddd9908B04f952b9cB2A6F8E4D451725ceDC',
                value: utils.parseEther('0.1')
            })
        }
        estimateGasAmount()
    }, [provider])
    
    return (
        <span>Gas needed: {gasAmount ?? "Loading..."}</span>
    )
}

Learn more about how to send Ethereum transactions here.

For calling write functions of smart contracts

To estimate the amount of gas needed to call a write function of a smart contract, you need to get a signer and create a Contract instance to use the estimateGas property.

The Signer is the wallet connected to your app. We need it to estimate the gas amount it will cost to send the transaction from this specific wallet. To get the signer, we use the useSigner hook which returns an object and the signer will be in the data property.

import { useSigner } from 'wagmi'

// in your component:
const { data: signer } = useSigner()

Here is the documentation of the useSigner hook.

The type of the signer is a Signer from Ethers JS. More on that type here.

Then, we create a Contract instance using the contract address, an ABI containing the function we want to call and estimate the gas and the signer. To create it, we use the useContract hook:

import { useContract, useSigner, useProvider } from 'wagmi'

// IN YOUR COMPONENT

const provider = useProvider()

const { data: signer } = useSigner()

const contract = useContract({
    address: "YOUR_CONTRACT_ADDRESS",
    abi: YOUR_CONTRACT_ABI,
    signerOrProvider: signer ?? provider
})

For the signerOrProvider property, we can't just pass signer because if no wallet is connected, it will crash because signer will be undefined.

So when there is no signer, we pass provider and when the user connects a wallet, it changes the signer so it will re-create the Contract instance.

The useContract hook returns a Contract instance from Ethers JS. More about that type here.

Next, we use the estimateGas property and we call the function we want to estimate the cost of.

For example, if I have a function called mint and it takes in parameter the amount to mint, I can estimate it that way:

// IN YOUR COMPONENT

const { data: signer } = useSigner()
const provider = useProvider()

const contract = useContract({
    address: "0xBE86EB679C791D0b4dC98A4fEbbbE86C74Eed872",
    abi: ["function mint(uint256 amount)"],
    signerOrProvider: signer ?? provider
})

useEffect(() => {
    async function estimateGasAmount() {
        const amount = await contract.estimateGas.mint(1)

        // do something with the gas amount here
    }
    
    if (signer && contract) {
        estimateGasAmount()
    }
}, [contract])

As you can see, the functions of the estimateGas property are asynchronous so you need to put that code in an async function in a useEffect.

It's important to note that it will throw an error if calling that function from the connected wallet will fail.

So you need to catch errors there or your app will crash:

async function estimateGasAmount() {
    try {
        const amount = await contract.estimateGas.mint(1)
        // do something with the gas amount here
    } catch (error) {
        // calling that function with the connected wallet will fail so we can't estimate the gas
        console.log(error)
        console.log("the transaction will fail so we can't estimate the gas fee")
    }
}

How to calculate the gas fee from the gas amount and the gas price

Now that you have the gas price and gas amount, you can multiply the 2 values and you'll get the gas fee that the transaction will cost in Wei. Then, we can use the formatEther function from the Ethers JS utils to convert that value into Ether so it's more readable for us and users.

Since both values are of type BigNumber, we can't just use the * operator in JavaScript, we need to use the mul method of one of the BigNumber and pass in parameter the other value:

import { utils } from 'ethers'

const gasFeeInWei = gasAmount.mul(gasPrice)

const formattedGasFee = utils.formatEther(gasFeeInWei)

And that's it 🎉

Thank you for reading this article