Since 2009, we have been utilizing our extensive expertise in blockchain technologies to help businesses, both large and small, maximize their efficiency.
Explore More
With more than 400+ experts, Oodles comprises a fantastic resource of business knowledge that spans multiple industries. Whatever the circumstances, we keep to our obligations.
Explore More
At Oodles, we help our clients work with a human understanding but at superhuman speed something that others can't. They thus advance and maintain their lead
2nd June 2024
12 min read
Sr. Lead Development
In the rapidly evolving world of digital assets, NFT development has emerged as a groundbreaking way to represent ownership and authenticity of unique items on the blockchain. Among the myriad of platforms for blockchain app development with NFTs, Solana stands out for its high speed and low transaction costs. But what if we could make these digital collectibles even more efficient? Enter compressed NFTs"”an innovative approach that leverages Solana's capabilities to create more scalable and cost-effective NFTs. In this guide, explore how to create compressed NFTs on Solana, unlocking new possibilities for artists, collectors, and businesses alike.
Also, Check | How to Setup and Run a Solana RPC Node
What You Will Need:
Access to a QuickNode endpoint with a DAS Add-on installed
- @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
- 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
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;
}
}
// 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 };
}
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;
}
}
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
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 the below links)
Deepak Thakur
Deepak is an accomplished backend developer who specializes in Mean technology. He is highly skilled in a variety of technologies including Javascript, Node.js, Express.js, MongoDB, Postgres, MySQL, DynamoDB, Redis, AWS Lambda, Solidity, WEB3.0 and EVM based blockchains. He has worked on several successful projects such as Onchain Index - a crypto investment platform, Distinct-NFT - an NFT marketplace, SWAPPER - a decentralized marketplace, GAMAVRS- a poker NFT marketplace, Moments- a poker NFT marketplace, Hydra- an automated market making system, Bakestarter- an IDO launchpad, Cognito, Safecamz- a CCTV management suite, and many others. With his vast knowledge and expertise, he is capable of handling complex tasks and delivering high-quality solutions to meet the needs of his clients.
Sr. Lead Development
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.
We would love to
hear from you!
Innovate with confidence!