如何获取Solana Candy Machine铸造的SPL Token并为元数据签名
功能实现完整方案
前置依赖
确保你已经安装了以下依赖包:
@solana/web3.js@metaplex-foundation/mpl-token-metadatabs58(可选,用于私钥解码)
代码修正与补全说明
你原有代码的问题是查询对象错误:你当前调用了Candy Machine程序的getProgramAccounts接口,实际需要查询Metaplex Token元数据程序的账户,过滤条件为「第一个创作者地址为你的Candy Machine地址」,对应就是所有该CM铸造的NFT元数据。
完整可运行代码如下:
import { Connection, Keypair, PublicKey, Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; import { createSignMetadataInstruction, Metadata, PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID } from "@metaplex-foundation/mpl-token-metadata"; import bs58 from "bs58"; // --------------- 替换为你自己的配置 --------------- const RPC_URL = "你的RPC节点地址"; const CANDY_MACHINE_ADDRESS = new PublicKey("你的Candy Machine地址"); const CREATOR_PRIVATE_KEY = bs58.decode("你的创作者账户私钥base58字符串"); const creatorKeypair = Keypair.fromSecretKey(CREATOR_PRIVATE_KEY); const BATCH_SIZE = 15; // 每批处理的NFT数量,避免交易过大/超时 // ------------------------------------------------- const connection = new Connection(RPC_URL, "confirmed"); async function getCMNFTMetadataList(): Promise<PublicKey[]> { // 元数据账户中第一个creator地址的偏移量:固定为249 const creatorOffset = 249; const filters = [ { memcmp: { offset: creatorOffset, bytes: CANDY_MACHINE_ADDRESS.toBase58(), }, }, // 可选过滤:只返回数据长度符合标准NFT元数据的账户,减少无效数据 { dataSize: 679 }, ]; const accounts = await connection.getProgramAccounts(TOKEN_METADATA_PROGRAM_ID, { commitment: "confirmed", filters, }); // 提取所有元数据地址 return accounts.map(acc => acc.pubkey); } async function batchSignMetadata(metadataList: PublicKey[]) { const total = metadataList.length; console.log(`共找到${total}个该Candy Machine铸造的NFT,开始分批签名`); for (let i = 0; i < total; i += BATCH_SIZE) { const batch = metadataList.slice(i, i + BATCH_SIZE); const tx = new Transaction(); for (const metadataAddr of batch) { // 先查询元数据,确认你是未签名的创作者,避免重复签名 const metadata = await Metadata.fromAccountAddress(connection, metadataAddr); const selfCreator = metadata.data.creators?.find(c => c.address.equals(creatorKeypair.publicKey)); // 已经签名过的跳过 if (!selfCreator || selfCreator.verified) continue; // 添加签名指令 tx.add( createSignMetadataInstruction({ metadata: metadataAddr, creator: creatorKeypair.publicKey, }) ); } // 空批次跳过 if (tx.instructions.length === 0) { console.log(`批次${Math.ceil(i/BATCH_SIZE)+1}无待签名NFT,跳过`); continue; } // 发送交易 const txSig = await sendAndConfirmTransaction(connection, tx, [creatorKeypair]); console.log(`批次${Math.ceil(i/BATCH_SIZE)+1}签名完成,交易哈希:${txSig}`); // 加延迟避免RPC限流 await new Promise(resolve => setTimeout(resolve, 1000)); } console.log("所有NFT签名处理完成"); } // 执行逻辑 async function main() { const metadataList = await getCMNFTMetadataList(); await batchSignMetadata(metadataList); } main().catch(console.error);
注意事项
- 建议先注释掉交易发送逻辑,先打印待签名的元数据地址,确认数量和地址正确后再实际执行签名,避免误操作
- 如果NFT数量超过1000个,建议使用带索引的RPC节点,否则
getProgramAccounts可能会超时 - 不要使用公共RPC节点处理大批量任务,容易被限流导致执行失败
内容的提问来源于stack exchange,提问作者dush88c
相关产品推荐
相关产品推荐

