分发类型

旧版 NFT 分发

Last updated August 26, 2026

旧版 NFT 分发将代币分配附加到 NFT mint 地址,并在领取执行时向拥有每个 NFT 的钱包付款。

摘要

LegacyNft 分发以旧版 NFT mint 作为 Merkle 叶子,并在 distributeToLegacyNft 期间验证其当前 SPL 代币账户所有者。

  • 从 NFT mint 地址而不是所有者钱包构建分配树。
  • DistributionType.LegacyNft 创建分发。
  • 将分发代币发送到当前所有者的代币账户。
  • 针对 NFT mint 记录收据,使所有权转移无法启用第二次领取。

仅限旧版 NFT

此流程验证余额为 1 的原始 SPL Token 账户。该代币程序上的 Token Metadata NFT 和 pNFT 符合条件。它与 MPL Core 资产或 Token-2022 NFT 不兼容。

旧版 NFT 分配模型

每笔分配提交旧版 NFT mint 地址、代币量和可选 nonce。

legacyNftAllocations.ts
1import {
2 DistributionType,
3 mplDistro,
4 prepareDistribution,
5} from '@metaplex-foundation/mpl-distro'
6import { publicKey } from '@metaplex-foundation/umi'
7import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
8
9const umi = createUmi(
10 process.env.RPC_URL ?? 'https://api.devnet.solana.com'
11).use(mplDistro())
12
13const nftMintA = publicKey(process.env.LEGACY_NFT_MINT_A!)
14const nftMintB = publicKey(process.env.LEGACY_NFT_MINT_B!)
15
16const allocations = [
17 { address: nftMintA, amount: 100_000n },
18 { address: nftMintB, amount: 100_000n },
19]
20
21const { root, proofs, treeHeight } = prepareDistribution(allocations)
22
23const distributionFields = {
24 merkleRoot: root,
25 treeHeight,
26 totalClaimants: BigInt(allocations.length),
27 distributionType: DistributionType.LegacyNft,
28}
29
30console.log(distributionFields, proofs.length)
31
32// Root, treeHeight, and LegacyNft fields for createDistribution

不要从快照所有者钱包构建叶子。NFT mint 是稳定 identity,允许在领取前转移所有权。

旧版 NFT 所有权验证

程序在领取时从 NFT 的 SPL 代币账户验证当前所有权。

提供的 NFT 代币账户必须:

  • 由原始 SPL Token 程序拥有。
  • 使用 Merkle 叶子中提交的 NFT mint。
  • 恰好持有 1 个代币。
  • 由提供的 nftOwner 拥有。

程序不调用 Token Metadata、Token Record 或 Authorization Rules。它只检查上面列出的 SPL 代币账户。

提交旧版 NFT 领取

distributeToLegacyNft 指令验证 mint 证明,并将代币发送到当前 NFT 所有者的 associated token account。

claimLegacyNft.ts
1import {
2 distributeToLegacyNft,
3 mplDistro,
4 prepareDistribution,
5} from '@metaplex-foundation/mpl-distro'
6import { publicKey } from '@metaplex-foundation/umi'
7import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
8
9const umi = createUmi(
10 process.env.RPC_URL ?? 'https://api.devnet.solana.com'
11).use(mplDistro())
12
13// Default nftOwner to the Umi identity, or pass nftOwner explicitly
14// for a sponsored claim.
15
16const distribution = publicKey(process.env.DISTRIBUTION_ADDRESS!)
17const mint = publicKey(process.env.TOKEN_MINT!)
18const nftMint = publicKey(process.env.LEGACY_NFT_MINT!)
19const allocations = [{ address: nftMint, amount: 100_000n }]
20const { proofs } = prepareDistribution(allocations)
21
22await distributeToLegacyNft(umi, {
23 distribution,
24 mint,
25 nftMint,
26 amount: allocations[0].amount,
27 proof: proofs[0],
28 nonce: 0,
29}).sendAndConfirm(umi)
30
31// The current NFT owner's ATA receives 100000 base units.
32// The receipt is keyed by the NFT mint, so the allocation cannot be claimed twice.

