Back to blog
How to Mint an HTML NFT
In this blog, we’ll focus on creating an HTML NFT using ChatGPT, Remix, and Pinata — a project blending AI, blockchain, and personal expression. ChatGPT writes our smart contract, Remix deploys it, and Pinata hosts it, making our HTML NFT easily accessible to the world. By the end, you’ll have something similar to this:
https://opensea.io/assets/ethereum/0x14a5cb1379d510d31a2dbc6580732ad0c4e203ee/1/
Project Inspiration
HTML NFTs have always been a little bit more interesting than image or video NFTs. At Pinata, we’ve always felt NFTs could represent more than a jpeg. The problem has always been accessibility to do so. With ChatGPT and other AI tools coming into the fold, it is now exponential easier for technical and non-technical people to create and build.
As for the series itself, it’s titled Cosmic Reprieve. Inspired by the Nebraska Sandhills, I wanted a fish out of water character in a desolate, yet unknown, Sandhill environment. Drawing from Toy Story’s Cowboy vs Astronaut storyline, the Prada store in Marfa, Texas, and some of the feelings I have building Pinata in Nebraska, hopefully it gives a little bit of a reprieve from whatever hectic world you live in.
With that, let’s jump in!
HTML
First, you will need an HTML file that you would like to mint. This tutorial assumes that you already have one. If you don’t, you can learn how to make one using Midjourney, ChatGPT and Pinata here: How to Create a Prompt Gallery
Once you have your HTML file, you can upload it to Pinata. To do so, simply login. Once on the file manager page, click + Add Files in the upper right hand corner to upload your HTML file.
Generate JSON Metadata
Next, you’ll want to head over to chat.openai.com to start prompting ChatGPT. Your First prompt should be the following:
Prompt 1
Can you generate a basic metadata JSON for an ERC721 token, including name, description, image, and animation? Please exclude attributes.
Answer 1
Sure, I can definitely generate a JSON for an ERC721 token metadata. Please note that the actual values for name, description, image, external URL, and animation are placeholders and should be replaced with real data. Here’s an example:
{
"name": "Token Name",
"description": "This is a description of the token.",
"image": "https://example.com/path/to/image.png",
"external_url": "https://example.com/path/to/token",
"animation_url": "https://example.com/path/to/animation.mp4"
}
"name"
: The name of the token."description"
: A description of the token."image"
: A URL to an image that represents the token. This should be a PNG, JPEG, or SVG image."external_url"
A URL that leads to a webpage with more information about the token."animation_url"
: A URL to an MP4 that contains an animation representing the token. This field is optional and may not be included if there is no animation for the token.
Again, these are just placeholder values and you should replace them with your actual data.
Prompt 2
Can you make the following changes:
name: Cosmic Reprieve
description: An HTML Astronaut
image: https://kyletut.com/ipfs/QmdJPaZMBstDTz12VLXCK6qJEUVW6Y5LgcwbefoyqvDdFF
external-url: https://kyletut.com
animation_url: https://kyletut.com/ipfs/Qmd6wyNbRmzpUaRaXPX1c6GWo8VC64PxJBsi4W8BYNV8qG
Answer 2
Absolutely, I can make those changes for you. Here is the updated metadata JSON for your ERC721 token:
{
"name": "Cosmic Reprieve",
"description": "An HTML Astronaut",
"image": "https://kyletut.com/ipfs/QmdJPaZMBstDTz12VLXCK6qJEUVW6Y5LgcwbefoyqvDdFF",
"external_url": "https://kyletut.com",
"animation_url": "https://kyletut.com/ipfs/Qmd6wyNbRmzpUaRaXPX1c6GWo8VC64PxJBsi4W8BYNV8qG"
}
Please remember to validate the JSON and ensure that the URLs are accessible and point to the correct resources.
Save the JSON Metadata File
You will want to save the above JSON file to your computer and upload it to Pinata. If you are new to coding, you will need to use a text editor such as Sublime. To save the file, simply copy the code into the text editor and save it to your computer. Once you’ve saved it to your computer, upload it to Pinata and grab the URL to the file. It should look like the following:
https://kyletut.com/ipfs/QmVvb12MKAwLubN5SnoEv8a83xWcSTEDZU17SSYMoWXZT3
Create ERC721 Contract
Be sure to grab the above URL and then prompt ChatGPT with the following.
Prompt 3
Can you generate a basic Solidity smart contract code for an ERC721 token named ‘Cosmic Reprieve’ with the symbol ‘CR’, which has a minting function and uses a base URI pointing to a metadata JSON file. Here is the URL to the JSON file:
https://kyletut.com/ipfs/QmVvb12MKAwLubN5SnoEv8a83xWcSTEDZU17SSYMoWXZT3
I want all tokens to have the same metadata and prevent the token ID from being appended. Override the tokenURI function in your contract to ignore the token ID and always return the base URI. The contracts should be imported directly into the Solidity code itself which Remix should fetch when compiling the contract. Please use the latest versions from OpenZeppelin.
Answer 3
This is a simple implementation of an ERC721 contract using OpenZeppelin’s library. ERC721 is a free, open standard that describes how to build non-fungible tokens on the Ethereum blockchain. Non-fungible tokens (NFTs) are tokens that are not interchangeable with each other and have unique properties.
Please note that it’s crucial to thoroughly audit and test any smart contract code for security and functionality before deployment. You should also make sure that you have a complete understanding of the code and its implications.
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/[email protected]/token/ERC721/ERC721.sol";
contract CosmicReprieve is ERC721 {
string constant baseURI = "https://kyletut.com/ipfs/QmVvb12MKAwLubN5SnoEv8a83xWcSTEDZU17SSYMoWXZT3"; // The base URI points to your metadata JSON
constructor() ERC721("Cosmic Reprieve", "CR") {}
function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
}
function _baseURI() internal pure override returns (string memory) {
return baseURI;
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
// Always return the baseURI regardless of tokenId
return baseURI;
}
}
Here’s what each part of the contract does:
Inside each SDK, you have specific API methods like pinFileToIPFS
, pinFileToIPFS
and testAuthentication
. Important functions that are a bit more difficult to execute on their own. The Pinata SDK comes pre-loaded with built-in instructions to process each method and make a beautiful cup ‘o coffee.
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help init` for definitive documentation on these fields and exactly
what they do.
Use `npm install <pkg>` afterwards to install a package and save it as a
dependency in the package.json file.
Press ^C at any time to quit.
package name: (pinata-sdk-guide)
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/[email protected]/token/ERC721/ERC721.sol";
contract CosmicReprieve is ERC721 {
string constant baseURI = "https://kyletut.com/ipfs/QmVvb12MKAwLubN5SnoEv8a83xWcSTEDZU17SSYMoWXZT3"; // The base URI points to your metadata JSON
constructor() ERC721("Cosmic Reprieve", "CR") {}
function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
}
function _baseURI() internal pure override returns (string memory) {
return baseURI;
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
// Always return the baseURI regardless of tokenId
return baseURI;
}
}
"name"
: The name of the token."description"
: A description of the token."image"
: A URL to an image that represents the token. This should be a PNG, JPEG, or SVG image."external_url"
: A URL that leads to a webpage with more information about the token."animation_url"
: A URL to an MP4 that contains an animation representing the token. This field is optional and may not be included if there is no animation for the token.pragma solidity >=0.8.0 <.09.0
: This line specifies the compiler version to be used for the smart contract. It states that this contract should be compiled with a Solidity compiler version greater than or equal to 0.8.0 but less than 0.9.0.import "@openzeppelin/[email protected]/token/ERC721/ERC721.sol"
: This line imports the ERC721 contract from the OpenZeppelin library. OpenZeppelin is a library for secure smart contract development.contract CosmicReprieve is ERC721 {...}
: The contractCosmicReprieve
is declared here, and it inherits from theERC721
contract imported from OpenZeppelin.string constant baseURI = "https://kyletut.com/ipfs/QmV
: This is the base URL that points to your metadata JSON. This is used to retrieve the metadata for each token.constructor() ERC721("Cosmic Reprieve", "CR") {}
: This is the constructor of the contract. It calls the constructor of the parent ERC721 contract with the name and symbol of this token.function mint(address to, uint256 tokenId) public {...}
: This function allows for the creation of new tokens. It's a public function so anyone can call it.function _baseURI() internal pure override returns (string memory) {...}
: This function overrides the _baseURI function from the ERC721 contract. It's used to provide the base URL for the token metadata.function tokenURI(uint256 tokenId) public view override returns (string memory) {...}
: This function overrides the tokenURI function from theERC721
contract. It's used to provide the URL for a token's metadata. In this contract, it always returns the base URI regardless of the token ID, meaning it assumes all tokens have the same metadata.
Deploy and Mint using Remix
Prompt 4
Can you show me how to deploy and mint the contract using Remix?
Answer 4
Sure, I can provide the steps to deploy and mint a token using the Remix Ethereum IDE:
Go to the Remix Ethereum IDE website (http://remix.ethereum.org). Click on the blue “New File” button towards the center-left of the page. Name it CosmicReprieve.sol
.
Copy the Solidity code provided earlier and paste it into this new file.
Once you have the contract ready, click on the “Solidity Compiler” button. Choose the appropriate compiler version (0.8.0 or higher as specified in the contract code). Then click on “Compile CosmicReprieve.sol”.
After the contract is compiled without errors, click on the “Deploy & Run Transactions” button. Choose the environment as “Injected Provider — MetaMask” (for MetaMask). You’ll need to connect your MetaMask or other provider. In MetaMask, be sure to chose the appropriate network you want to deploy to! For testing, use a test network like Goerli.
For “Contract”, choose CosmicReprieve.sol
. (it should be automatically selected if it's the only contract in the file). Then click on "Deploy". You'll need to confirm the transaction in your MetaMask wallet.
After the contract is deployed, it will appear under the “Deployed Contracts” section. Click on it to expand it, and you’ll see a list of all public functions that you can call.
To mint a token, enter the address you want to mint the tokens to and the token ID, then click on the “mint” button. In my instance, I entered “0x5ba…9bAD, 1”. If you’re using “Injected Provider”, you’ll need to confirm the transaction in your wallet.
Please note that this is a simplified guide and the actual steps might differ slightly based on the current design of Remix and the specific settings of your environment. You’ll also need some Ether (real on the mainnet, or fake on a testnet) to pay for transactions. And that’s it. Check out my Cosmic Reprieve NFT here!
Happy pinning!