如何在Solana中快速获取主版NFT的所有衍生版?
问题
尝试获取某主版NFT(Master Edition NFT)的所有衍生版。根据文档,存在一个PDA(程序派生地址)用于关联衍生版与主版,但无法通过Metaplex/Solana SDK以编程方式找到该关联关系。
当前解决方案是解析主版NFT的所有交易,筛选出创建衍生版的记录,但交易数量大时速度极慢。代码如下:
import {JsonMetadata, Metadata, Metaplex, TaskStatus} from "@metaplex-foundation/js"; import {Connection, PublicKey, ConfirmedSignatureInfo} from "@solana/web3.js"; import * as fs from "fs"; const RPC_URL = 'https://...'; const MASTER_EDITION_ADDRESS = ''; (async () => { const connection = new Connection(RPC_URL); const metaplex = new Metaplex(connection); const start = new Date().toISOString() // Paginate transactions from Master Edition const signatures: { signature: string; }[] = [] let before: string | undefined = undefined; while (true) { const sigs: ConfirmedSignatureInfo[] = await metaplex.connection.getSignaturesForAddress( new PublicKey(MASTER_EDITION_ADDRESS), { before: before }, 'finalized' ) if (!sigs.length) break signatures.push(...sigs) before = sigs[sigs.length - 1].signature } // Parse Transactions and find new edition mints const hashlist: string[] = [] for (let i = 0; i < signatures.length; i++) { console.log(`${i}/${signatures.length}`); await metaplex.connection.getParsedTransaction(signatures[i].signature).then(tx => { if (!tx) return if (JSON.stringify(tx).toLowerCase().includes('error')) return if (JSON.stringify(tx).includes('Mint New Edition from Master Edition')) { console.log(JSON.stringify(tx, null, 4)) hashlist.push(tx.transaction.message.accountKeys[1].pubkey.toString()); } }); } console.log('found:', hashlist.length, 'items') })()
前提假设:
- 首个创建者并非该集合独有(非Candy Machine创建)
- 更新权限并非该集合独有,可用于其他集合
请问是否存在快速可靠的实现方式?
解决方案
可以通过直接查询关联主版NFT的衍生版PDA来快速获取所有衍生版,无需遍历全部交易,具体实现如下:
1. 核心原理
Metaplex的Token Metadata程序中,每个衍生版(Edition)账户的PDA会存储主版NFT的MasterEdition账户地址作为parent字段。我们可以利用Solana RPC的getProgramAccounts方法,批量过滤出所有引用目标主版MasterEdition地址的Edition账户,直接提取衍生版的Mint地址。
2. 优化后的代码实现
import { Connection, PublicKey } from "@solana/web3.js"; import { PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID } from "@metaplex-foundation/mpl-token-metadata"; const RPC_URL = "https://..."; const MASTER_EDITION_ADDRESS = ""; // 填入目标主版的MasterEdition账户地址 (async () => { const connection = new Connection(RPC_URL); // 构建过滤器:匹配所有parent字段为目标MasterEdition地址的Edition账户 const filters = [ { dataSize: 67, // Edition账户的固定数据大小:1字节判别符 + 32字节parent + 32字节mint }, { memcmp: { offset: 1, // parent字段从第1字节开始(跳过1字节的账户判别符) bytes: MASTER_EDITION_ADDRESS, }, }, ]; // 查询所有符合条件的Edition账户 const editionAccounts = await connection.getProgramAccounts( TOKEN_METADATA_PROGRAM_ID, { filters } ); // 提取每个Edition对应的衍生版Mint地址 const derivativeMints = editionAccounts.map(account => { // Mint地址存储在data的第33-65字节位置(1+32字节之后) const mintPubkey = new PublicKey(account.account.data.slice(33, 65)); return mintPubkey.toString(); }); console.log("找到衍生版数量:", derivativeMints.length); console.log("衍生版Mint列表:", derivativeMints); })();
3. 优势说明
- 速度极快:直接通过RPC批量过滤账户,无需遍历历史交易,处理大规模数据时效率远超原方案。
- 准确性高:基于Metaplex账户结构的固定规则查询,不会遗漏或误判衍生版。
- 可扩展性强:如果需要衍生版的元数据,可基于获取到的Mint地址,通过Metaplex SDK的
findMetadataByMint方法进一步查询。
内容的提问来源于stack exchange,提问作者Ari
相关产品推荐
相关产品推荐

