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:
- Get an estimation of the current gas price. It's never the exact gas price because it can change but it's close enough
- Compute the amount of gas that the transaction will consume
- 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
: aBigNumber
containing the gas price in Wei
•maxFeePerGas
: aBigNumber
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 thegasPrice
, themaxFeePerGas
and themaxPriorityFeePerGas
as strings in Ether instead of Wei.isLoading
:true
if the data is loading andfalse
otherwiseisSuccess
:true
if the data was fetched successfully andfalse
otherwiseisError
:true
if fetching the gas price failed andfalse
otherwiseerror
: the error that was thrown when trying to fetch the gas price. It will benull
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