Picture this, you’re ready to deploy your next amazing smart contract, maybe it’s a minting contract for an awesome NFT collection, a contract for a Token, or maybe you’re deploying a contract for a Governor DAO. (Don’t worry you don’t need to know what all of this is for this tutorial) Whatever it is, what if you didn’t budget properly for the gas fees needed to deploy said contract? It’s important to understand and account for how much it’s going to cost you to get these Smart Contracts deployed onto the Ethereum Blockchain.
In this tutorial, we’re going to take a look at building a small dashboard that will give us some insights into the gas fees for the most recent blocks on Ethereum. We’re going to get a gas fee history from the 20 latest blocks and display that information in a graph and small chart so our users can get an idea of how the gas fees are at the moment.
First off, let’s create an API key on Alchemy which is going to be our Node Provider and will give us an awesome tool to quickly create this project. Once you’ve created an Alchemy Account let’s create an App and grab an API Key.
To create an app - click on Apps in the navigation bar of your dashboard and click Create App.
I named my App Gas Fee Estimator and kept all of the default values when creating it since I’m going to be connecting to the Ethereum Main net in this case. Once you’ve got this set up you can hit view details on your app and navigate to View Key to grab your API Keys. We’re going to need this in just a sec so keep this handy!
I’m going to assume you have Node JS installed on your local machine, if you don’t you can grab that here. I’m going to be using the Yarn package manager in this tutorial but I’ll include the commands for using NPM which comes prepackaged with Node!
Navigate to a fresh new folder in your Terminal and run one of these commands
yarn create next-app
or
npx create-next-app
Follow the prompts to scaffold out your new project and open that up in your favorite IDE. Let’s also install two dependencies we’re going to need for this project.
yarn add @alch/alchemy-web3 recharts
Alternatively you can use the npm install command instead!
One last thing to add before we dive into the fun stuff! Let’s create a .env file at the root of our folder and populate it with our Alchemy API Key URL we got earlier. Your env file just needs one line:
ALCHEMY_URL=https://eth-mainnet.alchemyapi.io/v2/<your api key>
That’s it for our environment set up, let’s start getting into the code!
Next JS is a React framework with a lot of cool features. We’re going to be leveraging a feature called getServerSideProps which will allow us to make a server side request for all the data we need and pass that data down as props to our Component. To get Started I removed a lot of the boilerplate code in my index.js file in the Pages directory and now just have this:
import styles from "../styles/Home.module.css";
import { createAlchemyWeb3 } from "@alch/alchemy-web3";
export default function Home() {
return (
<div className={styles.container}>
</div>
);
}
export async function getServerSideProps(context) {
}
I’m importing createAlchemyWeb3 from one of the dependencies we installed. This is a nice replacement for the popular Web3 JS package which follows the same API but is configured to work seamlessly with Alchemy.
Our work in this section is going to be centered around the getServerSideProps function! We can quickly leverage the power of web3 with this one line added to our function:
export async function getServerSideProps(context) {
const web3 = createAlchemyWeb3(process.env.ALCHEMY_URL);
}
This creates an instance of web3 for us and gives us access to all of the Web3 JS API’s. The method we’re most interested in is web3.eth.getFeeHistory. What we want to do is utilize this method to get information about the last 20 blocks and since users are usually accustomed to it we want to display fees at low, medium, and high priority transactions.
We can get that information like so:
const historicalBlocks = 20;
const history = await web3.eth.getFeeHistory(
historicalBlocks,
"latest",
[25, 50, 75]
);
The first argument we pass is the amount of blocks we want to get the history for, we want the latest information about this blocks, and finally the array represents the 25th, 50th, and 75th percentile of priority fees which we are using to match to our low, medium, and high priority transactions.
Now if we were to log out this object there would be quite alot going on, so let’s focus on what actually matters for our use case. We want to calculate the gas fees for all of these blocks. According to EIP-1559 we need to get a base Fee as well as the reward for our wonderful miners and add those together to understand what the total gas fee would be for those transactions. I’m going to grab those two bits of information as well as the oldest block number to help us reference which blocks we are getting information on:
const { reward, baseFeePerGas } = history;
const oldestBlock = Number(history.oldestBlock);
I de-structured out the reward and base fee information and converted the oldest block to a number to make it more readable for us.
Let’s create an object with all of the information we’re going to need to send over to our frontend once this server code executes.
const latestGasEstimates = reward.map((block, i) => {
const allGasInfo = {
blockNumber: oldestBlock + i,
baseFeePerGas: Number(baseFeePerGas[i]),
priorityFeePerGas: block.map((x) => Number(x)),
};
return {
blockNumber: allGasInfo.blockNumber,
low: allGasInfo.baseFeePerGas + allGasInfo.priorityFeePerGas[0],
medium: allGasInfo.baseFeePerGas + allGasInfo.priorityFeePerGas[1],
high: allGasInfo.baseFeePerGas + allGasInfo.priorityFeePerGas[2]
};
});
Lot’s going on here! Let’s break it down:
My objective here is to create an array of objects with gas fee information so I decided to map over Rewards which gives me an array of the amount miners receive for confirming transactions at each of the. That array looks something like this:
reward: [
[ '0x6fb9cbef7', '0x161dea08f7', '0x28be4928f7' ],
[ '0x10378b36e1', '0x1bdbc6aae1', '0x20fb1406e1' ],
[ '0x112fcac6f6', '0x1dfe0c2cf6', '0x287841aef6' ],
[ '0x1dfc552db', '0x59971f2db', '0xd8400c6db' ]
]
If you print the reward in your terminal you’ll get 20 results instead of these four arrays since we’re dealing with the last 20 blocks.
Now back to our code: while mapping over this array I’m also getting the block numbers for each block by starting from the oldest block given to us from the history and adding the index every time we iterate.
Next I’m grabbing the baseFeePerGas on each block, that is an array that looks like this:
baseFeePerGas: [
'0x1d1b1b8f09',
'0x1e5962991f',
'0x1d6123090a',
'0x210ced0925',
'0x252e208712'
]
I convert each of these to numbers so we can use them properly in our object. The last part here is to take the rewards and make sure they become numbers as well.
priorityFeePerGas: block.map((x) => Number(x))
Awesome now we have some of the information we need let’s construct the final object we need for our frontend. This is what I returned in the code above:
return {
blockNumber: allGasInfo.blockNumber,
low: allGasInfo.baseFeePerGas + allGasInfo.priorityFeePerGas[0],
medium: allGasInfo.baseFeePerGas + allGasInfo.priorityFeePerGas[1],
high: allGasInfo.baseFeePerGas + allGasInfo.priorityFeePerGas[2]
};
I’m tracking all of the block numbers and then adding base fee for the gas to each of the gas priority fees so we can display an object that has the associated fees for each level of priority.
This is enough to create a nice visualization on the frontend, but let’s also get an average gas fee over the last 20 blocks so we can give our users an extra data point.
I’m adding a generic function that calculates the average of all the numbers in an array here:
const calculateAverage = (arr) => {
const sum = arr.reduce((a, v) => a + v)
return Math.round(sum/arr.length)
}
Next let’s get the current base fee using the web3.eth.getBlock method.
const currentBlock = await web3.eth.getBlock("pending");
const currentBaseFeePerGas = Number(currentBlock.baseFeePerGas)
Finally we’re gong to calculate the averages and then return all of our information inside of a props object that can be accessed from our frontend component.
const lowAverage = calculateAverage(latestGasEstimates.map(estimate => estimate.low));
const midAverage = calculateAverage(latestGasEstimates.map(estimate => estimate.medium));
const highAverage = calculateAverage(latestGasEstimates.map(estimate => estimate.high));
return {
props: {
latestGasEstimates,
averages : {
low: lowAverage + currentBaseFeePerGas,
medium: midAverage + currentBaseFeePerGas,
high: highAverage + currentBaseFeePerGas
}
},
};
Don’t forget to add the base fee before sending the averages off to the frontend!
If you’ve been following along with this tutorial then at this point your getServerSideProps function should look something like this:
export async function getServerSideProps(context) {
const web3 = createAlchemyWeb3(process.env.ALCHEMY_URL);
const historicalBlocks = 20;
const history = await web3.eth.getFeeHistory(
historicalBlocks,
"latest",
[25, 50, 75]
);
const { reward, baseFeePerGas } = history;
const oldestBlock = Number(history.oldestBlock);
const latestGasEstimates = reward.map((block, i) => {
const allGasInfo = {
blockNumber: oldestBlock + i,
baseFeePerGas: Number(baseFeePerGas[i]),
priorityFeePerGas: block.map((x) => Number(x)),
};
return {
blockNumber: allGasInfo.blockNumber,
low: allGasInfo.baseFeePerGas + allGasInfo.priorityFeePerGas[0],
medium: allGasInfo.baseFeePerGas + allGasInfo.priorityFeePerGas[1],
high: allGasInfo.baseFeePerGas + allGasInfo.priorityFeePerGas[2],
};
});
const calculateAverage = (arr) => {
const sum = arr.reduce((a, v) => a + v)
return Math.round(sum/arr.length)
}
const currentBlock = await web3.eth.getBlock("pending");
const currentBaseFeePerGas = Number(currentBlock.baseFeePerGas)
const lowAverage = calculateAverage(latestGasEstimates.map(estimate => estimate.low));
const midAverage = calculateAverage(latestGasEstimates.map(estimate => estimate.medium));
const highAverage = calculateAverage(latestGasEstimates.map(estimate => estimate.high));
return {
props: {
latestGasEstimates,
averages : {
low: lowAverage + currentBaseFeePerGas,
medium: midAverage + currentBaseFeePerGas,
high: highAverage + currentBaseFeePerGas
}
},
};
}
In your Frontend code if you pass down props into the component and log it you should see all of the information above in a nicely formatted array of objects!
export default function Home(props) {
console.log(props)
return (
<div className={styles.container}>
<h1>Hello World </h1>
</div>
);
}
Let’s move on to displaying this in a nice visual!
Let’s build out our little dashboard now! One of the dependencies we installed at the start of this tutorial was recharts. This is a lightweight and easy to use library for helping us construct really awesome looking charts for visualizations! I’m going to import the components we’re using from recharts below:
import {
LineChart,
Line,
Legend,
XAxis,
YAxis,
Tooltip,
} from "recharts";
Seems like a lot, but all of these components do a lot of amazing work for us without us needing to fiddle around with SVG’s and designs for too long. Let’s grab the data we need from our props.
export default function Home({ latestGasEstimates, averages }) {
const maxValue = Math.max.apply(Math, latestGasEstimates.map( value => value.high));
const minValue = Math.min.apply(Math, latestGasEstimates.map( value => value.low));
const { low, medium, high} = averages
return (
<div className={styles.container}>
<h1>Hello World </h1>
</div>
);
}
I destructured out our gas estimates and averages from the props and also calculated some minimum and maximum values for our gas estimates. This is to make our chart easier to read because the amount of Wei returned in this calculations can be extremely high.
I also destructured out our low, medium, and high averages which we will also be displaying.
Let’s get to the interesting part: the line graph!
<h1>Latest Gas Estimates</h1>
<h2>Last 20 Blocks Fees in Wei</h2>
<p>Hover For breakdown of high, medium, and low priority transaction</p>
<LineChart width={650} height={500} data={latestGasEstimates} margin={{ left: 65, top: 80, right: 50}}>
<Line type="monotone" dataKey="high" stroke="red" />
<Line type="monotone" dataKey="medium" stroke="blue" />
<Line type="monotone" dataKey="low" stroke="green" />
<XAxis />
<YAxis type="number" domain={[ minValue, maxValue]} />
<Tooltip />
<Legend />
</LineChart>
I replaced the hello world with the JSX above! Let’s focus on the chart:
The Line Chart component takes a width and height and a data key with some optional margin to make it look nice. The important part here is making sure we pass the latestGasEstimates to data so that the recharts package can work it’s magic to convert that data into a chart.
I’m using three lines to represent our three data points for each priority fee. The important part here is to make sure that the dataKey prop matches the associated key in the gas estimates object. I’ve left the X axis alone so that it defaults to showing us regular numbers and formatted the Y axis to use the minValue and maxValue that we calculated before. Again this is just because our numbers are extremely large in wei.
I dropped in the Tooltip and Legend without any props so we can get the default styling on these. Now with all these props in the right places if you run your frontend using the command:
yarn dev
or
npm run dev
You’ll get a nice little chart that looks like this:
Really nice looking chart right? All we had to do was pass it the right data and recharts took care of the rest for us! You can even hover across each tick to get the values at each block!
Let’s also wrap this up by adding some additional information:
<div>
<h2>Average gas fee in wei over the last 20 blocks</h2>
<ul>
<li>High Priority: {low}</li>
<li>Medium Priority: {medium}</li>
<li>Low Priority: {high}</li>
</ul>
</div>
<div>
<h2>Last 20 blocks</h2>
{latestGasEstimates.map((estimate, i) => <li key={i}>{i}. {estimate.blockNumber}</li>)}
</div>
With this I’m displaying the average fees across different priorities for the last 20 blocks and letting the user know what block number we are on for the last 20 blocks as well.
This is optional but here is my JSX code with some class names for styling. You can also copy the styles into your Home.module.css so it’s all a little bit nicer to look at. I definitely encourage you to make this your own!
return (
<div className={styles.container}>
<div className={styles.innerContainer}>
<div>
<h1>Latest Gas Estimates</h1>
<h2>Last 20 Blocks Fees in Wei</h2>
<p>Hover For breakdown of high, medium, and low priority transaction</p>
<LineChart width={650} height={500} data={latestGasEstimates} margin={{ left: 65, top: 80, right: 50}}>
<Line type="monotone" dataKey="high" stroke="red" />
<Line type="monotone" dataKey="medium" stroke="blue" />
<Line type="monotone" dataKey="low" stroke="green" />
<XAxis />
<YAxis type="number" domain={[ minValue, maxValue]} />
<Tooltip />
<Legend />
</LineChart>
</div>
<div>
<div>
<h2>Average gas fee in wei over the last 20 blocks</h2>
<ul>
<li>High Priority: {low} wei</li>
<li>Medium Priority: {medium} wei</li>
<li>Low Priority: {high} wei</li>
</ul>
</div>
<div>
<h2>Last 20 blocks</h2>
{latestGasEstimates.map((estimate, i) => <li className={styles.blockItem} key={i}>{i}. {estimate.blockNumber}</li>)}
</div>
</div>
</div>
</div>
);
}
CSS:
.container {
padding: 0 2rem;
}
.container ul {
list-style: none;
}
.innerContainer {
display: flex;
}
.blockItem {
list-style: none;
}
Nothing Fancy we’ll just make this readable for now! The end result will look something like this:
Now we’ve got a dashboard that can help us take a look at the gas fee history over 20 blocks! As you can see the fees can vary wildly between the low and high priority fees at times!
Hope you enjoyed this tutorial! If you would like to dive into the source code the final code can be viewed on this repository:
If you want to learn more just about the script here is another repository focusing just on the script for getting the gas fees: