Solana发行NFT


初始化项目

mkdir new-nft && cd new-nft

pnpm init

pnpm add @metaplex-foundation/mpl-token-metadata @metaplex-foundation/umi-bundle-defaults @solana-developers/helpers @metaplex-foundation/umi @solana/web3.js esrun

npx esrun xx.ts

collection-nft.ts

// 导入 Metaplex 插件,用于操作 token metadata
import {
    createNft, // 创建 NFT 的函数
    fetchDigitalAsset, // 获取链上 NFT 的函数
    mplTokenMetadata, // 插件:Token Metadata 的功能模块
  } from "@metaplex-foundation/mpl-token-metadata";

  // 导入 Solana 工具方法
  import {
    airdropIfRequired, // 空投函数,如果余额不足则发放 SOL
    getExplorerLink, // 构造浏览器地址的函数
    getKeypairFromFile, // 从文件中读取用户密钥
  } from "@solana-developers/helpers";

  // 导入 umi 默认设置(用于构建 umi 实例)
  import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";

  // 导入 Solana 连接工具和常量
  import {
    Connection, // 创建连接对象
    LAMPORTS_PER_SOL, // 1 SOL = 10^9 lamports
    clusterApiUrl, // 快捷构造 devnet、testnet、mainnet 的 RPC 地址
  } from "@solana/web3.js";

  // 导入 umi 工具
  import {
    generateSigner, // 生成签名者(Keypair)
    keypairIdentity, // 设置身份(identity)用于后续签名操作
    percentAmount, // 处理百分比金额(如 0% 的版税)
  } from "@metaplex-foundation/umi";

  // 创建 devnet 连接(用于测试网络)
  const connection = new Connection(clusterApiUrl("devnet"));

  // 读取本地钱包文件(默认路径 ~/.config/solana/id.json)
  const user = await getKeypairFromFile();

  // 如果余额小于 0.5 SOL,则自动请求空投直到余额达到 1 SOL
  await airdropIfRequired(
    connection, // 网络连接
    user.publicKey, // 钱包地址
    1 * LAMPORTS_PER_SOL, // 目标余额:1 SOL
    0.5 * LAMPORTS_PER_SOL // 最小余额阈值:0.5 SOL(低于则空投)
  );

  // 打印用户钱包地址
  console.log("Loading user", user.publicKey.toBase58());

  // 创建 umi 实例,连接 Devnet 节点
  const umi = createUmi(connection.rpcEndpoint);

  // 加载 mpl-token-metadata 插件(注册到 umi 上)
  umi.use(mplTokenMetadata());

  // 将 Solana Keypair 转换为 umi 的身份对象(支持签名)
  const umiUser = umi.eddsa.createKeypairFromSecretKey(user.secretKey);

  // 设置当前 umi 身份(后续交易由该用户签名)
  umi.use(keypairIdentity(umiUser));

  // 打印身份加载完成提示
  console.log("Set up Umi instance for user");

  // 创建一个新的 mint 地址(生成一个新的公钥 + 私钥对)
  const collectionMint = generateSigner(umi);

  // 创建 NFT 交易,设置为集合 NFT(isCollection: true)
  // URI 应该指向一个符合 Metaplex 标准的 JSON 文件
  const transaction = await createNft(umi, {
    mint: collectionMint, // 使用新生成的 mint
    name: "NOAH NFT", // NFT 名称
    symbol: "NN", // NFT 符号(symbol)
    uri: "https://raw.githubusercontent.com/NoahChen1994/picgo/refs/heads/main/nft-metadata.json", // 元数据 JSON 链接(可用 GitHub 或 IPFS)
    sellerFeeBasisPoints: percentAmount(0), // 0% 版税
    isCollection: true, // 标记该 NFT 是一个集合
  });

  // 发送交易并确认上链
  await transaction.sendAndConfirm(umi);

  // 获取刚创建的 NFT 的链上信息(metadata、mint、update auth 等)
  const createdCollectionNft = await fetchDigitalAsset(
    umi, // umi 实例
    collectionMint.publicKey // NFT 的 mint 地址
  );

  // 打印浏览器地址(Solana 浏览器中查看该 NFT)
  console.log(
    `✅ Created Collection 📦! Address: ${getExplorerLink(
      "address", 
      createdCollectionNft.mint.publicKey, 
      "devnet" 
    )}`
  );  

创建集合类型的 NFT(Collection)

  • 该脚本中的 createNft 方法用于创建一个 集合类型的 NFT
  • isCollection: true 表示该 NFT 是一个 集合(collection),通常集合用于分组相关的 NFT。集合类型的 NFT 用于标识一个 NFT 项目的集合。
  • 该脚本指定了 uri(指向 metadata 文件)和 name(集合的名称)。它会创建一个集合类型的 NFT,该 NFT 可以用于标识一组相关的 NFT 项目,通常作为 NFT 的根集合。
const transaction = await createNft(umi, {
  mint: collectionMint, // 使用新生成的 mint
  name: "NOAH NFT", // NFT 名称
  symbol: "NN", // NFT 符号(symbol)
  uri: "https://raw.githubusercontent.com/NoahChen1994/picgo/refs/heads/main/nft-metadata.json", // 元数据 JSON 链接(可用 GitHub 或 IPFS)
  sellerFeeBasisPoints: percentAmount(0), // 0% 版税
  isCollection: true, // 标记该 NFT 是一个集合
});
  • 该脚本会生成一个新的 mint,用于创建集合类型的 NFT。

ordinary-nft.ts

// 导入 Metaplex 插件,用于操作 token metadata
import {
  createNft, // 创建 NFT 的函数
  fetchDigitalAsset, // 获取链上 NFT 的函数
  mplTokenMetadata, // 插件:Token Metadata 的功能模块
} from "@metaplex-foundation/mpl-token-metadata";

// 导入 Solana 工具方法
import {
  airdropIfRequired, // 空投函数,如果余额不足则发放 SOL
  getExplorerLink, // 构造浏览器地址的函数,用于生成 Solana 浏览器链接
  getKeypairFromFile, // 从文件中读取用户密钥,返回用户的 keypair
} from "@solana-developers/helpers";

// 导入 umi 默认设置(用于构建 umi 实例)
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";

// 导入 Solana 连接工具和常量
import {
  Connection, // 创建连接对象,连接到 Solana 网络
  LAMPORTS_PER_SOL, // 1 SOL = 10^9 lamports,这是 Solana 的最小单位
  clusterApiUrl, // 快捷构造 devnet、testnet、mainnet 的 RPC 地址
} from "@solana/web3.js";

// 导入 umi 工具
import {
  generateSigner, // 生成签名者(Keypair),用于签名交易
  keypairIdentity, // 设置身份(identity)用于后续签名操作
  percentAmount, // 处理百分比金额(如 0% 的版税)
  publicKey, // 创建公共密钥,用于表示 Solana 的账户地址
} from "@metaplex-foundation/umi";

// 创建 Solana 的连接对象,连接到 devnet 网络
const connection = new Connection(clusterApiUrl("devnet"));

// 从文件中加载用户的 Keypair(密钥对),包括私钥和公钥
const user = await getKeypairFromFile();

// 如果用户的账户余额不足 0.5 SOL,则自动发放 1 SOL
await airdropIfRequired(
  connection,
  user.publicKey,
  1 * LAMPORTS_PER_SOL, // 要发放的 SOL 数量
  0.5 * LAMPORTS_PER_SOL // 检查余额阈值,低于此值时发放 SOL
);

// 打印加载的用户公钥
console.log("Loaded user ", user.publicKey);

// 创建 umi 实例,用于后续操作
const umi = createUmi(connection.rpcEndpoint);

// 使用 Metaplex Token Metadata 插件,允许操作 NFT 的 metadata
umi.use(mplTokenMetadata());

// 创建 Umi 用户身份(keypair),用于后续签名
const umiUser = umi.eddsa.createKeypairFromSecretKey(user.secretKey);

// 设置 Umi 身份,后续交易会使用这个用户身份进行签名
umi.use(keypairIdentity(umiUser));

// 打印设置完成的 Umi 用户身份
console.log("Set Up umi user");

// 定义一个已存在的 collection(集合)地址,用于将 NFT 与该集合关联
const noahAddress = publicKey("FrN56USkTMeHrfw2qjUWDCNri46Zq9aAjNSxG1t7V3C");

