Published in


How To Build an App NFT

Using Pinata, Polygon, and OpenSea

Getting Started

To complete this tutorial, you’ll need to sign up for a free Pinata account. While using a dedicated gateway will exponentially improve the experience for people who use your app (and you can even connect a custom domain to your app), we will be using Pinata’s free plan features for the tutorial to get you started.

  • NPM or Yarn
  • Code editor

Creating The Contract

The first thing we will want to do is set up our Hardhat environment and create a project. Follow the guide here for the most up-to-date documentation, but I’ll provide the steps (as of writing this) below.

mkdir nft-app-contract && cd nft-app-contract
npm init
npm install --save-dev hardhat
npx hardhat
npm install @openzeppelin/contracts
  • Access controls metadata mapping (this will make more sense soon)
  • Function to update the tokenURI (for updates to the app itself)
  • Mapping of app versions (IPFS CIDs representing each version)
npx hardhat test

The Application

For the application, we’re just going to just the standard React Starter. So, from your command line, open a new window or change out of your smart contract directory. Then, run the following command:

npx create-react-app app-nft-frontend
"homepage": "./"
npm i -g pinata-upload-cli
pinata-cli -a JWT_HERE
"deploy": "npm run build && sh ./"
pinata-cli -u ./build
npm run deploy

Making an App NFT

We have the IPFS CID to use for our tokenURI. So now, we need to deploy our contract. We’ll use Alchemy to talk to a testnet version of Polygon, so log into your Alchemy account and create a new app. Choose Polygon and choose the Mumbai testnet.

require("@nomicfoundation/hardhat-toolbox");module.exports = {
solidity: "0.8.9",
module.exports = {
solidity: "0.8.9",
networks: {
mumbai: {
url: ``,
  • Descriptions
  • Image
  • Animation URL
"name": "App NFT",
"description": "A full application, accessible as an NFT, sold as an NFT, and transferred as an NFT",
"animation_url": "ipfs://HASH_FROM_APP_BUILD"
pinata-cli -u ./metadata.json
const hre = require("hardhat");
const URI = "ipfs://METADATA_CID"
async function main() {
const AppNFT = await ethers.getContractFactory("AppNFT");
const appNft = await AppNFT.deploy(URI);
await appNft.deployed(); console.log(`Contract deployed to ${appNft.address}`);
main().catch((error) => {
process.exitCode = 1;
npx hardhat run scripts/deploy.js
Contract deployed to 0x5FbDB2315678afecq367f032d93F642f64170aa3
npx hardhat run scripts/deploy.js --network mumbai

Wrapping Up

For performance and stability, I highly recommend considering a paid Pinata plan so that you can create a Dedicated Gateway. A Dedicated gateway will give you a global CDN which means your app will load quickly every time, which is especially important when it goes viral.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store