compressed-nfts-basics

Compressed NFTs Basics

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "compressed-nfts-basics" with this command: npx skills add sanctifiedops/solana-skills/sanctifiedops-solana-skills-compressed-nfts-basics

Compressed NFTs Basics

Role framing: You are a Solana NFT engineer specializing in state compression. Your goal is to help developers create, transfer, and manage compressed NFTs cost-effectively at scale.

Initial Assessment

  • What's the collection size: hundreds, thousands, or millions?

  • Minting pattern: all at once, on-demand, or continuous?

  • Who pays: creator upfront or buyers on mint?

  • Metadata: on-chain, off-chain, or hybrid?

  • Do you need to query/filter NFTs by attributes?

  • Transfer frequency: high (trading) or low (soulbound-ish)?

  • Budget: what's acceptable cost per NFT?

Core Principles

  • Compression trades account rent for tree rent: Instead of paying ~0.002 SOL per NFT account, pay ~0.5-2 SOL for a tree that holds thousands-millions.

  • Trees are immutable config: Max depth and buffer size are set at creation. Choose wisely.

  • Proofs are required for operations: Every transfer/burn needs a Merkle proof from an indexer.

  • Indexers are essential: Without Helius, Triton, or your own, you can't query or operate on cNFTs.

  • Not all marketplaces support cNFTs: Verify listing venue support before choosing compression.

  • Decompression is possible but costly: Can convert cNFT to regular NFT, but defeats the purpose.

Workflow

  1. Understanding the Economics

Cost comparison (approximate):

Collection Size Regular NFTs Compressed NFTs Savings

1,000 ~2 SOL ~0.5 SOL 75%

10,000 ~20 SOL ~1 SOL 95%

100,000 ~200 SOL ~2 SOL 99%

1,000,000 ~2000 SOL ~5 SOL 99.75%

The tree cost is upfront; minting is nearly free after.

  1. Merkle Tree Configuration

// Tree parameters interface TreeConfig { maxDepth: number; // Max NFTs = 2^maxDepth maxBufferSize: number; // Concurrent operations buffer canopyDepth: number; // Proof size optimization }

// Common configurations: const TREE_CONFIGS = { small: { // Up to 16,384 NFTs maxDepth: 14, maxBufferSize: 64, canopyDepth: 11, approxCost: '0.5 SOL', }, medium: { // Up to 1,048,576 NFTs maxDepth: 20, maxBufferSize: 256, canopyDepth: 14, approxCost: '1.5 SOL', }, large: { // Up to 1 billion NFTs maxDepth: 30, maxBufferSize: 2048, canopyDepth: 17, approxCost: '5+ SOL', }, };

// Calculate tree capacity function getTreeCapacity(maxDepth: number): number { return Math.pow(2, maxDepth); }

// Calculate approximate rent async function estimateTreeRent( connection: Connection, maxDepth: number, maxBufferSize: number, canopyDepth: number ): Promise<number> { const space = getConcurrentMerkleTreeAccountSize( maxDepth, maxBufferSize, canopyDepth ); return connection.getMinimumBalanceForRentExemption(space); }

  1. Creating a Merkle Tree

import { createTree, mplBubblegum, } from '@metaplex-foundation/mpl-bubblegum'; import { generateSigner, createSignerFromKeypair } from '@metaplex-foundation/umi'; import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';

async function createMerkleTree( connection: Connection, payer: Keypair, config: TreeConfig ): Promise<PublicKey> { // Setup UMI const umi = createUmi(connection.rpcEndpoint) .use(mplBubblegum());

const payerSigner = createSignerFromKeypair(umi, { publicKey: payer.publicKey, secretKey: payer.secretKey, }); umi.use(payerSigner);

// Generate tree keypair const merkleTree = generateSigner(umi);

// Create tree await createTree(umi, { merkleTree, maxDepth: config.maxDepth, maxBufferSize: config.maxBufferSize, canopyDepth: config.canopyDepth, public: false, // Only tree authority can mint }).sendAndConfirm(umi);

console.log('Tree created:', merkleTree.publicKey);

return new PublicKey(merkleTree.publicKey); }

  1. Minting Compressed NFTs

import { mintV1 } from '@metaplex-foundation/mpl-bubblegum';

interface CNFTMetadata { name: string; symbol: string; uri: string; sellerFeeBasisPoints: number; creators: Creator[]; collection?: { key: PublicKey; verified: boolean; }; }

async function mintCompressedNFT( umi: Umi, treeAddress: PublicKey, recipient: PublicKey, metadata: CNFTMetadata ): Promise<string> { const { signature } = await mintV1(umi, { leafOwner: recipient, merkleTree: treeAddress, metadata: { name: metadata.name, symbol: metadata.symbol, uri: metadata.uri, sellerFeeBasisPoints: metadata.sellerFeeBasisPoints, creators: metadata.creators.map(c => ({ address: c.address, share: c.share, verified: c.verified, })), collection: metadata.collection ? { key: metadata.collection.key, verified: metadata.collection.verified, } : null, uses: null, primarySaleHappened: false, isMutable: true, }, }).sendAndConfirm(umi);

return signature; }

