How to Create a Compressed NFT on Solana

Posted By : Deepak

Apr 30, 2024

Solana's compression tool employs Merkle trees to store and verify substantial data volumes on the blockchain efficiently. This blog guide showcases the process of minting and retrieving compressed NFTs using this blockchain app development technology. 

 

 

Also, Check Out | How to Setup and Run a Solana RPC Node

 

 

Creating a Compressed NFT on Solana

 

What You Will Need - Prior experience with Solana NFTs - Node.js (version 16.15 or higher) - Proficiency in TypeScript - Access to a QuickNode endpoint with DAS Add-on installed

Dependencies: 

- @metaplex-foundation/digital-asset-standard-api 

- @metaplex-foundation/umi 

- @metaplex-foundation/umi-bundle-defaults 

- @metaplex-foundation/mpl-bubblegum 

- @metaplex-foundation/mpl-token-metadata 

- @solana/spl-account-compression 

- @solana/spl-token 

- @solana/web3.js 

Key Concepts: 

 - Hashing: Converting data into fixed-size strings

 - Merkle Trees: Efficient storage and verification of extensive datasets 

 

You may also like | A Detailed Guide to NFT Minting on Solana using Metaplex API

 

 

Steps: 1.) Create Merkel tree

const maxDepthSizePair: ValidDepthSizePair = { maxDepth: 14, maxBufferSize: 64, }; 
const canopyDepth = maxDepthSizePair.maxDepth - 5; const payer = Keypair.generate(); 
const treeKeypair = Keypair.generate(); 
const tree = await createTree(connection, payer, treeKeypair, maxDepthSizePair, canopyDepth); 
async function createTree( connection: Connection, payer: Keypair, treeKeypair: Keypair, maxDepthSizePair: ValidDepthSizePair, canopyDepth: number = 0, ) { 
	// derive the tree's authority (PDA), owned by Bubblegum 
	const [treeAuthority, _bump] = PublicKey.findProgramAddressSync( [treeKeypair.publicKey.toBuffer()], BUBBLEGUM_PROGRAM_ID, ); // allocate the tree's account on chain with the space 
	
	// NOTE: this will compute the space needed to store the tree on chain (and the lamports required to store it) 
	const allocTreeIx = await createAllocTreeIx( connection, treeKeypair.publicKey, payer.publicKey, maxDepthSizePair, canopyDepth, ); // create the instruction to actually create the tree 
	
	const createTreeIx = createCreateTreeInstruction( 
	{ payer: payer.publicKey, treeCreator: payer.publicKey, treeAuthority, merkleTree: treeKeypair.publicKey, compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, logWrapper: SPL_NOOP_PROGRAM_ID, }, 
	{ maxBufferSize: maxDepthSizePair.maxBufferSize, maxDepth: maxDepthSizePair.maxDepth, public: false, }, 
	BUBBLEGUM_PROGRAM_ID, ); 
	
	try { 
	 	// create and send the transaction to initialize the tree 
	 	const tx = new Transaction().add(allocTreeIx).add(createTreeIx); 
	 	tx.feePayer = payer.publicKey; // send the transaction 
	 	await sendAndConfirmTransaction( connection, tx, // ensuring the treeKeypair PDA and the payer are BOTH signers 
	 	[treeKeypair, payer], { 
	 		commitment: "confirmed", skipPreflight: true, 
	 	}, ); 
	 	console.log("\nMerkle tree created successfully!"); 
	 	return { treeAuthority, treeAddress: treeKeypair.publicKey }; 
	 } catch (err: any) { 
	 	console.error("\nFailed to create merkle tree:", err); throw err; 
	 } 
} 

2.) Create collection 

 

