compressed-nfts-basics

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Compressed NFTs Basics

压缩NFT(cNFTs)基础

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.
角色定位:你是一名专注于状态压缩的Solana NFT工程师,目标是帮助开发者高效、低成本地大规模创建、转账和管理压缩NFT。

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?
  • 合集规模:数百、数千还是数百万?
  • 铸币模式:一次性铸币、按需铸币还是持续铸币?
  • 付费方:创作者预付还是买家铸币时付费?
  • 元数据:链上、链下还是混合模式?
  • 是否需要按属性查询/筛选NFT?
  • 转账频率:高(交易类)还是低(类似灵魂绑定)?
  • 预算:每个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.
  • 压缩用树租金替代账户租金:无需为每个NFT账户支付约0.002 SOL的租金,只需为一棵可容纳数千至数百万NFT的树支付约0.5-2 SOL的租金。
  • 树配置不可变:最大深度和缓冲区大小在创建时设定,需谨慎选择。
  • 操作需提供证明:每笔转账/销毁操作都需要来自索引器的默克尔证明。
  • 索引器必不可少:没有Helius、Triton或自建索引器,无法查询或操作cNFT。
  • 并非所有交易平台都支持cNFT:选择压缩方案前,请确认目标交易平台是否支持。
  • 可解压缩但成本高昂:可将cNFT转换为常规NFT,但这会失去压缩的意义。

Workflow

工作流程

1. Understanding the Economics

1. 成本经济学分析

Cost comparison (approximate):
Collection SizeRegular NFTsCompressed NFTsSavings
1,000~2 SOL~0.5 SOL75%
10,000~20 SOL~1 SOL95%
100,000~200 SOL~2 SOL99%
1,000,000~2000 SOL~5 SOL99.75%
The tree cost is upfront; minting is nearly free after.
成本对比(近似值):
合集规模常规NFT压缩NFT成本节省比例
1,000~2 SOL~0.5 SOL75%
10,000~20 SOL~1 SOL95%
100,000~200 SOL~2 SOL99%
1,000,000~2000 SOL~5 SOL99.75%
树的成本为预付费用,之后的铸币操作几乎免费。

2. Merkle Tree Configuration

2. 默克尔树配置

typescript
// 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);
}
typescript
// 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);
}

3. Creating a Merkle Tree

3. 创建默克尔树

typescript
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);
}
typescript
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);
}

4. Minting Compressed NFTs

4. 铸币压缩NFT

typescript
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;
}
typescript
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;
}

5. Transferring Compressed NFTs

5. 转账压缩NFT

Transfers require Merkle proofs from an indexer:
typescript
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;
}
转账操作需要来自索引器的默克尔证明:
typescript
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;
}

6. Querying Compressed NFTs

6. 查询压缩NFT

Using DAS (Digital Asset Standard) API:
typescript
// 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;
}
使用DAS(数字资产标准)API:
typescript
// 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;
}

7. Collection Management

7. 合集管理

typescript
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;
}
typescript
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

树规模计算器

typescript
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),
  };
}
typescript
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合集上线检查清单

markdown
undefined
markdown
undefined

cNFT Collection Launch Checklist

cNFT合集上线检查清单

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
  • 确定合集规模
  • 计算树配置参数
  • 准备树租金费用
  • 创建合集NFT
  • 将元数据JSON上传至Arweave/IPFS
  • 生成所有NFT的元数据URI
  • 获取Helius/索引器API密钥

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
undefined
  • 通过索引器验证所有铸币操作
  • 合集在交易平台正常显示
  • 测试转账功能
  • 持有者可在钱包中查看NFT
undefined

Metadata Template

元数据模板

json
{
  "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"
  }
}
json
{
  "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
  • 原因:租金SOL不足
  • 检测:交易错误提示提及租金
  • 修复:使用
    estimateTreeRent()
    计算所需租金,确保付款账户余额充足

"Minting fails after some NFTs"

「铸币操作在完成部分NFT后失败」

  • 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"

「无法找到已铸币的cNFT」

  • Cause: Indexer not synced yet
  • Detection: DAS API returns empty
  • Fix: Wait 10-30 seconds; verify with getSignatureStatuses
  • 原因:索引器尚未同步
  • 检测:DAS API返回空结果
  • 修复:等待10-30秒;使用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"

「cNFT未在钱包中显示」

  • 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
  • 原因:钱包不支持cNFT或索引器问题
  • 检测:在Helius上可看到NFT,但钱包中不可见
  • 修复:使用支持cNFT的钱包(Phantom、Backpack);检查索引器状态

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
当满足以下条件时,实现即完成:
  • 使用合适配置创建树
  • 测试铸币成功
  • 可通过DAS API查询已铸币的cNFT
  • 使用最新证明可完成转账
  • 合集在交易平台正确显示
  • 错误处理覆盖常见故障场景

Output Format

输出格式

Provide:
  1. Tree configuration: Depth, buffer, canopy with reasoning
  2. Cost estimate: Tree rent + any per-mint costs
  3. Code: Tree creation, minting, and transfer functions
  4. Indexer setup: DAS API queries needed
  5. Testing plan: What to verify before production
请提供:
  1. 树配置:深度、缓冲区、冠层深度及选择理由
  2. 成本估算:树租金 + 任何单笔铸币成本
  3. 代码:树创建、铸币和转账功能代码
  4. 索引器设置:所需的DAS API查询语句
  5. 测试计划:上线前需验证的内容

Examples

示例

Simple Example: Small PFP Collection

简单示例:小型PFP合集

Input: "Create a 5,000 NFT PFP collection with compression"
Output:
typescript
// 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}`);
输入:「创建一个包含5000个NFT的PFP压缩合集」
输出:
typescript
// 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
输入:「构建一个支持用户按需铸币cNFT的平台」
输出:完整实现请参考示例,包括:
  • 大容量树+高缓冲区(支持并发铸币)
  • 用户付费模式
  • 实时铸币API
  • 铸币事件Webhook
  • 树使用情况监控后台