skip to content
Steve Simkins

How to Offset Your NFT Project Carbon Emissions with Aerial

Learn how Aerial is helping make NFTs carbon neutral with their emissions API

Link to Medium Read this post on Medium

As a believer in NFTs and Web3, I am always ecstatic to see what can be done with this new technology. I get bullish over new projects, experimental ideas, and cutting edge utility (especially when it’s something we make at Pinata like Submarine.me). However, I don’t look at this Metaverse with rose colored glasses. There are many imperfections in this space, and one of the worst ones is environmental impact.

While I love dooting around on my computer, I also love the outdoors. I love hiking, seeing mountains, hearing rivers, and smelling wildflowers. I love hearing birds in the morning and geese at night. I love to watch my son experience nature and get excited over a butterfly. So yeah, it kinda kills me to think of how this wonderful industry of crypto can harm this amazing planet.

ethereum carbon emissions chart

The energy consumed by proof of work blockchains is staggering. It’s no secret, and if we want to enjoy this planet we need to accept it. I know I feel defensive when someone attacks something I enjoy, but it helps to take a deep breath and ask the big questions: “could I be wrong?” If crypto is putting the planet in danger, what do we do?

It might sound easy to just throw Web3 out the window and call it a day, but as with most technological advances, the cat is out of the bag. There’s no putting the lid back on Pandora’s box. In my opinion, there is no easy fix to this problem. There will be a need for multiple approaches to help offset our carbon footprint, including but not limited to better blockchain mechanisms and organizations that pursue renewable energy and conservation.

This is where Aerial steps in. Aerial is a sustainability platform that helps users track their carbon footprint and make donations to help offset their carbon footprint. They are known for their beautiful mobile application, but more recently they have developed an API that can track and estimate the carbon footprint of NFT projects and crypto! Per their website, “Emissions are calculated based on the energy consumption of the Ethereum network and the average gas used per transaction.” Since Pinata is the home of NFT media we want to always support initiatives like this to make NFTs more sustainable!

They have some awesome and simple ways you can integrate their widget to your website that looks something like this:

<iframe src="https://www.aerial.is/nft/embed?address=0x3e88721fa41d5e102d54b4a04e550222efdd234d">
screenshot of widget

In this post we’ll take their API a step further and build our own custom widget. We can use it in our frontend website that displays more details about the project’s emissions with the goal of making visitors more aware!

If you want to follow along for this tutorial, you’ll need to have npm installed and be familiar with a frontend framework like React. First thing we’ll do is create a new React app and CD into it.

npx create-react-app aerial-component

cd aerial-component && npm start

Then we’ll delete the CSS files and boilerplate so we are left with this in App.js

function App() {
	return <div className="App"></div>;
}

export default App;

Now let’s make a new component. Create a new file in the src folder called Aerial.js and fill it with this starter code

const Aerial = () => {
	return <h1>Aerial</h1>;
};

export default Aerial;

Now let’s import the new component to our App.js

import Aerial from "./Aerial";

function App() {
	return (
		<div className="App">
			<Aerial />
		</div>
	);
}

export default App;

If you run npm start you should see your basic app with “Aerial” in the top left!

Now that we have our new component ready to go, we need to start making some API calls and fetching the data. If you need a reference for how to use Aerial’s API you can see their reference here.

It’s really quite easy, all we have to do to fetch some basic data is use the following format

GET https://aerial.is/_nft/<address>

This will return data in the following format

{
 "co2": <emissions in CO2>,
 "gas": <gas used>,
 "transactions": <number of transactions>,
 "credits": <credits required to offset>,
 "cost": <cost to offset in USD>,
 "credits_purchased": <number of credits already purchased>,
 "transactions_offset": <number of transactions already offset>
}

So let’s give it a shot! First thing we need to do is install Axios to help us with our API calls. Go back to the terminal and run

npm install axios

Now make sure to import it into the top of our Aerial component like so

import axios from "axios";

Now we’re going to make a quick function that will get the data. For our example we’re going to use the NFT contract address 0x2acab3dea77832c09420663b0e1cb386031ba17b.

const getEmissionsData = async () => {
	try {
		const response = await axios.get(
			"https://aerial.is/_nft/0x2acab3dea77832c09420663b0e1cb386031ba17b"
		);
		console.log(response.data);
	} catch (error) {
		console.log(error);
	}
};

To run this function we’ll use the useEffect hook to fetch the data as we load the app. To do that we simply need to import it at the top like so

import { useEffect } from "react";

Then we need to run the function inside useEffect,

useEffect(() => {
	getEmissionsData();
}, []);

This is what our code will look like with everything in place:

import { useEffect } from "react";
import axios from "axios";

const Aerial = () => {
	const getEmissionsData = async () => {
		try {
			const response = await axios.get(
				"https://aerial.is/_nft/0x2acab3dea77832c09420663b0e1cb386031ba17b"
			);
			console.log(response.data);
		} catch (error) {
			console.log(error);
		}
	};

	useEffect(() => {
		getEmissionsData();
	}, []);

	return (
		<div className="aerial-container">
			<h1>Aerial</h1>
		</div>
	);
};

export default Aerial;

If we run the app and check the dev console, we can see our data!

dev tools

Now that we have the data, it’s as simple as displaying it so users on our website can see it!

To store the data we’ll import the useState hook at the top of our app along with useEffect

import { useEffect, useState } from "react";

Then right above our function to grab the data, we’ll declare our state variable as an empty array where we can push stuff in later.

const [emissionsData, setEmissionsData] = useState([]);

Now all we have to do is edit our function just a little bit to push that data into our state using the “setEmissionsData”! Here is our function now

const getEmissionsData = async () => {
	try {
		const response = await axios.get(
			"https://aerial.is/_nft/0x2acab3dea77832c09420663b0e1cb386031ba17b"
		);
		console.log(response.data);
		setEmissionsData(response.data);
	} catch (error) {
		console.log(error);
	}
};

The next thing we’re gonna do is create two components inside our file, one as a loading indicator, and the other as the data we want to display. To do this we’ll just make two more functions like this

const loading = () => (
	<div className="loading-container">
		<h1>Loading</h1>
	</div>
);

const emissionsComponent = () => {
	return (
		<div className="data-container">
			<h1>Data goes here</h1>
		</div>
	);
};

To switch between the two we’ll create a new state called “isLoading” right underneath our previous state. We’ll set the default value to “false” for now

const [emissionsData, setEmissionsData] = useState([]);
const [isLoading, setIsLoading] = useState(false);

Back in our getEmissionsData function we need to turn the “loading” on when we start the request, and then off when we’re done.

const getEmissionsData = async () => {
	try {
		setIsLoading(true);
		const response = await axios.get(
			"https://aerial.is/_nft/0x2acab3dea77832c09420663b0e1cb386031ba17b"
		);
		console.log(response.data);
		setEmissionsData(response.data);
		setIsLoading(false);
	} catch (error) {
		console.log(error);
	}
};

Finally, way down at the bottom where we render the whole app, we’ll add in some conditional rendering to say “display the loading component while loading, then display the data component when not loading.”

return <div className="aerial-container">{isLoading ? loading() : emissionsComponent()}</div>;

As a quick recap this is what our component looks like at the moment

import { useState, useEffect } from "react";
import axios from "axios";

const Aerial = () => {
	const [emissionsData, setEmissionsData] = useState([]);
	const [isLoading, setIsLoading] = useState(false);

	const getEmissionsData = async () => {
		try {
			setIsLoading(true);
			const response = await axios.get(
				"https://aerial.is/_nft/0x2acab3dea77832c09420663b0e1cb386031ba17b"
			);
			console.log(response.data);
			setEmissionsData(response.data);
			setIsLoading(false);
		} catch (error) {
			console.log(error);
		}
	};

	const loading = () => (
		<div className="loading-container">
			<h1>Loading</h1>
		</div>
	);

	const emissionsComponent = () => {
		return (
			<div className="data-container">
				<h1>Data goes here</h1>
			</div>
		);
	};

	useEffect(() => {
		getEmissionsData();
	}, []);

	return <div className="aerial-container">{isLoading ? loading() : emissionsComponent()}</div>;
};

export default Aerial;

Ok now the good part! Time to parse that data and display it. We’re going to go over a few of the pieces of data and how you may want to display them.

The first is the CO2 emissions, which the API returns as a raw number in the unit of Kg. The best way to make this number manageable in my opinion is to round up the number, and use some javascript to add the commas for each three digits. So back in our emissionsComponent, I have declared the following variable from our saved state.

const co2 = new Intl.NumberFormat().format(Math.round(emissionsData.co2));

Next up is the gas used for this project, and for this one it returns a whole number so no need to round it up. We’ll just format it to be readable.

const gas = new Intl.NumberFormat().format(emissionsData.gas);

Aerial of course also provides their unit for donations to offset the carbon emissions they call “credits.” The API can return the total credits need to make the NFT project carbon neutral, how many are purchased so far, and how much it would cost in total to offset the project in USD. To make this data more readable, we want to display how many credits have been purchased, how many are needed to offset, and how much it would cost to completely offset the project. We just need a little math to make that happen!

For the credits remaining, we just need to subtract the credits already purchased from the total credits needed to offset like so.

const creditsRemaining = new Intl.NumberFormat().format(
	emissionsData.credits - emissionsData.credits_purchased
);

Of course we want to display how many have already purchased and that’s pretty simple.

const creditsPurchased = new Intl.NumberFormat().format(emissionsData.credits_purchased);

Now the more complicated part is calculating how much the remaining cost is. The API gives us the total cost, but it does not include how much has been spent. So for us to get this number we need to divide the emissions cost with the total credits needed to offset, then multiply that against the total emissions credits minus the credits already purchased. In the end it looks like this!

const cost = new Intl.NumberFormat().format(
	(emissionsData.cost / emissionsData.credits) *
		(emissionsData.credits - emissionsData.credits_purchased)
);

Lastly, we want the number of total number of transactions already offset.

const transactions = new Intl.NumberFormat().format(emissionsData.transactions)

Alright that was a bit of work to get sorted, but now we can easily plug this into our emissionsComponent so people can see the data in a more easy to read format. We’re also going to display this in a nice grid with each data cell in a box, so our code inside emissions is going to look like this.

return (
	<div className="data-container">
		<div className="header">
			<h1>Deadfellaz Carbon Offset</h1>
		</div>
		<div className="data-grid">
			<div className="data-cell">
				<h2>{co2} Kg</h2>
				<h3>CO2 Emissions</h3>
			</div>
			<div className="data-cell">
				<h2>{gas}</h2>
				<h3>Gas Used</h3>
			</div>
			<div className="data-cell">
				<h2>{transactions}</h2>
				<h3>Transactions</h3>
			</div>
			<div className="data-cell">
				<h2>${cost}</h2>
				<h3>Cost to Offset</h3>
			</div>
			<div className="data-cell">
				<h2>{creditsRemaining}</h2>
				<h3>Credits needed to offset</h3>
			</div>
			<div className="data-cell">
				<h2>{creditsPurchased}</h2>
				<h3>Credits Purchased so Far</h3>
			</div>
		</div>
		<a
			className="cta-button"
			target="_blank"
			rel="noreferrer"
			href="https://aerial.is/nft/0x2acab3dea77832c09420663b0e1cb386031ba17b"
		>
			Offset
		</a>
	</div>
);

At the bottom we’ve also added a button where a user can click on it and be directed to Aerials page for that particular NFT project, where they can make purchase credits to help offset that project.

To make this whole component feel clean as well as themed for the project, we took some colors and styled from DeadFellaz and added it to this page with some CSS; we also added a fun Lottie animation for the loading component instead of just a header that says loading. In the end our code looks like this!

import { useState, useEffect } from "react";
import "./Aerial.css";
import axios from "axios";
import Lottie from "react-lottie";
import co2 from "./co2.json";

