Skip to main content

How to Mint an NFT on Solana

Updated on
Mar 21, 2023

How to Mint an NFT on Solana

13 min read

Before you start this guide

This guide will give you an in-depth walkthrough of how to mint an NFT on Solana through a series of technical steps. If you’d like to accomplish this task quicker and leave the heavy lifting to us, we recommend the Crossmint NFT Mint API [mainnet] add-on. By using the Crossmint NFT Mint API [mainnet], you can airdrop NFTs to Solana users and email addresses with a single POST request. Use the NFT Mint API to create collections and mint NFTs easily!

Try the Crossmint NFT Mint API [mainnet]

Overview

Updated at: April 10, 2022

Welcome to another QuickNode guide on Solana - the up-and-coming blockchain that seeks to solve the scalability issues of Ethereum. We will be walking through step-by-step how to create an NFT on Solana. NFT, short for Non Fungible Token, is a cryptographically unique hash that is unreplicable. When you create an NFT, it is similar to creating an ERC20 token; however, the critical difference is only one token is ever issued. In this guide, we will be programmatically creating 2 separate accounts with wallets, one which will mint the NFT and another which will receive it. We will then be writing the code that will accomplish the minting and sending of the NFT on Solana. If you get stuck at any point, feel free to refer to the complete solution code at the end of the guide. Let us get to minting!

Prerequisites:

  •  NodeJS installed
  •  Terminal/CLI familiarity
  •  A Text editor
  •  TypeScript installed 

What is Solana?

Solana's goal is singular in nature. That goal is to scale the blockchain for global adoption. Solana Labs, the developers of the Solana Protocol, are doing a few different things to achieve that dream. 

Blockchain technology has a few knobs when it comes to tuning performance. One of which is the consensus mechanism. This is how nodes communicate together to arrive at an identical conclusion. Bitcoin uses Proof of Work or PoW, BNB Smart Chain, also known as BSC, uses a Proof of Staked Authority or PoSA. And Ethereum is migrating to Proof of Stake aka PoS. As you can tell, consensus is by no means a solved game at this point in time.

Solana uses a consensus called Proof of History. Proof of History works through a time-stamping solution; each transaction has a time stamp allocated to it that allows it to be verified as a legitimate transaction by the rest of the network in mere fractions of a second. Solana has a breakdown of the eight technologies that they believe position themselves as the fastest, most scalable, and most secure blockchain in the game.

Setting up the Project Locally

Open Terminal and navigate to a folder you'd like to create your project in. Next, run the following commands in this exact order:

mkdir SolanaNFT
npm install --prefix ./SolanaNFT @solana/web3.js @solana/spl-token
cd SolanaNFT
touch index.ts

The first command creates a new project directory called SolanaNFT. With npm install --prefix ./SolanaNFT @solana/web3.js @solana/spl-token, we're installing Solana's JavaScript API, @solana/web3.js and a TypeScript library @solana/spl-token, for interacting with the SPL Token program. Finally, with touch index.ts, we create a Typescript file, index.ts, where we'll be writing all of our code.

Connecting to Solana

Open the SolanaNFT project directory within your editor of choice, and let us start writing some code to connect to Solana!

Within the index.ts file, we'll want to start by importing all of the functionality we'll need from @solana/web3.js and @solana/spl-token. Add the following two import statements:

import { clusterApiUrl, Connection, Keypair, LAMPORTS_PER_SOL } from  "@solana/web3.js";
import { createMint, getOrCreateAssociatedTokenAccount, mintTo, setAuthority, transfer } from "@solana/spl-token";

> Note: If you are seeing errors from these lines of code, then you likely do not have the libraries installed. Ensure you're in the correct directory and install them again.  

Below the import statements, add this function: 

(async () => {
// Connect to cluster
const connection = new Connection(clusterApiUrl('devnet'), "confirmed");
})

