How To Create NFT on Solana With IPFS
Published: 2022-04-08 08:09:37

In case you slept through all of 2021, NFTs (non-fungible tokens) are hot. They have evolved from “random jpegs” to self-contained applications, access tokens, communities, and more. With increased creativity and expanding support across multiple blockchains, NFTs don’t look to be going away any time soon.
While Ethereum might be the first blockchain one thinks of when talking about NFTs, there are plenty of other chains out there to consider. I previously wrote about creating NFTs on the Flow blockchain, but an increasingly popular choice is Solana. Solana recently overtook Ethereum in total transaction volume in November of 2021, and Rarity Sniper (a tool for creating rarity rankings on NFT collections) moved to support Solana in 2021 after initially supporting Ethereum only.
With that as our background, let’s take a look at how to create an NFT collection on Solana using Pinata and IPFS. This is an especially interesting tutorial because Solana has been closely linked to Arweave, but many projects prefer IPFS for its rapid content access and reliability. Metaplex, a project built to make creating NFT projects on Solana easier is has IPFS support baked in, and this includes the ability to use Pinata to store content and serve that content through a dedicated IPFS gateway.

## Setting Up The Development Environment
To begin, make sure you sign up for a Pinata account. You can test with a free account, but for a mainnet NFT launch, you may want to consider the Professional Plan with a Dedicated IPFS Gateway.
Once you’ve signed up for an account, you really just need to make sure you have the following installed (each is linked to the installed instructions of you need to install them):

- Node.js version 16.13.0 or newer is recommended
- Git version 2.32.0 or newer
- Yarn version 1.22.17 or newer
- ts-node version 10.4.0 or newer
- solana cli

For what it’s worth, we will be following much of the instructions on Metaplex’s website with some modifications that allow us to upload content to IPFS through Pinata.

## Preparing The Assets
This tutorial is not going to go through the process of generating assets for your NFT project. That’s a totally separate endeavor that has to happen before the contract can be deployed to Solana. So, assuming you have the art created, we’ll walk through how to prep this for upload to IPFS through Metaplex.
The first step is to create a folder for your NFT project assets. From the command line, that will look like this:

```
mkdir nft-project
```
Inside that folder, create another folder called assets. In the assets folder, you will add all of your images. It’s important that each of your images be labeled in a base 0 index format. That means the first image would be 0.png and the second would be 1.png and so on.
Once your images are in the folder, you will need to create the metadata for these images. If you have any experience with Ethereum NFT metadata, you’ll feel right at home with Solana NFT metadata. The structure is nearly identical. Let’s took a look at a basic JSON metadata file structure for NFTs on Solana:

```
{
    "name": "Number #0001",
    "symbol": "NB",
    "description": "Collection of 10 numbers on the blockchain. This is the number 1/10.",
    "image": "0.png",
    "attributes": [
        {"trait_type": "Layer-1", "value": "0"},
        {"trait_type": "Layer-2", "value": "0"}, 
        {"trait_type": "Layer-3", "value": "0"},
        {"trait_type": "Layer-4", "value": "1"}
    ],
    "properties": {
        "creators": [{"address": "N4f6zftYsuu4yT7icsjLwh4i6pB1zvvKbseHj2NmSQw", "share": 100}],
        "files": [{"uri": "0.png", "type": "image/png"}]
    },
    "collection": {"name": "numbers", "family": "numbers"}
}
```
Just like with the Ethereum metadata standard, the Solana metadata standard has a name, image, and description. Additionally, you can include attributes (just like with ETH), a symbol, and collection details. In Ethereum projects, the token symbol is generally assigned at contract deployment and not in the metadata. Another difference is the properties portion of the Solana metadata. This is required and it allows you to include an array of files for your NFTs. You have to have at least one file in that array which points to the same asset as the image property, but you can include other files that make up your entire NFT. This is a really interesting concept that should be explored further, but for the sake of this post, we will only be operating with single asset NFTs.

Ok, so we know we have images in the folder. We know those images need to be named in a specific way. And we know that we need JSON metadata files. How are we now going to get those JSON files created and added to the assets folder. You could do this manually, but with a 10,000 NFT project, that would be nearly impossible.

Let’s write a script to create the metadata files.
From your command line, make sure you are in the nft-project folder. We will create a new file called `metadata-generator.js` by running this command: `touch metadata-generator.js`.

In your code editor, open that new file. It’s empty, but we’ll fill it in now. We need to loop through all of the images in our assets folder and create a unique JSON file for each. We need to name and save that JSON file in the same assets folder. To do this, we will make use of the Node.js fs which is built into Node.js.

In your `metadata-generator.js` file add this code:

```
const fs = require('fs');
const imageDir = fs.readdirSync("./assets");
imageDir.forEach(img => {
  const metadata = {
    name: `Image ${img.split(".")[0]}`,
    description: "An image in the NFT collection",
    symbol: "YOUR NFT COLLECTION SHORT SYMBOL",
    image: img,
    seller_fee_basis_points: ROYALTIES_PERCENTAGE_BASIS_POINTS,
    properties: {
      files: [{ uri: img, "type": "image/png" }],
      category: "image",
      creators: [{
        address: "YOUR_SOL_WALLET_ADDRESS",
        share: 100
      }]
    }
  }
  fs.writeFileSync(`./assets/${img.split(".")[0].json}`, JSON.stringify(metadata))
});
```
To run your script and generate the metadata, you should run this command from the root your project folder: `node metadata-generator.js`.

When the script is finished, you’ll have an assets folder that has images and JSON files together.

## Using Metaplex
Metaplex is a tool that makes launching an NFT project on Solana much easier. Rather than having to write your own smart contract like you would have to do with Ethereum, Metaplex has pre-written contracts that projects can plug into. To make this work, Metaplex has to be able to access the files associated with the NFTs, then it needs to be able to upload those files and associated them with each token to be minted.

Metaplex has support for IPFS through a few services, but we’ll be focusing on using Pinata.
We will be following the standard Metaplex Candy Machine guide found here. The first step is going to be to clone Metaplex. Run the following command in your terminal:
```
git clone https://github.com/metaplex-foundation/metaplex.git ~/metaplex
```
We are cloning the directory into the home directory so that we don’t have to remember where the project was cloned. Inside the new metaplex repo is code to support the JavaScript CLI we will be making use of. So, we need to install dependencies for that CLI code.

`yarn install --cwd ~/metaplex/js/`

Now, let’s make sure the install worked. Remember, we installed ts-node? We’re going to use that now to run a command for the metaplex cli.

```
ts-node ~/metaplex/js/packages/cli/src/candy-machine-v2-cli.ts --version
```
This should print out a version if everything was installed properly. Now, we need to make sure the Solana CLI is working. Run this command to get the version number:

```
solana --version
```
If that works, you’re good to go. If not, double check that you have installed the Solana CLI Toolkit. Next, we’re going to use that CLI to create devnet wallet. The Solana devnet is where we can experiment without fear of incurring any real financial impact.
Run this command to create your new devnet wallet and store the keys:

```
solana-keygen new --outfile ~/.config/solana/devnet.json
```
Now, we can set the default keypair for our Solana CLI interactions:

```
solana config set --keypair ~/.config/solana/devnet.json
```
Finally, let’s let the CLI know that we intent to interact with the devnet:

```
solana config set --url https://api.devnet.solana.com
```
To confirm this all worked as expected, we can fetch the configuration for Solana like this:

```
solana config get
```
This should print out the configuration information. If so, you’re ready to get airdropped some SOL!
This is, of course, devnet SOL, but it’s still necessary to interact with the devnet. Run this command:

```
solana airdrop 2
```
When that is complete, you can run this command to verify your devnet wallet has the SOL:

```
solana balance
```
You should see that your devnet wallet has 2 SOL.
With all of the initial Solana and Metaplex installation and configuration done, we will move on to configuring out NFT drop.

## Drop Configuration
This is the most important part of the entire drop, so you should read about it in full here. Metaplex uses a tool called Candy Machine for NFT drops like this, and it’s important to make sure your project is configured properly.
In the root of your project, create a JSON file called `config.json`. Then, enter a configuration like this:

```
{
"price": 0.05,
"number": 100,
"gatekeeper": null,
"solTreasuryAccount": "YOUR DEVNET WALLET ADDRESS",
"splTokenAccount": null,
"splToken": null,
"goLiveDate": "01 Jan 2022 00:00:00 GMT",
"endSettings": null,
"whitelistMintSettings": null,
"hiddenSettings": null,
"storage": "pinata",
"ipfsInfuraProjectId": null,
"ipfsInfuraSecret": null,
"pinataJwt": "YOUR PINATA JWT TOKEN",
"pinataGateway": "YOUR PINATA GATEWAY URL",
"awsS3Bucket": null,
"noRetainAuthority": false,
"noMutable": false
}
```
This may feel like a lot, but this is actually just the minimum configuration required. There’s a lot more you can add. Don’t worry, though. We’re keeping it simple. You only need to change five things in this file. The first change is to set the storage property to point to pinata.
Then, you need to decide if you are going to use a Pinata Dedicated Gateway or not. Doing so will give you and anyone who loads your NFTs the best possible performance. But, while recommended it’s not required. If you do decide to get a Dedicated Gateway, you’ll need to upgrade to the Professional Plan in Pinata.

Next, you need to get a JWT for Pinata API access. To do so, click the dropdown menu in the top-right, click API Keys, then generate a new API Key. You can either select specific endpoints the key should have access to (pinFileToIPFS) or you can make it an admin key which has full access. This is up to your security preferences, but the key must have access to pinFileToIPFS. You can read more about scoped API keys here.

