xiaohongshu
xiaohongshu
compressed-nfts-basics
Build with Solana compressed NFTs (cNFTs) - Merkle trees, minting at scale, transfers, and metadata. Use when creating large NFT collections or reducing mint costs.
Full skill instructions
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.
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.
// 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);
}
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);
}
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;
}
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;
}
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;
}
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;
}
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 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
{
"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"
}
}
estimateTreeRent() and ensure payer has enoughImplementation is complete when:
Provide:
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}`);
Input: "Build a platform where users mint cNFTs on-demand"
Output: See complete implementation in examples including:
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience