压缩 NFT 完整实操:从原理到脚本的一站式指引

·

什么是压缩 NFT(cNFT)

🔍 压缩 NFT(Compressed NFT,简称 cNFT) 通过 State Compression(状态压缩) 把 NFT 的全部元数据做一次性哈希处理,仅把根哈希存入链上的 Merkle 树账户,从而将节点存储成本拉低到传统铸造方式的千分之一:

核心关键词:压缩 NFT、cNFT、State Compression、Merkle 树、低成本、批量铸造

实现路径拆解

  1. NFT 元数据做哈希 → 存为「叶子」
  2. Merkle 树逐级合并 → 生成 32 字节「根哈希」
  3. RPC 服务商提前索引元数据 → 提供 Read API 随时查询

👉 进一步了解如何利用 Merkle 树大幅节省手续费


Concurrent Merkle 树的三把钥匙

为了让多枚叶子(=NFT)在同区块里并发修改而互不阻塞,Solana 采用 concurrent Merkle 树,需人工定义三项参数:

参数作用经济权衡
maxDepth决定叶子总量 2^maxDepth↑越大 → 支持越多 NFT,账户初始化费用↑
maxBufferSize同一 slot 可并发写数量↑越大 → 支持密集销售,账户规模↑
canopyDepth上链缓存的证明节点层级↑越大 → 后续验证更轻,费用↑

选择思路:

👉 选择参数时如何平衡成本与可扩展性


简易 6 步创建你的第一条 cNFT 系列

1. 创建项目

mkdir cnft-demo && cd cnft-demo
npm init -y
npm install @metaplex-foundation/mpl-bubblegum \
            @solana/web3.js \
            umi @metaplex-foundation/umi \ 
            @metaplex-foundation/umi-bundle-defaults \
            @metaplex-foundation/umi-signer-wallet-adapters \
            @metaplex-foundation/umi-plugin-bundle \
            @metaplex-foundation/umi-public-keys \
            @metaplex-foundation/mpl-token-metadata \
            esrun

2. 准备 Collection NFT

3. 初始化 Merkle 树账户

创建 create-tree.ts

import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
import { dasApi } from '@metaplex-foundation/digital-asset-standard-api'
import { mplBubblegum } from '@metaplex-foundation/mpl-bubblegum'
import { createTree } from '@metaplex-foundation/mpl-bubblegum'
import { signerIdentity, generateSigner } from '@metaplex-foundation/umi'

const umi = createUmi('https://api.devnet.solana.com').use(mplBubblegum()).use(dasApi())
// .use(keypairIdentity(你的密钥对))

const merkleTree = generateSigner(umi)
await createTree(umi, {
  merkleTree,
  maxDepth: 20,       // 100 万
  maxBufferSize: 64,  // 并发适中
  canopyDepth: 14,    // 方便后续验证
  public: false       // 只有创建者可铸造
}).sendAndConfirm(umi)

console.log('Tree:', merkleTree.publicKey.toString())

执行:

npx esrun create-tree.ts

4. 铸造 cNFT

新建 mint.ts

await mintToCollectionV1(umi, {
  leafOwner: 目标钱包,
  merkleTree: merkleTree.publicKey,
  collection: 你的CollectionNFT.publicKey,
  metadata: {
    name: 'Demo cNFT',
    uri: 'https://your-cdn.com/metadata/1.json',
    sellerFeeBasisPoints: 500,
    creators: [{address: 你的地址, verified: true, share: 100}]
  },
}).sendAndConfirm(umi)

5. 读取数据

fetch.ts

const assetId = findLeafAssetIdPda(umi, {merkleTree: merkleTree.publicKey, leafIndex: 0})
const asset = await umi.rpc.getAsset(assetId[0])
console.log(asset.content.metadata)

6. Transfer

transfer.ts

const res = await transfer(umi, {
  leafOwner: 原持有钱包,
  newLeafOwner: 新钱包,
  merkleTree: merkleTree.publicKey,
  assetId: assetId[0],
})

常见问题与解答

Q1:把元数据仅存在链上哈希,会不会哪天完全丢失?
A:不会。通过 State Compression 铸造的每一笔数据都写入 Solana 账本的最早交易日志,目前主流 RPC 商同步无限期存档;即便单一节点过期,也可全链重放获取原始数据。

Q2:我能自由调整 Merkle 树的三把钥匙吗?
A:只能一次性设定;预算允许可把 maxDepth 取大,当未来 NFT 数量超出时再建新树即可。

Q3:传统钱包是否支持展示 cNFT?
A:需 RPC 端支持 Digital Asset Standard(DAS)Read API;主流插件钱包已陆续适配,Zero、Glow、OK 钱包测试通过。

Q4:用户如何知道 cNFT 属于哪个合集?
A:铸造时必须绑定 Collection NFT 的 Mint ID;链上验证其母合集的署名。

Q5:我能在链上直接改动 cNFT 元数据 URI 吗?
A:可以,通过 Bubblegum 的 updateMetadata 指令改写叶子值即可,但需重新计算 Merkle 证明并提供完整路径。

Q6:成本极限可降低多少?
A:实测 Devnet 单枚 cNFT 总 Gas 成本 ~0.000005 SOL;相较传统 Token Metadata 减少 99%+。


接下来的挑战

你已经掌握了 Solana 低成本批量发行数字资产的核心武器,接下来就交给创意与实践了!