// 打印 NFT 创建开始的提示
console.log("Nft creating! ");

// 生成一个新的 mint(铸币)签名者,表示 NFT 的 mint 账户
const mint = generateSigner(umi);

// 创建 NFT 交易,设置 NFT 的名称、metadata URI、卖家费用、以及所属 collection 等信息
const transaction = await createNft(umi, {
  mint, // mint 签名者,表示 NFT 的 mint 地址
  name: "MyNft", // NFT 的名称
  uri: "https://raw.githubusercontent.com/NoahChen1994/picgo/refs/heads/main/nft-metadata.json", // 指向 NFT metadata 的 URI
  sellerFeeBasisPoints: percentAmount(0), // 卖家费用百分比,这里设置为 0%(即不收取卖家费用)
  collection: {
    key: noahAddress, // NFT 所属的 collection 地址
    verified: false, // collection 是否经过验证,这里设置为 false
  },
});

// 发送并确认交易,确保交易被链上确认
const tx = await transaction.sendAndConfirm(umi);

// 打印交易成功的结果
console.log("Transaction successful:", tx);

// 打印 NFT 的 mint 地址
console.log("Mint Address:", mint.publicKey);

// 获取刚才创建的 NFT 信息(根据 mint 地址)
const createdNft = await fetchDigitalAsset(umi, mint.publicKey);

// 打印成功创建的 NFT 的地址
console.log(
  `🖼️ Created NFT! Address is ${getExplorerLink(
    "address", // 生成地址的链接
    createdNft.mint.publicKey, // 获取创建的 NFT mint 地址
    "devnet" // 指定查询网络,devnet 网络
  )}`
);

创建普通类型的 NFT

  • 该脚本创建的 NFT 是普通的、非集合类型的
  • collection 只是指向一个已存在的 collection 地址(noahAddress),表示这枚 NFT 将属于一个已经存在的集合。
  • isCollection 没有被设置为 true,表示这并不是一个集合,而是集合中的一枚普通 NFT。
const transaction = await createNft(umi, {
  mint, // mint 签名者,表示 NFT 的 mint 地址
  name: "MyNft", // NFT 的名称
  uri: "https://raw.githubusercontent.com/NoahChen1994/picgo/refs/heads/main/nft-metadata.json", // 指向 NFT metadata 的 URI
  sellerFeeBasisPoints: percentAmount(0), // 卖家费用百分比,这里设置为 0%(即不收取卖家费用)
  collection: {
    key: noahAddress, // NFT 所属的 collection 地址
    verified: false, // collection 是否经过验证,这里设置为 false
  },
});
  • 该脚本创建的 NFT 属于已经存在的 noahAddress 集合,collection.key 指定了 NFT 所属的集合地址。
  • 这是一枚 普通的 NFT,它只是与一个集合关联,而不是创建一个新的集合。

verify.ts

// 从 mpl-token-metadata 包中引入用于元数据和集合验证的方法
import {
  findMetadataPda,        // 查找 NFT 元数据账户的 PDA
  mplTokenMetadata,       // 加载 Token Metadata 插件
  verifyCollectionV1,     // 验证 NFT 是否属于某个集合
} from "@metaplex-foundation/mpl-token-metadata";

// 引入一些常用工具函数
import {
  airdropIfRequired,      // 如果余额不足,自动申请 airdrop
  getExplorerLink,        // 获取 Solana 区块链浏览器的链接
  getKeypairFromFile,     // 从本地文件中加载用户钱包密钥对
} from "@solana-developers/helpers";

// 创建 Umi 实例的默认工具包
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";

// 引入 Solana web3.js 中的一些核心对象
import { Connection, LAMPORTS_PER_SOL, clusterApiUrl } from "@solana/web3.js";

// 引入 Umi 的身份验证和公钥方法
import { keypairIdentity, publicKey } from "@metaplex-foundation/umi";

// 创建与 devnet 网络的连接对象
const connection = new Connection(clusterApiUrl("devnet"));

// 从本地文件加载用户密钥对(通常是 .json 文件)
const user = await getKeypairFromFile();