// Batch minting async function batchMintCNFTs( umi: Umi, treeAddress: PublicKey, mints: { recipient: PublicKey; metadata: CNFTMetadata }[], batchSize: number = 5 // Transactions per batch ): Promise<string[]> { const signatures: string[] = [];

for (let i = 0; i < mints.length; i += batchSize) { const batch = mints.slice(i, i + batchSize);

const txPromises = batch.map(({ recipient, metadata }) =>
  mintV1(umi, {
    leafOwner: recipient,
    merkleTree: treeAddress,
    metadata: {
      name: metadata.name,
      symbol: metadata.symbol,
      uri: metadata.uri,
      sellerFeeBasisPoints: metadata.sellerFeeBasisPoints,
      creators: metadata.creators,
      collection: metadata.collection,
      uses: null,
      primarySaleHappened: false,
      isMutable: true,
    },
  }).sendAndConfirm(umi)
);

const results = await Promise.all(txPromises);
signatures.push(...results.map(r => r.signature));

console.log(`Minted ${Math.min(i + batchSize, mints.length)}/${mints.length}`);

}

return signatures; }

  1. Transferring Compressed NFTs

Transfers require Merkle proofs from an indexer:

import { transfer } from '@metaplex-foundation/mpl-bubblegum'; import { getAssetWithProof } from '@metaplex-foundation/mpl-bubblegum';

async function transferCNFT( umi: Umi, assetId: PublicKey, currentOwner: PublicKey, newOwner: PublicKey ): Promise<string> { // Get asset with proof from indexer (DAS API) const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true, });

// Execute transfer const { signature } = await transfer(umi, { ...assetWithProof, leafOwner: currentOwner, newLeafOwner: newOwner, }).sendAndConfirm(umi);

return signature; }

// Using Helius DAS API directly async function getAssetProofHelius( assetId: string, heliusApiKey: string ): Promise<AssetProof> { const response = await fetch( https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 'my-id', method: 'getAssetProof', params: { id: assetId }, }), } );

const { result } = await response.json(); return result; }

  1. Querying Compressed NFTs

Using DAS (Digital Asset Standard) API:

// Get all cNFTs by owner async function getCNFTsByOwner( ownerAddress: string, heliusApiKey: string ): Promise<Asset[]> { const response = await fetch( https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 'my-id', method: 'getAssetsByOwner', params: { ownerAddress, page: 1, limit: 1000, }, }), } );

const { result } = await response.json(); return result.items; }

// Get cNFTs by collection async function getCNFTsByCollection( collectionAddress: string, heliusApiKey: string ): Promise<Asset[]> { const response = await fetch( https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 'my-id', method: 'getAssetsByGroup', params: { groupKey: 'collection', groupValue: collectionAddress, page: 1, limit: 1000, }, }), } );

const { result } = await response.json(); return result.items; }

// Search by attributes async function searchCNFTs( heliusApiKey: string, params: { ownerAddress?: string; creatorAddress?: string; collectionAddress?: string; compressed?: boolean; } ): Promise<Asset[]> { const response = await fetch( https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 'my-id', method: 'searchAssets', params: { ...params, compressed: true, page: 1, limit: 1000, }, }), } );

const { result } = await response.json(); return result.items; }

  1. Collection Management

import { createCollection } from '@metaplex-foundation/mpl-bubblegum';

// Create collection NFT first (regular NFT) async function createCollectionNFT( umi: Umi, metadata: CollectionMetadata ): Promise<PublicKey> { const collectionMint = generateSigner(umi);

await createNft(umi, { mint: collectionMint, name: metadata.name, symbol: metadata.symbol, uri: metadata.uri, sellerFeeBasisPoints: metadata.sellerFeeBasisPoints, isCollection: true, }).sendAndConfirm(umi);

return new PublicKey(collectionMint.publicKey); }

// Mint cNFT with collection async function mintWithCollection( umi: Umi, treeAddress: PublicKey, collectionMint: PublicKey, collectionAuthority: Keypair, recipient: PublicKey, metadata: CNFTMetadata ): Promise<string> { const { signature } = await mintToCollectionV1(umi, { leafOwner: recipient, merkleTree: treeAddress, collectionMint, metadata: { name: metadata.name, symbol: metadata.symbol, uri: metadata.uri, sellerFeeBasisPoints: metadata.sellerFeeBasisPoints, creators: metadata.creators, }, }).sendAndConfirm(umi);

return signature; }

Templates / Playbooks

Tree Size Calculator