const Aerial = () => {
	const [emissionsData, setEmissionsData] = useState([]);
	const [isLoading, setIsLoading] = useState(false);

	const getEmissionsData = async () => {
		try {
			setIsLoading(true);
			const response = await axios.get(
				"https://aerial.is/_nft/0x2acab3dea77832c09420663b0e1cb386031ba17b"
			);
			console.log(response.data);
			setEmissionsData(response.data);
			setIsLoading(false);
		} catch (error) {
			console.log(error);
		}
	};

	const loading = () => (
		<div className="loading-container">
			<Lottie options={{ animationData: co2 }} height={400} width={400} />
		</div>
	);

	const emissionsComponent = () => {
		const co2 = new Intl.NumberFormat().format(Math.round(emissionsData.co2));
		const gas = new Intl.NumberFormat().format(emissionsData.gas);
		const creditsRemaining = new Intl.NumberFormat().format(
			emissionsData.credits - emissionsData.credits_purchased
		);
		const creditsPurchased = new Intl.NumberFormat().format(emissionsData.credits_purchased);
		const cost = new Intl.NumberFormat().format(
			(emissionsData.cost / emissionsData.credits) *
				(emissionsData.credits - emissionsData.credits_purchased)
		);
		const transactions = new Intl.NumberFormat().format(emissionsData.transactions);

		return (
			<div className="data-container">
				<div className="header">
					<h1>Deadfellaz Carbon Offset</h1>
				</div>
				<div className="data-grid">
					<div className="data-cell">
						<h2>{co2} Kg</h2>
						<h3>CO2 Emissions</h3>
					</div>
					<div className="data-cell">
						<h2>{gas}</h2>
						<h3>Gas Used</h3>
					</div>
					<div className="data-cell">
						<h2>{transactions}</h2>
						<h3>Transactions</h3>
					</div>
					<div className="data-cell">
						<h2>${cost}</h2>
						<h3>Cost to Offset</h3>
					</div>
					<div className="data-cell">
						<h2>{creditsRemaining}</h2>
						<h3>Credits needed to offset</h3>
					</div>
					<div className="data-cell">
						<h2>{creditsPurchased}</h2>
						<h3>Credits Purchased so Far</h3>
					</div>
				</div>
				<a
					className="cta-button"
					target="_blank"
					rel="noreferrer"
					href="https://aerial.is/nft/0x2acab3dea77832c09420663b0e1cb386031ba17b"
				>
					Offset
				</a>
			</div>
		);
	};

	useEffect(() => {
		getEmissionsData();
	}, []);

	return <div className="aerial-container">{isLoading ? loading() : emissionsComponent()}</div>;
};

export default Aerial;

Here is a quick preview of our final project

gif

You can actually view the working component live here, and you can freely download the repo on GitHub!

If you wanted to take this step further you can request an API key from Aerial to use their offsetting endpoints. These let you not only fetch the data, but also provide a way to make a payment for the credits with Stripe or even Coinbase if you have merchant accounts with both of them! In the end it will always be a little easier to redirect users to Aerial.is to make those credit purchases, but I still love that Aerial has made this an option.

Aerial’s API is a great way to display emissions for Ethereum projects and help offset them, however if you are still working on your NFT project there are some simple steps you can take to help offset emissions before you launch!

One of the best things you can do is select a proof of stake blockchain like Solana, Tezos, Flow, or Polygon to create your NFTs on. You can read more on the difference between proof of work and proof of stake blockchains here, but essentially the amount of energy is significantly lower on proof of stake chains because of how they verify transactions.

If you do choose Ethereum there are a lot more gas friendly smart contracts being developed such as ERC-721A by Azuki or ERC-1155D. You can also consider changing how many NFTs you mint for a project. 5,000 to 10,000 is a really popular number, but as Solana has proven you don’t need that many to have a successful project. If you’re doing a much smaller 1/1 project you can even consider using lazy minting. However, if your Ethereum contract ends up putting out emissions, you can offset them from the start with Aerial!

In conclusion, as NFTs progress and blockchains progress, I believe we will find better and more eco-friendly solutions for energy usage. One of the best thing we can do now is to raise awareness, and it starts with each one of us! Using initiatives like Aerial to bring the issue front and center to your NFT holds is an easy way to not only help educate the public, but also provide an avenue to help offset those emissions. At Pinata we would love to see this more and more as we assist creators with their NFT media.

It’s easy to assume that NFTs and Web3 aren’t going away anytime soon, but I’m not sure if we can say the same thing about our planet unless we take care of it.