Here we are instantiating a new connection instance. This requires two arguments, the first being a URL endpoint that points to the Solana Network. In our case, clusterApiUrl('devnet') is a convenient method to point to the public endpoint of the Solana Devnet which is what we will be using in this tutorial. Solana has 3 different networks: the mainnet, the testnet, and the devnet. The devnet is a low-risk environment where you can "air drop" SOL tokens to yourself. 

With a connection established, we can now create the NFT and perform the other associated steps. 

> Note: Each of the code blocks we write going forward should be placed directly underneath the previous block, all within the curly braces of the top-level async function. We'll provide you with the full code at the end to check yours against. 

Creating a New Wallet and Airdropping SOL

The first task we'll need to accomplish is creating an account with a wallet and funding it. We'll also need to ensure that each step has completed successfully before moving on to the next. Here's what this code will look like:

// Generate a new wallet keypair and airdrop SOL
const fromWallet = Keypair.generate();

const fromAirdropSignature = await connection.requestAirdrop(
fromWallet.publicKey,
LAMPORTS_PER_SOL
);

// Wait for airdrop confirmation
await connection.confirmTransaction(fromAirdropSignature);

Remember to add this code within the async function. Let us now break it down by line. 

Line 2: We're using the Keypair class we imported earlier to generate a new keypair by calling on a generate() method. This creates a new pair of a public and secret keys and stores it in fromWallet

Line 4: We're requesting an airdrop into our wallet. The requestAirdrop() method takes a public key and the amount of lamports in SOL you would like to receive. Lamports are Solana's equivalent to wei, the smallest amount that a SOL can be broken into. Most methods that require a number will default to the lamport measurement. In our case, the LAMPORTS\_PER\_SOL is a constant that represents 1 SOL worth of lamports. 

Line 9: To confirm that the airdrop has been successful, we're utilizing the confirmTransaction method and awaiting its success. This call allows us to pass in a signed transaction as an argument and have the program wait until it has been confirmed before moving on to other portions of the code. This is important as our next step will have to pay a fee, and we will require the airdropped funds.

Creating a New Token Mint

We'll now need to create a new token mint and retrieve our token account. 

const mint = await createMint(
connection,
fromWallet, // Payer of the transaction
fromWallet.publicKey, // Account that will control the minting
null, // Account that will control the freezing of the token
0 // Location of the decimal place
);

The createMint function will be what creates our actual token. It takes 6 arguments:

      1. The connection to the Solana Network. (connection)

      2. The account that will pay the fee. (fromWallet)

      3. The public key of the account that has the authority to mint tokens of this type. (fromWallet.publicKey)

      4. The public key of the account that has the authority to freeze tokens of this type. This argument is optional. (null)

      5. The location of the decimal place for the token. 

For more information on this function and other spl-token functions used in this guide, visit this page in Solana-labs.github.io.

Once the token mint is created, we need to grab the token account from the fromWallet Solana address. If it does not exist, we must create it. For this, we will utilize the getorCreateAssociatedTokenAccount() function, passing in most of the previous values, and storing it in fromTokenAccount

// Get the token account of the "fromWallet" Solana address. If it does not exist, create it.
const fromTokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
fromWallet,
mint,
fromWallet.publicKey
);

You can think about the chain of custody like this: the NFT resides in the account, and your wallet owns this account. 

> Keys -> Wallet -> Account -> NFT (top to down)

Creating an Account with a Wallet to Send the NFT to

We have an account to send the NFT from; now, we need an account to send the NFT to. The code to achieve this should look very familiar as we'll be leveraging the same functions and variables to generate a new wallet and grab the token mint as we've done previously. 

// Generate a new wallet to receive the newly minted token
const toWallet = Keypair.generate();

// Get the token account of the "toWallet" Solana address. If it does not exist, create it.
const toTokenAccount = await mint.getOrCreateAssociatedAccountInfo(
connection,
fromWallet,
mint,
toWallet.publicKey
);

The above code block creates a wallet with a separate set of public/secret keys and then creates an account linking the mint variable to our newly created wallet.

Minting the NFT and Sending It

Now it's time to mint an NFT and send it to someone! Take a moment to look at the code below that achieves this and read the comments to understand what each function does. 