function recommendTreeConfig(expectedNFTs: number): TreeConfig { // Find minimum depth to fit NFTs let maxDepth = Math.ceil(Math.log2(expectedNFTs));

// Add buffer for future mints (2x capacity) maxDepth = Math.max(maxDepth + 1, 14);

// Cap at practical limits maxDepth = Math.min(maxDepth, 30);

// Buffer size based on expected concurrency const maxBufferSize = expectedNFTs < 10000 ? 64 : expectedNFTs < 100000 ? 256 : expectedNFTs < 1000000 ? 1024 : 2048;

// Canopy depth (higher = smaller proofs but more rent) const canopyDepth = Math.min(maxDepth - 3, 17);

return { maxDepth, maxBufferSize, canopyDepth, capacity: Math.pow(2, maxDepth), }; }

cNFT Launch Checklist

cNFT Collection Launch Checklist

Pre-Launch

  • Collection size determined
  • Tree configuration calculated
  • Tree rent funded
  • Collection NFT created
  • Metadata JSON uploaded to Arweave/IPFS
  • Metadata URIs generated for all NFTs
  • Helius/indexer API key obtained

Tree Creation

  • Tree created with correct config
  • Tree authority set correctly
  • Tree address recorded

Minting

  • Test mint on devnet
  • Batch minting script tested
  • Error handling in place
  • Progress tracking implemented

Post-Launch

  • All mints verified via indexer
  • Collection showing on marketplaces
  • Transfer functionality tested
  • Holders can see NFTs in wallets

Metadata Template

{ "name": "Collection Name #1", "symbol": "COL", "description": "Description of this NFT", "image": "https://arweave.net/...", "animation_url": "https://arweave.net/...", "external_url": "https://your-site.com", "attributes": [ { "trait_type": "Background", "value": "Blue" }, { "trait_type": "Rarity", "value": "Legendary" } ], "properties": { "files": [ { "uri": "https://arweave.net/...", "type": "image/png" } ], "category": "image" } }

Common Failure Modes + Debugging

"Tree creation fails"

  • Cause: Insufficient SOL for rent

  • Detection: Transaction error mentions rent

  • Fix: Calculate rent with estimateTreeRent() and ensure payer has enough

"Minting fails after some NFTs"

  • Cause: Tree full or buffer exceeded

  • Detection: "Tree is full" or similar error

  • Fix: Create new tree; ensure depth was sufficient

"Can't find minted cNFTs"

  • Cause: Indexer not synced yet

  • Detection: DAS API returns empty

  • Fix: Wait 10-30 seconds; verify with getSignatureStatuses

"Transfer fails with proof error"

  • Cause: Stale proof (tree updated since fetch)

  • Detection: "Invalid proof" error

  • Fix: Fetch fresh proof immediately before transfer

"cNFTs not showing in wallet"

  • Cause: Wallet doesn't support cNFTs or indexer issue

  • Detection: NFTs visible on Helius but not wallet

  • Fix: Use wallet that supports cNFTs (Phantom, Backpack); check indexer

Quality Bar / Validation

Implementation is complete when:

  • Tree created with appropriate configuration

  • Test mint successful

  • Minted cNFTs queryable via DAS API

  • Transfer works with fresh proofs

  • Collection displays correctly on marketplaces

  • Error handling covers common failures

Output Format

Provide:

  • Tree configuration: Depth, buffer, canopy with reasoning

  • Cost estimate: Tree rent + any per-mint costs

  • Code: Tree creation, minting, and transfer functions

  • Indexer setup: DAS API queries needed

  • Testing plan: What to verify before production

Examples

Simple Example: Small PFP Collection

Input: "Create a 5,000 NFT PFP collection with compression"

Output:

// Configuration for 5,000 NFTs const config = { maxDepth: 14, // Capacity: 16,384 (room to grow) maxBufferSize: 64, // Sufficient for moderate minting canopyDepth: 11, // Reasonable proof sizes };

// Estimated cost: ~0.5 SOL for tree + ~0.00001 SOL per mint // Total: ~0.55 SOL for 5,000 NFTs (vs ~10 SOL regular)

// Step 1: Create collection NFT const collectionMint = await createCollectionNFT(umi, { name: 'Cool Cats Collection', symbol: 'CATS', uri: 'https://arweave.net/collection-metadata.json', sellerFeeBasisPoints: 500, // 5% royalty });

// Step 2: Create Merkle tree const treeAddress = await createMerkleTree(connection, payer, config);

// Step 3: Mint all NFTs const metadataList = generateMetadataForAll(5000); // Your metadata generation const signatures = await batchMintCNFTs(umi, treeAddress, metadataList);

console.log(Minted ${signatures.length} cNFTs to tree ${treeAddress});

Complex Example: On-Demand Minting Platform

Input: "Build a platform where users mint cNFTs on-demand"

Output: See complete implementation in examples including:

  • Large tree with high buffer (handles concurrent mints)

  • User-pays-fee model

  • Real-time minting API

  • Webhook for mint events

  • Admin dashboard for monitoring tree usage

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Web3

whale-wallet-analysis

No summary provided by upstream source.

Repository SourceNeeds Review
Web3

wallet-monitoring-bot

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

trading-bot-architecture

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

jupiter-swap-integration

No summary provided by upstream source.

Repository SourceNeeds Review