Javascript
How to Create a NFT On Solana
Last updated June 18, 2024
This is an intial guide on how to create an NFT on the Solana blockchain with the Metaplex Token Metadata protocol.
Prerequisite
- Code Editor of your choice (recommended Visual Studio Code)
- Node 18.x.x or above.
Initial Setup
This guide will run through creation of an NFT with Javascript based on a single file script. You may need to modify and move functions around to suit your needs.
Initializing
Start by initializing a new project (optional) with the package manager of your choice (npm, yarn, pnpm, bun) and fill in required details when prompted.
npm init
Required Packages
Install the required packages for this guide.
1import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
2import { generateSigner, percentAmount } from '@metaplex-foundation/umi';
3import {
4 mplTokenMetadata,
5 createNft,
6 fetchDigitalAsset,
7} from '@metaplex-foundation/mpl-token-metadata';
8
9// Create Umi instance with the Token Metadata plugin
10const umi = createUmi('https://api.devnet.solana.com')
11 .use(mplTokenMetadata());
12
13// Connect your wallet (keypair or wallet adapter)
14// For keypair: umi.use(keypairIdentity(keypair))
15// For wallet adapter: umi.use(walletAdapterIdentity(wallet))
16
17// Generate a new mint keypair
18const mint = generateSigner(umi);
19
20// Create an NFT
21await createNft(umi, {
22 mint,
23 name: 'My NFT',
24 uri: 'https://example.com/my-nft.json',
25 sellerFeeBasisPoints: percentAmount(5.5),
26}).sendAndConfirm(umi);
27
28// Fetch the NFT data
29const asset = await fetchDigitalAsset(umi, mint.publicKey);
30
31console.log('NFT created successfully!');
32console.log('Mint address:', mint.publicKey);
33console.log('Name:', asset.metadata.name);
34console.log('URI:', asset.metadata.uri);
1import {
2 appendTransactionMessageInstructions,
3 createSolanaRpc,
4 createSolanaRpcSubscriptions,
5 createTransactionMessage,
6 generateKeyPairSigner,
7 getSignatureFromTransaction,
8 type Instruction,
9 type TransactionSigner,
10 pipe,
11 sendAndConfirmTransactionFactory,
12 setTransactionMessageFeePayer,
13 setTransactionMessageLifetimeUsingBlockhash,
14 signTransactionMessageWithSigners,
15} from '@solana/kit';
16import {
17 createNft,
18 fetchDigitalAsset,
19} from '@metaplex-foundation/mpl-token-metadata-kit';
20
21// Create RPC connection
22const rpc = createSolanaRpc('https://api.devnet.solana.com');
23const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.devnet.solana.com');
24
25// Generate keypairs (or load from wallet)
26const authority = await generateKeyPairSigner();
27const mint = await generateKeyPairSigner();
28
29// Helper function to send and confirm transactions
30// Works with any signer type - signers are automatically extracted from instruction accounts
31async function sendAndConfirm(options: {
32 instructions: Instruction[];
33 payer: TransactionSigner;
34}) {
35 const { instructions, payer } = options;
36 const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
37
38 const transactionMessage = pipe(
39 createTransactionMessage({ version: 0 }),
40 (tx) => setTransactionMessageFeePayer(payer.address, tx),
41 (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
42 (tx) => appendTransactionMessageInstructions(instructions, tx),
43 );
44
45 // Sign with all signers attached to instruction accounts
46 const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
47
48 const sendAndConfirmTx = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
49 await sendAndConfirmTx(signedTransaction, { commitment: 'confirmed' });
50
51 return getSignatureFromTransaction(signedTransaction);
52}
53
54// Create and mint an NFT using the helper function
55const [createIx, mintIx] = await createNft({
56 mint,
57 authority,
58 payer: authority,
59 name: 'My NFT',
60 uri: 'https://example.com/my-nft.json',
61 sellerFeeBasisPoints: 550, // 5.5%
62 tokenOwner: authority.address,
63});
64
65// Send transaction
66const sx = await sendAndConfirm({
67 instructions: [createIx, mintIx],
68 payer: authority,
69});
70
71// Fetch the NFT data
72const asset = await fetchDigitalAsset(rpc, mint.address);
73
74console.log('NFT created successfully!');
75console.log('Mint address:', mint.address);
76console.log('Signature:', sx);
77console.log('Name:', asset.metadata.name);
78console.log('URI:', asset.metadata.uri);
Setting up the SDK
1import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
2import { generateSigner, percentAmount } from '@metaplex-foundation/umi';
3import {
4 mplTokenMetadata,
5 createNft,
6 fetchDigitalAsset,
7} from '@metaplex-foundation/mpl-token-metadata';
8
9// Create Umi instance with the Token Metadata plugin
10const umi = createUmi('https://api.devnet.solana.com')
11 .use(mplTokenMetadata());
12
13// Connect your wallet (keypair or wallet adapter)
14// For keypair: umi.use(keypairIdentity(keypair))
15// For wallet adapter: umi.use(walletAdapterIdentity(wallet))
16
17// Generate a new mint keypair
18const mint = generateSigner(umi);
19
20// Create an NFT
21await createNft(umi, {
22 mint,
23 name: 'My NFT',
24 uri: 'https://example.com/my-nft.json',
25 sellerFeeBasisPoints: percentAmount(5.5),
26}).sendAndConfirm(umi);
27
28// Fetch the NFT data
29const asset = await fetchDigitalAsset(umi, mint.publicKey);
30
31console.log('NFT created successfully!');
32console.log('Mint address:', mint.publicKey);
33console.log('Name:', asset.metadata.name);
34console.log('URI:', asset.metadata.uri);
1import {
2 appendTransactionMessageInstructions,
3 createSolanaRpc,
4 createSolanaRpcSubscriptions,
5 createTransactionMessage,
6 generateKeyPairSigner,
7 getSignatureFromTransaction,
8 type Instruction,
9 type TransactionSigner,
10 pipe,
11 sendAndConfirmTransactionFactory,
12 setTransactionMessageFeePayer,
13 setTransactionMessageLifetimeUsingBlockhash,
14 signTransactionMessageWithSigners,
15} from '@solana/kit';
16import {
17 createNft,
18 fetchDigitalAsset,
19} from '@metaplex-foundation/mpl-token-metadata-kit';
20
21// Create RPC connection
22const rpc = createSolanaRpc('https://api.devnet.solana.com');
23const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.devnet.solana.com');
24
25// Generate keypairs (or load from wallet)
26const authority = await generateKeyPairSigner();
27const mint = await generateKeyPairSigner();
28
29// Helper function to send and confirm transactions
30// Works with any signer type - signers are automatically extracted from instruction accounts
31async function sendAndConfirm(options: {
32 instructions: Instruction[];
33 payer: TransactionSigner;
34}) {
35 const { instructions, payer } = options;
36 const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
37
38 const transactionMessage = pipe(
39 createTransactionMessage({ version: 0 }),
40 (tx) => setTransactionMessageFeePayer(payer.address, tx),
41 (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
42 (tx) => appendTransactionMessageInstructions(instructions, tx),
43 );
44
45 // Sign with all signers attached to instruction accounts
46 const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
47
48 const sendAndConfirmTx = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
49 await sendAndConfirmTx(signedTransaction, { commitment: 'confirmed' });
50
51 return getSignatureFromTransaction(signedTransaction);
52}
53
54// Create and mint an NFT using the helper function
55const [createIx, mintIx] = await createNft({
56 mint,
57 authority,
58 payer: authority,
59 name: 'My NFT',
60 uri: 'https://example.com/my-nft.json',
61 sellerFeeBasisPoints: 550, // 5.5%
62 tokenOwner: authority.address,
63});
64
65// Send transaction
66const sx = await sendAndConfirm({
67 instructions: [createIx, mintIx],
68 payer: authority,
69});
70
71// Fetch the NFT data
72const asset = await fetchDigitalAsset(rpc, mint.address);
73
74console.log('NFT created successfully!');
75console.log('Mint address:', mint.address);
76console.log('Signature:', sx);
77console.log('Name:', asset.metadata.name);
78console.log('URI:', asset.metadata.uri);
Creating the NFT
Uploading the Image
The first thing we need to do is to an image that represents the NFT and makes it recognisable. This can be in the form of jpeg, png or gif.
Umi comes with downloadable storage plugins that allow you to upload to storage solutions such Arweave, NftStorage, AWS, and ShdwDrive. At start of this guide we had installed the irsyUploader() plugin which stores content on the Arweave blockchain so we'll stick with using that.
Local script/Node.js
This example is using a localscript/node.js approach using Irys to upload to Arweave. If you wish to upload files to a different storage provider or from the browser you will need to take a different approach. Importing and using fs won't work in a browser scenario.
Error: Could not load example "token-metadata/upload-assets"
Make sure the file exists at: src/examples/token-metadata/upload-assets/index.js
Cannot find module './token-metadata/upload-assets/index.js'
Uploading the Metadata
Once we have a valid and working image URI we can start working on the metadata for our NFT.
The standard for offchain metadata for a fungilbe token is as follows;
{
"name": "My NFT",
"description": "This is an NFT on Solana",
"image": "https://arweave.net/my-image",
"external_url": "https://example.com/my-nft.json",
"attributes": [
{
"trait_type": "trait1",
"value": "value1"
},
{
"trait_type": "trait2",
"value": "value2"
}
],
"properties": {
"files": [
{
"uri": "https://arweave.net/my-image",
"type": "image/png"
}
],
"category": "image"
}
}
The fields here include
name
The name of your token.
symbol
The short hand of your token. Where Solana's shorthand would be SOL.
description
The description of your token.
image
This will be set to the imageUri (or any online location of the image) that we uploaded previously.
NFT vs pNFT
The Token Metadata program can mint 2 kinds of NFTs, a normal NFT, and a pNFT (programmable Non-Fungible Asset). The main difference between the two types of NFTs here are one is royalty enforced (pNFT) and the other is not (NFT).
NFT
- No royatly enforcement
- Simpler in initial setup and to work with in future.
pNFT
- More accounts to deal with when it comes to future development.
- Royalty enforcement
- Programable in which we have rulesets which can block programs from making a transfer.
Minting the Nft
From here you can pick the type of NFT mint instruction you wish to use, either NFT or pNFT.
NFT
1import { percentAmount, generateSigner } from '@metaplex-foundation/umi';
2import { createNft } from '@metaplex-foundation/mpl-token-metadata';
3
4// Assuming umi is set up with mplTokenMetadata plugin
5// See getting-started for full setup
6
7const mint = generateSigner(umi);
8
9// Create and mint an NFT in one step
10await createNft(umi, {
11 mint,
12 name: 'My NFT',
13 uri: 'https://example.com/my-nft.json',
14 sellerFeeBasisPoints: percentAmount(5.5),
15 // Optional: add to collection (must verify separately)
16 // collection: some({ key: collectionMint.publicKey, verified: false }),
17}).sendAndConfirm(umi);
18
19console.log('NFT created:', mint.publicKey);
1import { generateKeyPairSigner } from '@solana/kit';
2import { createNft } from '@metaplex-foundation/mpl-token-metadata-kit';
3
4// Assuming rpc, rpcSubscriptions, and sendAndConfirmInstructions are set up
5// See getting-started for full setup
6
7const mint = await generateKeyPairSigner();
8const authority = await generateKeyPairSigner(); // Your wallet
9
10// Create and mint an NFT in one step
11const [createIx, mintIx] = await createNft({
12 mint,
13 authority,
14 payer: authority,
15 name: 'My NFT',
16 uri: 'https://example.com/my-nft.json',
17 sellerFeeBasisPoints: 550, // 5.5%
18 tokenOwner: authority.address,
19 // Optional: add to collection (must verify separately)
20 // collection: { key: collectionMint.address, verified: false },
21});
22
23// Send both instructions in one transaction
24await sendAndConfirm({
25 instructions: [createIx, mintIx],
26 payer: authority,
27});
28
29console.log('NFT created:', mint.address);
pNFT
1import { percentAmount, generateSigner } from '@metaplex-foundation/umi';
2import { createProgrammableNft } from '@metaplex-foundation/mpl-token-metadata';
3
4// Assuming umi is set up with mplTokenMetadata plugin
5
6const mint = generateSigner(umi);
7
8// Create and mint a Programmable NFT in one step
9await createProgrammableNft(umi, {
10 mint,
11 name: 'My Programmable NFT',
12 uri: 'https://example.com/my-programmable-nft.json',
13 sellerFeeBasisPoints: percentAmount(5.5),
14 // Optional: add to collection (must verify separately)
15 // collection: some({ key: collectionMint.publicKey, verified: false }),
16}).sendAndConfirm(umi);
17
18console.log('Programmable NFT created:', mint.publicKey);
1import { generateKeyPairSigner } from '@solana/kit';
2import { createProgrammableNft } from '@metaplex-foundation/mpl-token-metadata-kit';
3
4// Assuming rpc, rpcSubscriptions, and sendAndConfirmInstructions are set up
5
6const mint = await generateKeyPairSigner();
7const authority = await generateKeyPairSigner(); // Your wallet
8
9// Create and mint a Programmable NFT in one step
10const [createIx, mintIx] = await createProgrammableNft({
11 mint,
12 authority,
13 payer: authority,
14 name: 'My Programmable NFT',
15 uri: 'https://example.com/my-programmable-nft.json',
16 sellerFeeBasisPoints: 550, // 5.5%
17 tokenOwner: authority.address,
18 // Optional: add to collection (must verify separately)
19 // collection: { key: collectionMint.address, verified: false },
20});
21
22// Send both instructions in one transaction
23await sendAndConfirm({
24 instructions: [createIx, mintIx],
25 payer: authority,
26});
27
28console.log('Programmable NFT created:', mint.address);
What's Next?
This guide helped you to create a basic NFT, from here you can head over to the Token Metadata Program and check out things like creating a collection and adding your new NFT into a collection and the various other interactions you can perform with your NFT.
