Introduction Non-Fungible Tokens (NFTs) have exploded in popularity over the last year. And with NFTs being used for , , lifestyle brands, sports, games, , and more, they won’t be going away anytime soon. But the current state of NFTs has a few problems - notably, minting on Ethereum can be prohibitively expensive and the metadata behind those NFTs is often centralized. art PFPs the metaverse 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 as our development environment and to connect and interact with the blockchain and IPFS. Truffle Infura A Quick Overview of NFTs 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, are - one token is not replaceable by another, and they may have varying values (1 != 1). An NFT is an undisputable representation of digital ownership. It can be used to represent any manner of digital property such as images, videos, music, and so much more. NFTs not fungible NFTs on Ethereum and Layer Two Most NFTs are created on the , as it allows for the greatest sense of security and decentralization. However, for the average person, there is a major issue: Transacting on Layer 1 of the Ethereum Network is expensive. Ethereum Network Over the course of 2021, several solutions to this problem have gained popularity. Layer 2 solutions such as , , and the are here to provide a more user-friendly experience. Transaction fees on these networks are a fraction of what they would be on Layer 1, and in most cases, significantly faster. Arbitrum zkSync Polygon Network 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 , it’s more fun to actually deploy one yourself and you will get a better understanding of how it all works behind the scenes. ConsenSys 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. The Project What You Will Learn Upload the NFT image and metadata to IPFS via Infura Set up the Polygon Network in MetaMask and get test funds Set up Truffle Development Environment Write an NFT smart contract Test deployment locally using Ganache Deploy to Polygon testnet using Truffle console and Infura Verify the NFT on opensea.io What You Will Need - installation and package management NodeJS / NPM - development environment Truffle - local blockchain instance for testing Ganache - used to interact with IPFS and Polygon Networks Infura account - supply funds to process transactions MetaMask account Additional Resources Polygon Docs Truffle Docs Infura Docs MetaMask Docs Setting Up Accounts 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. Get an Infura Account 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 and set up a free account. After verifying our email address, we can access the dashboard, where we can make new projects. The Polygon Network isn’t enabled by default, however. infura.io We can add Polygon to our plan by selecting the “ ” tab on the left-hand side of the screen and scrolling down to “ ”. Next, we select “ ” and go through the prompt to add it. Add-Ons Network Add-Ons Polygon PoS Add-On Now we’re ready to create a new project. Let’s head back to the dashboard and click on the “ ” button. We’ll select Ethereum as the product and name our project . Create New Project Polygon-NFT Once our project is created, there are several things to note: The Project ID The project secret Endpoints selection Our endpoint link Let’s change our Endpoint from to , 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. MAINNET POLYGON MUMBAI IPFS via Infura 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 into the folder where our image is stored. cd 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" Note : should be the name of the image file in the current folder. So in our case . @myfile @polygon-nft.jpg and will be the keys provided in the settings for our Infura project. PROJECT_ID PROJECT_SECRET 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: https://ipfs.io/ipfs/YOUR_HASH_HERE 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 . Now that our image is stored on IPFS, let’s get the metadata stored there as well. According to , we will want our metadata in JSON format like so: unpin it standards by OpenSea { "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 we’ll insert the actual Hash for our image that we just verified. We’ll save the file as . 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. YOUR_HASH_HERE nft-metadata.json 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. Setting Up MetaMask 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 and download the extension for our browser. When using a crypto wallet like MetaMask, it’s a good idea to understand . metamask.io wallet safety : Never share your seed phrase with anyone! Whoever has your seed phrase can withdraw your tokens. Remembe r 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 . add both the Polygon Mainnet and Mumbai Testnet We will need to enter in the following information: Mumbai Testnet Network Name: Mumbai (Polygon Testnet) New RPC URL: https://rpc-mumbai.maticvigil.com/ Chain ID: 80001 Currency Symbol: MATIC Block Explorer URL: <https://mumbai.polygonscan.com/](https://mumbai.polygonscan.com/) Polygon Mainnet Network Name: Polygon Mainnet New RPC URL: https://polygon-rpc.com/ Chain ID: 137 Currency Symbol: MATIC Block Explorer URL: <https://polygonscan.com/](https://polygonscan.com/) 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 which is a faucet that provides test funds for development. We need to select the Mumbai Network and then paste our address into the text field. Once we hit “Submit” we should see some MATIC tokens in our MetaMask wallet after a minute or two. https://faucet.polygon.technology/ : If you have multiple accounts in your MetaMask wallet, Truffle uses the first one by default. So be sure to add the test MATIC tokens to the first account in your list. Note Now that we have loaded our wallet with some test tokens, we are ready to move on. Install Truffle and Ganache Truffle 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, . check out this link 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 afterward to ensure it was installed correctly. If there are any errors, be sure to add the npm modules to your path. truffle version Ganache 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 , but we will use the terminal version for the rest of this tutorial. It can be installed using this command: here npm install ganache --global Again, we can verify proper installation by checking the version: ganache --version Setting Up Our Project 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 “ ” for getting started quickly. They are essentially boilerplates that include helpful modules, pre-made smart contracts, frontend views, and more. For this project, we will utilize Truffle’s . Installation is simple; we just need to type this command: boxes Polygon Box 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 file, , the under the folder, and files, which can be found in the folder. README.md truffle-config.polygon.js 1_deploy_simple_storage.js migrations SimpleStorage.sol contracts You will likely see an and folder under . 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 contracts since we won’t be using them. ethereum polygon contracts SimpleStorage.sol Taking a quick glance through the file, we can see that in order to deploy to the Polygon Network, we will have to add two things: README.md The Mnemonic phrase (seed phrase) of the wallet we are using The Infura Project ID We’ll want to store these in a file and make sure it’s added to our so we don’t accidentally upload these secrets if storing our project on a public repository. .env .gitignore Downloading the Polygon Truffle Box also installed the package for us. All we need to do is create a file in our root folder. .dotenv .env The mnemonic phrase for our wallet can be found in our MetaMask settings under the “ ” heading. The Project ID can be found under the “Keys” heading in our Infura project settings. Our file will look something like this: Security & Privacy .env 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 file, we can add the compiler version we wish to use. truffle-config.polygon.js Under the section, within the curly braces, we’ll add the following: (the latest version of Solidity at the time of this writing). compilers solc version: “0.8.11” It should look like this: // Configure your compilers compilers: { solc: { version: "^0.8.11" } }, : If you didn’t delete the contracts, then you will need to update their Solidity version in order to compile properly. Simply change the pragma line to the following: Note SimpleStorage.sol pragma solidity >=0.4.21 <0.9.0; The last bit of prep work is just to install the , as we will import several contracts. We can install it by typing this command in the root directory of our project: OpenZeppelin contract library 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! The Smart Contract We will open up our preferred code editor from our project root folder. (I’m using vscode so the command is ). Create a new file under the folder named . code . contracts PolygonNFT.sol If you need to brush up on the Solidity coding language, check out my . : Note Solidity Basics Series 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 function, putting it together with our ) function, and then providing a way for OpenSea or anyone else to retrieve the NFT metadata through our function. _setTokenURI() mint( tokenURI() Testing Deployment With Ganache Deploying to the blockchain is pretty simple, but before we can do that, we need to modify the file under the folder. We just need to replace every instance of with whatever we named our NFT smart contract. In our case: . We should also rename the file to to eliminate any confusion. 1_deploy_simple_storage.js migrations SimpleStorage PolygonNFT 1_deploy_polygon_nft.js 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 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-config.js 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 to interact with it. The Truffle Console is a powerful tool that allows us to use javascript to interact directly with our contract without having to set up a frontend. To use it, type the command: Truffle Console truffle console --config=truffle-config.polygon.js --network=development Notice how the command prompt changes to . We are now ready to start interacting with our smart contract. truffle(development)> 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 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: instance 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. Deploy to Polygon 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 twice and then follow the exact same steps as above. The only difference is we will replace the network= with . ctrl+c development polygon_infura_testnet Before moving forward, we need to make sure our Mnemonic phrase and Project ID are set up properly in our 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: .env 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 __ __address/ https://mumbai.polygonscan.com/ 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= with . development 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 function was called! This time, we can verify that our NFT actually exists by checking it out on OpenSea. mint() Verify Our NFT On OpenSea We can easily check out our new NFT on OpenSea by copying our contract address into the search bar at . Ethereum addresses have , one that is and one that is not. The difference being the non-checksummed version is all lowercase. We may need to use the non-checksummed version to find our NFT on OpenSea. We can get this address by clicking on the Txn Hash on mumbai.polygonscan and then copying the address across from https://testnets.opensea.io/ two versions checksummed Interacted With (To): If our metadata was entered correctly we should now see our beautiful, new NFT live on OpenSea! Conclusion 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 instead of . network=polygon_infura_mainnet 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 in the meantime. Polygon Docs Thank you for following along with this tutorial! Take care, and happy building! (The code for this project can be found here: ) https://github.com/paul-mcaviney/polygon-nft-project Also Published here