// Minting 1 new token to the "fromTokenAccount" account we just returned/created.
let signature = await mintTo(
connection,
fromWallet, // Payer of the transaction fees
mint, // Mint for the account
fromTokenAccount.address, // Address of the account to mint to
fromWallet.publicKey, // Minting authority
1 // Amount to mint
);

await setAuthority(
connection,
fromWallet, // Payer of the transaction fees
mint, // Account
fromWallet.publicKey, // Current authority
0, // Authority type: "0" represents Mint Tokens
null // Setting the new Authority to null
);

signature = await transfer(
connection,
fromWallet, // Payer of the transaction fees
fromTokenAccount.address, // Source account
toTokenAccount.address, // Destination account
fromWallet.publicKey, // Owner of the source account
1 // Number of tokens to transfer
);


console.log("SIGNATURE", signature);
})();

We'll briefly touch on the setAuthority() function as it is one of the most crucial parts. This function will revoke minting privileges and ensure that we can not create additional tokens of this type. Note that this action cannot be undone.

To execute the program, run the following commands one after another: 

tsc index.ts

node index.js

These two commands will run the TypeScript file, generate a JavaScript file with the same name, and run that file. You should see a signature logged in the terminal after some time.

If you visit the Solana Explorer, you should see your signature. It will look something like this:

Here's the complete code you should have: 

import { clusterApiUrl, Connection, Keypair, LAMPORTS_PER_SOL } from  "@solana/web3.js";
import { createMint, getOrCreateAssociatedTokenAccount, mintTo, setAuthority, transfer } from "@solana/spl-token";

(async () => {
// Connect to cluster
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");

// Generate a new wallet keypair and airdrop SOL
const fromWallet = Keypair.generate();
const fromAirdropSignature = await connection.requestAirdrop(
fromWallet.publicKey,
LAMPORTS_PER_SOL
);

// Wait for airdrop confirmation
await connection.confirmTransaction(fromAirdropSignature);

// Create a new token
const mint = await createMint(
connection,
fromWallet, // Payer of the transaction
fromWallet.publicKey, // Account that will control the minting
null, // Account that will control the freezing of the token
0 // Location of the decimal place
);

// Get the token account of the fromWallet Solana address. If it does not exist, create it.
const fromTokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
fromWallet,
mint,
fromWallet.publicKey
);

// Generate a new wallet to receive the newly minted token
const toWallet = Keypair.generate();

// Get the token account of the toWallet Solana address. If it does not exist, create it.
const toTokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
fromWallet,
mint,
toWallet.publicKey
);

// Minting 1 new token to the "fromTokenAccount" account we just returned/created.
let signature = await mintTo(
connection,
fromWallet, // Payer of the transaction fees
mint, // Mint for the account
fromTokenAccount.address, // Address of the account to mint to
fromWallet.publicKey, // Minting authority
1 // Amount to mint
);

await setAuthority(
connection,
fromWallet, // Payer of the transaction fees
mint, // Account
fromWallet.publicKey, // Current authority
0, // Authority type: "0" represents Mint Tokens
null // Setting the new Authority to null
);

signature = await transfer(
connection,
fromWallet, // Payer of the transaction fees
fromTokenAccount.address, // Source account
toTokenAccount.address, // Destination account
fromWallet.publicKey, // Owner of the source account
1 // Number of tokens to transfer
);

console.log("SIGNATURE", signature);

})();

Conclusion

If you made it this far, you learned a few things from this tutorial and some from our other Solana guides, so congratulations! Having gone through this one, you have successfully created an NFT on the Solana blockchain. The next step for you to do would be to link this unique one-of-a-kind token, to some asset. In the market right now, it is typically a picture that has been randomly generated with various properties or a one-off piece of art. If you would like to learn how to do exactly that, you can learn how to do that in this tutorial!

Subscribe to our newsletter for more articles and guides on Ethereum. If you have any feedback, feel free to reach out to us via Twitter. You can always chat with us on our Discord community server, featuring some of the coolest developers you will ever meet :)

Share this guide