Non-Fungible Tokens (NFTs) have exploded in popularity over the last year. And with NFTs being used for
In this article, we’ll look at one way to solve those issues by creating and deploying an NFT to the Polygon Network and using IPFS to store the metadata. In our example, we’ll use
Fungible tokens are cryptographic tokens that can be exchanged for any other of the same token (1 == 1). These include cryptocurrencies such as Bitcoin (BTC) or ETH. One Bitcoin is exactly the same as any other Bitcoin.
In contrast,
Most NFTs are created on the
Over the course of 2021, several solutions to this problem have gained popularity. Layer 2 solutions such as
Another current problem with NFTs is that the metadata they contain isn’t always stored in a decentralized manner. If the organization hosting the metadata disappears, so does the value of your NFT. The best solution is to host the metadata using a global, peer-to-peer file system such as InterPlanetary File Storage (IPFS).
Combining technologies such as Polygon and IPFS creates a cost-effective method to deploy and store your NFT in a more decentralized and permanent way. Although we could use no-code services such as those provided by
So let’s look at how you can create and deploy an NFT to the Polygon Network. We will use Truffle as our development environment and Infura to connect and interact with the blockchain, and to store the metadata with IPFS.
Before deploying, we will test on a local instance of a blockchain with Ganache. When it’s ready to go live, we will deploy to the Polygon Mumbai Test Network and will verify the NFT on OpenSea, the world’s most popular NFT platform.
Before we get to the fun parts like building and testing, let’s set up our accounts and get everything in place. This will ensure the rest of the process goes smoothly.
Infura is an Ethereum node provider and API. It will allow us to easily connect to the Polygon Network and also provides an easy-to-use method for utilizing IPFS.
The first thing we need to do is head to
We can add Polygon to our plan by selecting the “Add-Ons” tab on the left-hand side of the screen and scrolling down to “Network Add-Ons”. Next, we select “Polygon PoS Add-On” and go through the prompt to add it.
Now we’re ready to create a new project. Let’s head back to the dashboard and click on the “Create New Project” button. We’ll select Ethereum as the product and name our project Polygon-NFT.
Once our project is created, there are several things to note:
Let’s change our Endpoint from MAINNET to POLYGON MUMBAI, which is the Polygon testnet. Our Endpoint link will then change. We will need the values on this page later, but for now, we are done with the project page.
Since we are already in our Infura account, let’s go ahead and create another project. This one will be for utilizing IPFS to store the image and metadata of our NFT. This time when we create a new project, we will select IPFS as the product. Let’s name this one Polygon-NFT-Metadata.
For the next steps we will need the Project ID, project secret, and endpoint URL, but first, we need an image for our NFT.
Feel free to use your own image moving forward. OpenSea recommends the image size to be 350 x 350 pixels.
Now let’s upload the image to IPFS!
Using a command terminal, we will cd
into the folder where our image is stored.
To upload the image, type the following command:
curl -X POST -F file=@myfile \ -u "PROJECT_ID:PROJECT_SECRET" \
"https://ipfs.infura.io:5001/api/v0/add"
@myfile
should be the name of the image file in the current folder. So in our case @polygon-nft.jpg
.PROJECT_ID
and PROJECT_SECRET
will be the keys provided in the settings for our Infura project.
The output from the command should look something like this:
The most important part of the output will be our “Hash”. We can actually verify the image was uploaded successfully by pasting that Hash into the following URL:
It’s important to note that Infura pins our data to IPFS by default. If we ever want to remove an item from IPFS we have to
{
"name": "My Sweet Polygon NFT",
"description": "Our amazing NFT deployed to the Polygon Network",
"image": "ipfs://YOUR_HASH_HERE"
}
So let’s create a new text file in the same folder on our local machine as our NFT image. We can use any text editor for this; it doesn’t need to be anything fancy.
We’ll add the metadata in the same format as above. However, rather than using YOUR_HASH_HERE
we’ll insert the actual Hash for our image that we just verified. We’ll save the file as nft-metadata.json
. We also need to make sure there is no comma after the last item in our JSON file. If there is a comma, then OpenSea will not display the NFT properly.
Now we can add this file to IPFS using the same command as last time; we just need to replace the file name with @nft-metadata.json
.
Excellent! We now have our metadata pinned to IPFS which has a link that points to our already pinned image. We’ll copy the Hash in the output to use later.
In order to interact with the Polygon Network and to pay any transaction fees, we will need to set up a Web3 wallet. We will use MetaMask to do this. It is installed as a browser extension and allows us to connect to Decentralized Apps (dApps).
Let’s head over to
We will need to use the 12-word seed phrase when setting up our Truffle project. So be sure to write that down and save it for later. Once we install MetaMask and open it to the dashboard, we need to add Polygon to our Networks dropdown.
When we click the dropdown, it reveals a button to add new networks to the list. So we’ll press the “Add Network” button, and
We will need to enter in the following information:
Network Name: Mumbai (Polygon Testnet)
New RPC URL:
Chain ID: 80001
Currency Symbol: MATIC
Block Explorer URL:
Network Name: Polygon Mainnet
New RPC URL:
Chain ID: 137
Currency Symbol: MATIC
Block Explorer URL:
Now we have the Polygon Network added to our MetaMask, but if we switch to the Mumbai Testnet, there’s no MATIC! Let’s change that. We’ll need to copy our wallet address, which is listed under the name of our Account. It starts with 0x…
Next, we’ll head over to
Now that we have loaded our wallet with some test tokens, we are ready to move on.
Truffle is a development environment that provides tools to make building on the blockchain much easier. In order to install it via the command line, we will need NodeJS v8.9.4 or later and Node Package Manager (npm). If it’s not already installed,
Once it’s installed on our machine, we can install Truffle using this command in the terminal:
npm install -g truffle
We can type the command truffle version
afterward to ensure it was installed correctly. If there are any errors, be sure to add the npm modules to your path.
Next we’ll install Ganache, a local blockchain instance used for testing before deploying to the Mumbai Testnet.
Ganache has a GUI version that can be downloaded
npm install ganache --global
Again, we can verify proper installation by checking the version: ganache --version
Now that our accounts are set up and everything is installed properly, let’s start putting it all together. The first step is to navigate in our terminal to where we want to create our project and make a new directory.
mkdir polygon-nft-project && cd polygon-nft-project
One incredibly useful thing about Truffle is they offer a full suite of “
truffle unbox polygon
After installation, we can see there are several new files in our project folder. The most important ones we will be working with are the README.md
file, truffle-config.polygon.js
, the 1_deploy_simple_storage.js
under the migrations folder, and SimpleStorage.sol
files, which can be found in the contracts folder.
You will likely see an ethereum and polygon folder under contracts. This is because typically when a project is deployed to Polygon, it should also be deployed to Ethereum so users can easily bridge their assets back and forth. We can delete the SimpleStorage.sol
contracts since we won’t be using them.
Taking a quick glance through the README.md
file, we can see that in order to deploy to the Polygon Network, we will have to add two things:
We’ll want to store these in a .env
file and make sure it’s added to our .gitignore
so we don’t accidentally upload these secrets if storing our project on a public repository.
Downloading the Polygon Truffle Box also installed the .dotenv
package for us. All we need to do is create a .env
file in our root folder.
The mnemonic phrase for our wallet can be found in our MetaMask settings under the “Security & Privacy” heading. The Project ID can be found under the “Keys” heading in our Infura project settings. Our .env
file will look something like this:
MNEMONIC="your twelve words here ..."
INFURA_PROJECT_ID="project ID # here"
The next thing we should do is update Truffle to use the latest version of Solidity. In our truffle-config.polygon.js
file, we can add the compiler version we wish to use.
Under the compilers section, within the solc curly braces, we’ll add the following: version: “0.8.11”
(the latest version of Solidity at the time of this writing).
It should look like this:
// Configure your compilers
compilers: {
solc: {
version: "^0.8.11"
}
},
SimpleStorage.sol
contracts, then you will need to update their Solidity version in order to compile properly. Simply change the pragma line to the following: pragma solidity >=0.4.21 <0.9.0;
The last bit of prep work is just to install the
npm install @openzeppelin/contracts
We are utilizing the OpenZeppelin library to make writing our smart contract easier and much safer. The contracts we will use have had thorough security audits and it ensures we are adhering to the industry standard for NFTs.
With our project set up properly, we are ready to get to the best part… Creating the NFT!
We will open up our preferred code editor from our project root folder. (I’m using vscode so the command is code .
). Create a new file under the contracts folder named PolygonNFT.sol
.
We’ll be using the following smart contract for our NFT:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
// Import the OpenZeppelin contracts
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
// Declare our contract and inherit from the OpenZeppelin ERC721 and Ownable contracts
contract PolygonNFT is ERC721, Ownable {
// Helpers for counting safely and converting data to strings
using Counters for Counters.Counter;
using Strings for uint256;
// State variable for storing the current token Id
Counters.Counter private _tokenIds;
// Map token Ids to token URI
mapping (uint256 => string) private _tokenURIs;
// ERC721 requires a name for the NFT collection and a symbol
constructor() ERC721("PolygonNFT", "PNFT") {}
// Set the URI (metadata) for tokenId
function _setTokenURI(uint256 tokenId, string memory _tokenURI)
internal
virtual
{
_tokenURIs[tokenId] = _tokenURI;
}
// Return the Token URI - Required for viewing properly on OpenSea
function tokenURI(uint256 tokenId)
public
view
virtual
override
returns (string memory)
{
require(_exists(tokenId), "Token does not exist");
string memory _tokenURI = _tokenURIs[tokenId];
return _tokenURI;
}
// Mint the NFT to the provided address, using the provided metadata URI
// Only the wallet address that deployed this contract can call this function
function mint(address recipient, string memory uri)
public
onlyOwner
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, uri);
return newItemId;
}
}
Pretty short, right?! That’s the beautiful thing about the composability of Web3. OpenZeppelin is doing most of the heavy lifting here with their ERC721 standard.
Basically, we are simply defining the NFT collection and symbol, supplying the NFT metadata via the _setTokenURI()
function, putting it together with our mint(
) function, and then providing a way for OpenSea or anyone else to retrieve the NFT metadata through our tokenURI()
function.
Deploying to the blockchain is pretty simple, but before we can do that, we need to modify the 1_deploy_simple_storage.js
file under the migrations folder. We just need to replace every instance of SimpleStorage
with whatever we named our NFT smart contract. In our case: PolygonNFT
. We should also rename the file to 1_deploy_polygon_nft.js
to eliminate any confusion.
Our migration file should now look like this:
const PolygonNFT = artifacts.require("PolygonNFT");
module.exports = function (deployer) {
deployer.deploy(PolygonNFT);
};
Before deploying our project to a live blockchain, it is common practice to test it on a local blockchain instance. We will use Ganache to do this. In a new terminal window, we will use this command to get it up and running:
ganache
The terminal should output some Available Accounts, their Private Keys, and so on.
To deploy our project to Ganache, open up the original terminal window. In the root directory of our project, type the command:
truffle migrate --config=truffle-config.polygon.js --network=development
Since there are two config files in our project, we need to specify which one to use. Truffle will use the truffle-config.js
file by default. After hitting enter, we can see that Truffle compiles our contracts and then starts the migration. If all goes successfully, you will receive a transaction receipt that will look something like this:
truffle console --config=truffle-config.polygon.js --network=development
Take note of the contract address
Now that our contract has migrated successfully to our local blockchain instance, we can use the
truffle console --config=truffle-config.polygon.js --network=development
Notice how the command prompt changes to truffle(development)>
. We are now ready to start interacting with our smart contract.
First, we need to get an instance of our contract. Copy the contract address from our transaction receipt to be used in this line of code:
let instance = await PolygonNFT.at("YOUR_CONTRACT_ADDRESS_HERE")
It will return undefined but if we type instance
it should output our contract ABI. Now we can call the mint function. We will need a contract address in which to send the NFT and our IPFS URI from earlier in the format: ipfs://YOUR_HASH_HERE
await instance.mint("YOUR_WALLET_ADDRESS", "YOUR_METADATA_URI")
It’s important to note that the mint function is being called by the address that deployed the contract because that is the one we logged into the Truffle Console with by default. The address we placed in the code above is the recipient of the NFT.
If all went well with our code above, we shouldn’t see any errors after we hit enter! We now know our contract works and we are able to mint an NFT from it. Unfortunately, since this is a local blockchain instance, we don’t have OpenSea or PolygonScan to verify that our
NFT actually exists. For that, we will deploy to the Mumbai Testnet.
The process to deploy to the Mumbai Testnet is very similar to launching on our Ganache blockchain instance. We just need to exit the Truffle console by typing ctrl+c
twice and then follow the exact same steps as above. The only difference is we will replace the network= development
with polygon_infura_testnet
.
Before moving forward, we need to make sure our Mnemonic phrase and Project ID are set up properly in our .env
file otherwise, the next steps won’t work. (See the steps in the Setting Up Our Project section.) With those in place, our commands will now look like this:
truffle migrate --config=truffle-config.polygon.js --network=polygon_infura_testnet
The migration will take a little bit longer than our Ganache instance. Once it’s complete it will output a transaction receipt that looks similar to our last one. This time we can verify that our contract was successfully migrated to the Mumbai Testnet by entering our contract address to __https://mumbai.polygonscan.com/__address/YOUR_ADDRESS_HERE
Excellent! Our contract is live on the Mumbai Testnet! Now let’s interact with it and actually mint our NFT!
We’ll access the Truffle Console using the same commands as before, again, replacing the network= development
with polygon_infura_testnet
.
truffle console --config=truffle-config-polygon.js --network=polygon_infura_testnet
Get an instance of our contract using the contract address that was output on our Mumbai Testnet transaction receipt:
let instance = await PolygonNFT.at("YOUR_CONTRACT_ADDRESS_HERE")
And now, for the moment we’ve been building towards this entire article! Mint our NFT to our desired address using our IPFS URI in the format: ipfs://YOUR_HASH_HERE
await instance.mint("YOUR_WALLET_ADDRESS", "YOUR_METADATA_URI")
If there aren't any errors, we can check our contract on mumbai.polygonscan again and see that our mint()
function was called! This time, we can verify that our NFT actually exists by checking it out on OpenSea.
We can easily check out our new NFT on OpenSea by copying our contract address into the search bar at Interacted With (To):
If our metadata was entered correctly we should now see our beautiful, new NFT live on OpenSea!
Congratulations! You have successfully deployed an NFT to the Polygon Mumbai Testnet! You uploaded your NFT image and metadata to IPFS using Infura’s API, set up your Truffle project and wrote a smart contract to create an NFT, tested deployment locally using Ganache, deployed to the Polygon Testnet with Truffle, Infura, and MetaMask, and finally, verified your NFT on OpenSea.
You now have the knowledge to deploy to the Polygon Mainnet, just make sure you have real MATIC tokens in your MetaMask wallet. Additionally, when deploying, be sure to use network=polygon_infura_mainnet
instead of polygon_infura_testnet
.
Your next steps would be to deploy to the Ethereum Mainnet to be able to bridge your NFT from Layer 2 to Layer 1. That is the topic for another article so be sure to check out the
Thank you for following along with this tutorial! Take care, and happy building!
(The code for this project can be found here:
Also Published here