When you’ve created your key a modal will display your API Key, API Secret, and your JWT. We only need the JWT, so copy that and paste it into the pinataJwt configuration file property’s value.
If you created a Dedicated Gateway, you can paste in your Dedicated Gateway’s URL in the pinataGateway section of the config file. If you did not upgrade and do not have a Dedicated Gateway, you should update that property to look like this:

```
pinataGateway: null
```
Finally, you just need to set the number of items in your NFT collection for the number property and the price in SOL for the price property.
That’s all you need. Now, there’s a lot more you can do, but I want to point out something that a lot of projects probably want to do, and that’s hide the NFT assets until after a reveal date. This is possible with Metaplex and Candy Machine with hidden settings. You can read about that here. We won’t be doing that in this tutorial. The NFT project in this tutorial will represent a mint with immediate reveal.
The final thing you’ll want to change in the config file is the solTreasuryAccount. This should probably point to your devnet wallet address, but it can be any Solana wallet address. To get your devnet wallet address, you can run this in the command line:

```
solana address
```
We’re about ready to upload some assets and create our NFT contract!

## Uploading To Pinata and Solana
We’ve finished all the prep work. It’s time to upload. Fortunately, this is really simple.
In the root of the project directory, run this command in the command line:
```
ts-node ~/metaplex/js/packages/cli/src/candy-machine-v2-cli.ts upload \
    -e devnet \
    -k ~/.config/solana/devnet.json \
    -cp config.json \
    -c nft-project \
    ./assets
```
This command will take your config file parse it so that Metaplex knows how to upload your files and how to get the NFT mint info onto Solana. You’ll see in your command line each upload of your asset folder’s metadata and your asset folders images. Depending on how many files you are uploading this process could take a long time.

When the process is done, you’ll need to do one more thing. You’ll need to verify the upload. This helps you know if everything is ready to go and if your NFTs can be minted. You just need to run this command:

```
ts-node ~/metaplex/js/packages/cli/src/candy-machine-v2-cli.ts verify_upload \
    -e devnet \
    -k ~/.config/solana/devnet.json \
    -c nft-project
```
If all goes well, you should see a message that includes Ready to deploy!. This means your project is ready to go.

Your NFT drop is ready for minting! But what do we do now? You probably want to create a website to allow for minting. Fortunately, the Metaplex code base has us covered.

## Creating a Minting App
Remember, we cloned the the full repository for Metaplex? We cloned that into our home directory. So, from your command line, let’s change to our home directory and into the metaplex project, directly into the sample React app:

```
cd ~/metaplex/js/packages/candy-machine-ui
```
You’ll need to install the dependencies like this:
yarn install
Then, open the project in your code editor. You’ll see a .env file in that folder. You should update it to look like this:

```
REACT_APP_CANDY_MACHINE_ID=YOUR CANDY MACHINE ID
REACT_APP_SOLANA_NETWORK=devnet
REACT_APP_SOLANA_RPC_HOST=https://api.devnet.solana.com
```
Remember, we used the Candy Machine ID to look up our project on the Solana Explorer? We are going to paste that same ID where is says YOUR CANDY MACHINE ID.
Now, you can run the app with:

```
yarn start
```

When the app load, you should see:

!(https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rz4ksvhvusoqa0p716kg.PNG)

You will be able to choose a Solana wallet to connect with. Regardless of which wallet you choose, you’ll need to make sure it has some devnet SOL in it. Remember when we airdropped ourselves some devnet SOL? We can also do that with another address. Connect your wallet, make sure you’re on devnet, then copy the wallet address. From the command line run:

```
solana airdrop 2 YOUR_ADDRESS
```
Once this is complete, you should have SOL in your browser-based wallet and can now mint one of your new NFTs. To do so, just click the Mint button:


!(https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g8c51uq8cmk7z8sj7l6p.PNG)

You’ll need to confirm the transaction. When you do so, the minting process should only take a few seconds. Once it’s complete, your new NFT should show up in your wallet’s NFT collection.

The image you see of your NFT is being loaded from Pinata. Depending on if you provided a Dedicated Gateway URL in your config file, the NFT is loading through your custom gateway or a public IPFS gateway. Note: If you provided a Dedicated Gateway URL in the config, your NFT asset will not only load quicker but will be cached so that all subsequent loads are instant. You can see this in the example below using Phantom Wallet. The two NFTs that are stuck loading are using a public IPFS gateway. The two that load almost instantly are loaded through a Dedicated Gateway.



!(https://dev-to-uploads.s3.amazonaws.com/uploads/articles/73vilc2t12si99e4qdoo.gif)



Wrapping Up
The benefits of using IPFS for the storage of NFT assets are well-documented. IPFS offers quick retrieval (especially when using a Pinata Dedicated Gateway), IPFS offers content addressability, and IPFS is portable. Ownership of NFTs can transfer in both the token sense and the media sense.
However, it has not always been easy to use IPFS with Solana NFT projects. Hopefully, this tutorial helps change that. Hopefully, it can lead to more creative projects.
Happy pinning, and happy drop days!