// Define the metadata to be used for creating the NFT collection 
const collectionMetadataV3: CreateMetadataAccountArgsV3 = { // ...The usual metadata of an NFT }; 
const collection = await createCollection(connection, payer, collectionMetadataV3);
async function createCollection( connection: Connection, payer: Keypair, metadataV3: CreateMetadataAccountArgsV3, ) { 
	const mint = await createMint( connection, payer, payer.publicKey, payer.publicKey, 0, ); 
	
	const tokenAccount = await createAccount( connection, payer, mint, payer.publicKey, ); 
	
	await mintTo( connection, payer, mint, tokenAccount, payer, 1, [], undefined, TOKEN_PROGRAM_ID, ); 
	
	const [metadataAccount, _bump] = PublicKey.findProgramAddressSync( [Buffer.from("metadata"), TOKEN_METADATA_PROGRAM_ID.toBuffer(), mint.toBuffer()], TOKEN_METADATA_PROGRAM_ID, ); 
				   	 
	const createMetadataIx = createCreateMetadataAccountV3Instruction( 
		{ metadata: metadataAccount, mint: mint, mintAuthority: payer.publicKey, payer: payer.publicKey, updateAuthority: payer.publicKey, }, 
		{ createMetadataAccountArgsV3: metadataV3, }, 
	); 
	
	const [masterEditionAccount, _bump2] = PublicKey.findProgramAddressSync( [ Buffer.from("metadata"), TOKEN_METADATA_PROGRAM_ID.toBuffer(), mint.toBuffer(), Buffer.from("edition"), 		
	], TOKEN_METADATA_PROGRAM_ID, ); 
	
	const createMasterEditionIx = createCreateMasterEditionV3Instruction( 
		{ edition: masterEditionAccount, mint: mint, mintAuthority: payer.publicKey, payer: payer.publicKey, updateAuthority: payer.publicKey, metadata: metadataAccount, }, 
		{ createMasterEditionArgs: { maxSupply: 0, }, }, 
	); 
	const collectionSizeIX = createSetCollectionSizeInstruction( 
		{ collectionMetadata: metadataAccount, collectionAuthority: payer.publicKey, collectionMint: mint, }, 
		{ setCollectionSizeArgs: { size: 10000 }, }, 
	); 
	try { 
		const tx = new Transaction() .add(createMetadataIx) .add(createMasterEditionIx) .add(collectionSizeIX); 
		tx.feePayer = payer.publicKey; 
		await sendAndConfirmTransaction(connection, tx, [payer], { 
			commitment: "confirmed", skipPreflight: true, }); 
		} catch (err) { 
			console.error("\nFailed to create collection:", err); 
			throw err; 
		} return { mint, tokenAccount, metadataAccount, masterEditionAccount };
	} 

 

3.) Mint Compressed NFTs into the collection. 

 

const compressedNFTMetadata: MetadataArgs = { // The usual NFT Metadata }; 
const receiver = Keypair.generate(); 
await mintCompressedNFT( connection, payer, treeKeypair.publicKey, collection.mint, collection.metadataAccount, collection.masterEditionAccount, compressedNFTMetadata, receiver.publicKey, ); 
async function mintCompressedNFT( connection: Connection, payer: Keypair, treeAddress: PublicKey, collectionMint: PublicKey, collectionMetadata: PublicKey, collectionMasterEditionAccount: PublicKey, compressedNFTMetadata: MetadataArgs, receiverAddress: PublicKey, ) { 
	const treeAuthority = PublicKey.findProgramAddressSync( [treeAddress.toBuffer()], BUBBLEGUM_PROGRAM_ID, )[0];
	const bubblegumSigner = PublicKey.findProgramAddressSync( [Buffer.from("collection_cpi")], BUBBLEGUM_PROGRAM_ID, )[0]; 
	const metadataArgs = Object.assign(compressedNFTMetadata, { collection: { key: collectionMint, verified: false }, }); 
	const mintIx: TransactionInstruction = createMintToCollectionV1Instruction( 
		{ payer: payer.publicKey, merkleTree: treeAddress, treeAuthority, treeDelegate: payer.publicKey, leafOwner: receiverAddress, leafDelegate: receiverAddress, 	 
		collectionAuthority: payer.publicKey, collectionAuthorityRecordPda: BUBBLEGUM_PROGRAM_ID, collectionMint: collectionMint, collectionMetadata: collectionMetadata, 
		editionAccount: collectionMasterEditionAccount, compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, logWrapper: SPL_NOOP_PROGRAM_ID, bubblegumSigner: bubblegumSigner, 
		tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID, }, { metadataArgs, }, ); 
	
	try { 
		const tx = new Transaction().add(mintIx); 
		tx.feePayer = payer.publicKey; 
		const txSignature = await sendAndConfirmTransaction(connection, tx, [payer], { commitment: "confirmed", skipPreflight: true, }); 
		return txSignature; 
	} catch (err) { 
		console.error("\nFailed to mint compressed NFT:", err); 
		throw err; 
	} 
}

 

Execution: -

 

- Check Costs:

 

 Evaluate the expenses associated with initializing a Merkle Tree for a specific number of compressed NFTs.

 

 - Create Merkle Tree: 

 

Generate the Merkle Tree and configure its parameters. 

 

- Create collection: 

 

Mint NFTs into a collection. 

 

Read also | A Complete Guide on How to Create SPL Tokens on Solana

 

Conclusion 

 

This blog guide demonstrates the successful minting and retrieving of compressed NFTs on Solana using Merkle trees, optimizing storage and verification processes. Connect with our blockchain developers today if you have similar projects in mind.

 

Above Blog References List: (Check out the below links)

 

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

January 31, 2025 at 10:04 am

Your comment is awaiting moderation.

By using this site, you allow our use of cookies. For more information on the cookies we use and how to delete or block them, please read our cookie notice.

Chat with Us

Contact Us

Oodles | Blockchain Development Company

Name is required

Please enter a valid Name

Please enter a valid Phone Number

Please remove URL from text