// 如果用户余额不足 0.5 SOL,则申请 airdrop 至目标为 1 SOL
await airdropIfRequired(
  connection,
  user.publicKey,
  1 * LAMPORTS_PER_SOL,      // 如果当前余额小于 0.5 SOL,则补足到 1 SOL
  0.5 * LAMPORTS_PER_SOL     // 最低余额要求
);

// 输出加载成功的信息
console.log("Loaded user", user.publicKey.toBase58());

// 创建 Umi 实例,并指定 RPC 节点地址
const umi = createUmi(connection.rpcEndpoint);

// 使用 mplTokenMetadata 插件扩展 Umi 实例
umi.use(mplTokenMetadata());

// 将 Solana Keypair 转换成 Umi 支持的密钥格式
const umiUser = umi.eddsa.createKeypairFromSecretKey(user.secretKey);

// 设置 Umi 实例的默认身份(authority)为当前用户
umi.use(keypairIdentity(umiUser));

// 输出 Umi 实例配置成功
console.log("Set up Umi instance for user");

// 定义集合 NFT 的地址(即集合的 mint 地址)
const collectionAddress = publicKey(
  "FrN56USkTMeHrfw2qjUWDCNri46Zq9aAjNSxG1t7V3C"
);

// 定义需要验证的普通 NFT 的地址(即 NFT 的 mint 地址)
const nftAddress = publicKey("7mcqfSdkSDoTpJWQdsQ3nGBof92nvR9c5FsBy4fh1Ucb");

// 构建一个用于 verifyCollection 的交易对象
const transaction = await verifyCollectionV1(umi, {
  metadata: findMetadataPda(umi, { mint: nftAddress }), // 查找 NFT 的元数据 PDA 地址
  collectionMint: collectionAddress,                    // 指定要验证的集合地址
  authority: umi.identity,                              // 使用当前用户作为验证人(需有权限)
});

// 发送并确认交易
transaction.sendAndConfirm(umi);

// 输出成功信息,并附带区块链浏览器链接
console.log(
  `✅ NFT ${nftAddress} verified as member of collection ${collectionAddress}! See Explorer at ${getExplorerLink(
    "address",
    nftAddress,
    "devnet"
  )}`
);

root@DESKTOP-OF7POL3:new-nft$ npx esrun verifiy.ts 
bigint: Failed to load bindings, pure JS will be used (try npm run rebuild?)
Loaded user DPAGUq1r8pkVhHC2sEFAccPMvYqYcMHXMiLe2cJrRjGn
Set up Umi instance for user
✅ NFT 7mcqfSdkSDoTpJWQdsQ3nGBof92nvR9c5FsBy4fh1Ucb verified as member of collection FrN56USkTMeHrfw2qjUWDCNri46Zq9aAjNSxG1t7V3C! See Explorer at https://explorer.solana.com/address/7mcqfSdkSDoTpJWQdsQ3nGBof92nvR9c5FsBy4fh1Ucb?cluster=devnet

验证集合所属nft

// 定义需要验证的普通 NFT 的地址(即 NFT 的 mint 地址)
const nftAddress = publicKey("7mcqfSdkSDoTpJWQdsQ3nGBof92nvR9c5FsBy4fh1Ucb");

// 构建一个用于 verifyCollection 的交易对象
const transaction = await verifyCollectionV1(umi, {
  metadata: findMetadataPda(umi, { mint: nftAddress }), // 查找 NFT 的元数据 PDA 地址
  collectionMint: collectionAddress,                    // 指定要验证的集合地址
  authority: umi.identity,                              // 使用当前用户作为验证人(需有权限)
});
  • 根据 nftAddress 生成它的元数据地址(PDA)
  • 指定目标集合地址 collectionMint
  • 使用你自己的身份去执行验证(需要权限)

 本篇
Solana发行NFT Solana发行NFT
初始化项目mkdir new-nft && cd new-nft pnpm init pnpm add @metaplex-foundation/mpl-token-metadata @metaplex-foundation/umi-b
2025-04-10
下一篇 
Solana 发行 Token Solana 发行 Token
Solana 发行 Token 全流程命令整理初始化目录mkdir new-token cd new-token 生成自定义前缀的钱包solana-keygen grind --starts-with noah:1 选中一个结果,例如:
2025-04-10
  目录