省略 nftOwner 时,SDK 默认使用交易支付方并推导该支付方的 NFT 代币账户。当 Permissionless 服务代表另一所有者支付时,请显式提供 nftOwner

sponsoredLegacyNftClaim.ts
1import {
2 distributeToLegacyNft,
3 mplDistro,
4} from '@metaplex-foundation/mpl-distro'
5import { publicKey } from '@metaplex-foundation/umi'
6import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
7
8const umi = createUmi(
9 process.env.RPC_URL ?? 'https://api.devnet.solana.com'
10).use(mplDistro())
11
12const airdropService = umi.payer
13const distribution = publicKey(process.env.DISTRIBUTION_ADDRESS!)
14const rewardMint = publicKey(process.env.TOKEN_MINT!)
15const nftMint = publicKey(process.env.LEGACY_NFT_MINT!)
16const currentOwner = publicKey(process.env.NFT_OWNER!)
17const amount = 100_000n
18const proof = []
19const nonce = 0
20
21await distributeToLegacyNft(umi, {
22 payer: airdropService,
23 distribution,
24 mint: rewardMint,
25 nftMint,
26 nftOwner: currentOwner,
27 amount,
28 proof,
29 nonce,
30}).sendAndConfirm(umi)
31
32// The current NFT owner's ATA receives the allocation.

旧版 NFT 领取收据

旧版 NFT 收据将 NFT mint 存储为其接收方 identity。

收据组成部分
Recipient seedNFT mint,不是所有者钱包
Destination当前所有者针对分发 mint 的 associated token account
Ownership transfer effect改变谁可以收到未领取分配
Repeat claim after transfer被拒绝,因为收据仍绑定到 NFT mint

旧版 NFT 分发访问模式

Allowed distributor 模式适用于 NFT 所有者,而不是 NFT mint。

模式领取签名者要求
Permissionless任意支付方可以为已验证的当前所有者提交
Recipient当前 nftOwner 必须签名
Permissioned配置的 permissioned distributor 必须签名

当当前持有者必须选择加入时使用 Recipient。当中继者可以为已验证的当前所有者支付领取而无需该所有者签名时使用 Permissionless

旧版 NFT 快照注意事项

Merkle 树固定符合条件的 NFT mint,而所有权在每个 mint 领取之前保持动态。

这种区别产生两种常见模型:

  1. Mint 资格模型: 无论之后如何转移,符合条件的 NFT mint 都可以领取,领取时的所有者获得奖励。
  2. 所有者快照模型: 当快照后的转移不得移动资格时,改用快照所有者钱包并使用 钱包分发

避免市场意外

公开资格是跟随 NFT mint 还是快照所有者。买家可以收到未领取的基于 mint 的分配,但不能仅凭所有权判断领取状态;应用程序应检查领取收据。

注意事项

旧版 NFT 分发验证可替代代币账户事实,而不是完整的 NFT 元数据语义。

  • 集合验证和 NFT 资格必须在生成根之前完成。
  • 冻结或已委托的 NFT 代币账户仍需要应用层审查。
  • 奖励代币到达 NFT 所有者的规范 associated token account。
  • 当前程序在赎回后不关闭领取收据。

常见问题

NFT 转移后谁收到分配?

领取执行时拥有 NFT 代币账户的钱包收到分配。

之后的 NFT 所有者可以再次领取吗?

不可以。领取收据按 NFT mint、amount 和 nonce 键控,因此所有权转移不会重置它。

此流程可以向 MPL Core 资产持有者分发代币吗?

不可以。LegacyNft 验证 SPL 代币账户所有权;Core 资产需要 Wallet 分发的资产签名者模式。

LegacyNft 适用于 pNFT 吗?

适用,前提是 pNFT 代币账户由原始 SPL Token 程序拥有且余额为 1。程序不调用 Token Metadata、Token Record 或 Authorization Rules。不支持 Token-2022 pNFT。