简介

入门指南

Last updated August 27, 2026

本指南使用 MPL-DistroUmi 框架 将现有代币发送到两个钱包。

摘要

MPL-Distro 启动需要现有 SPL 代币 mint、已保存的链下 Merkle 分配,以及分发金库中足够的代币。

  • prepareDistribution 构建根和证明。
  • 创建为期七天、Permissionless 提交的 Wallet 分发。
  • 在领取开始前存入所有分配之和。
  • 提交树中承诺的精确 amount、nonce 和证明。

你将构建的内容

你将创建一份两接收方分发,存入 350,000 代币基本单位,并提交第一位接收方的 100,000 单位领取。

从 CLI 创建并注资

Metaplex CLI 可以创建分发并存入或提取代币。生成 Merkle 证明并提交领取请使用本 SDK 演练。

跳转至: 前置条件 · 安装 · 创建 · 注资 · 领取 · 错误

快速开始

MPL-Distro 快速开始有四个必需阶段。

  1. 安装 MPL-Distro 客户端并向 Umi 注册 mplDistro()
  2. 生成并保存分配根、证明、amount 和 nonce。
  3. 创建分发并存入完整代币分配。
  4. distribute 提交证明并验证领取收据。

前置条件

MPL-Distro 需要有资金的 Solana 签名者,以及由原始 SPL Token 程序拥有的现有 mint。

  • Node.js 20 或更高版本
  • 拥有租金、交易费和 0.002 SOL 领取协议费用 SOL 的 Umi identity
  • 现有 SPL 代币 mint 及其权限方已注资的 associated token account
  • 以代币基本单位表示的接收方地址和分配量(mint 的最小单位;6 位小数的代币每 1.0 代币为 1_000_000 单位)

示例不接受 Token-2022 mint。请使用原始 SPL Token 程序 mint。

安装 MPL-Distro SDK

在准备并提交交易的应用程序中安装 MPL-Distro 客户端及其 Umi 对等依赖。

Terminal
npm install @metaplex-foundation/mpl-distro@^0.4 \
@metaplex-foundation/umi@^1.1 \
@metaplex-foundation/umi-bundle-defaults \
@metaplex-foundation/mpl-toolbox@^0.10

仅在向 Core 资产签名者领取时安装 @metaplex-foundation/mpl-core

创建钱包分发

将接收方列表作为 Merkle 根提交,并将返回的证明保存在链下以创建分发。

createDistribution.ts
1import {
2 AllowedDistributor,
3 createDistribution,
4 DistributionType,
5 findDistributionPda,
6 mplDistro,
7 prepareDistribution,
8} from '@metaplex-foundation/mpl-distro'
9import { generateSigner, publicKey } from '@metaplex-foundation/umi'
10import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
11
12const umi = createUmi(
13 process.env.RPC_URL ?? 'https://api.devnet.solana.com'
14).use(mplDistro())
15
16// Use umi.use(keypairIdentity(yourKeypair)) when the Umi identity
17// should be the distribution authority.
18
19const mint = publicKey(process.env.TOKEN_MINT!)
20const recipients = [
21 { address: publicKey(process.env.RECIPIENT_1!), amount: 100_000n },
22 { address: publicKey(process.env.RECIPIENT_2!), amount: 250_000n },
23]
24const { root, proofs, treeHeight } = prepareDistribution(recipients)
25const seed = generateSigner(umi)
26const now = BigInt(Math.floor(Date.now() / 1000))
27
28await createDistribution(umi, {
29 mint,
30 seed,
31 merkleRoot: root,
32 treeHeight,
33 startTime: now,
34 endTime: now + 7n * 24n * 60n * 60n,
35 totalClaimants: BigInt(recipients.length),
36 name: 'Community distribution',
37 distributionType: DistributionType.Wallet,
38 allowedDistributor: AllowedDistributor.Permissionless,
39 subsidizeReceipts: false,
40}).sendAndConfirm(umi)
41
42const [distribution] = findDistributionPda(umi, {
43 mint,
44 seed: seed.publicKey,
45})
46
47// Store each recipient's amount, nonce, and proof in your claim service.
48console.log('Distribution:', distribution)
49console.log('Proofs:', proofs)
50
51// Distribution: <distribution PDA>
52// Proofs: <one proof array per recipient>

seed 签名者使分发地址对某个 mint 唯一,因此同一代币可以有多个分发。结果 PDA 使用 ["distribution", mint, seed],因此若应用程序需要再次推导地址,必须保留 seed 公钥。

领取期间分配数据不可变

startTime <= now <= endTime 期间,权限方不能更改 Merkle 根、树高度、开始时间或 claimant 数量。打开领取前请校验并备份完整分配文件。

为钱包分发注资

将至少等于所有分配之和的代币存入程序拥有的 associated token account 来为分发注资。当前分发权限方必须签署 deposit

fundDistribution.ts
1import {
2 deposit,
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
12// The Umi identity must be the current distribution authority.
13
14const distribution = publicKey(process.env.DISTRIBUTION_ADDRESS!)
15const mint = publicKey(process.env.TOKEN_MINT!)
16const totalAmount = 350_000n
17
18await deposit(umi, {
19 distribution,
20 mint,
21 amount: totalAmount,
22}).sendAndConfirm(umi)
23
24// The distribution ATA contains 350000 base units.

本教程只存入代币。可选的领取收据租金补贴见 注资与回收

领取钱包分配

提交从已提交列表生成的相同接收方、amount、nonce 和证明来领取分配。

claimDistribution.ts
1import {
2 distribute,
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// The payer may be the recipient or a third-party distributor, depending on
14// the distribution's allowedDistributor setting.
15
16const distribution = publicKey(process.env.DISTRIBUTION_ADDRESS!)
17const mint = publicKey(process.env.TOKEN_MINT!)
18const recipients = [
19 { address: publicKey(process.env.RECIPIENT_1!), amount: 100_000n },
20 { address: publicKey(process.env.RECIPIENT_2!), amount: 250_000n },
21]
22const recipientIndex = 0
23const { proofs } = prepareDistribution(recipients)
24
25await distribute(umi, {
26 distribution,
27 mint,
28 recipient: recipients[recipientIndex].address,
29 amount: recipients[recipientIndex].amount,
30 proof: proofs[recipientIndex],
31 nonce: 0,
32}).sendAndConfirm(umi)
33
34// The recipient ATA receives 100000 base units and a claim receipt is created.

程序会在需要时创建接收方的规范 associated token account,从金库转移代币,并创建领取收据。带有相同分配的第二笔交易会以 AlreadyClaimed 失败。

验证 MPL-Distro 账户

确认后通过获取分发和确定性领取收据来验证领取。

verifyClaim.ts
1import {
2 fetchClaimReceipt,
3 fetchDistribution,
4 findClaimReceiptPda,
5 mplDistro,
6} from '@metaplex-foundation/mpl-distro'
7import { publicKey } from '@metaplex-foundation/umi'
8import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
9
10const umi = createUmi(
11 process.env.RPC_URL ?? 'https://api.devnet.solana.com'
12).use(mplDistro())
13
14const distribution = publicKey(process.env.DISTRIBUTION_ADDRESS!)
15const recipients = [
16 { address: publicKey(process.env.RECIPIENT_1!), amount: 100_000n },
17 { address: publicKey(process.env.RECIPIENT_2!), amount: 250_000n },
18]
19const recipientIndex = 0
20
21const [receipt] = findClaimReceiptPda(umi, {
22 distribution,
23 recipient: recipients[recipientIndex].address,
24 amount: recipients[recipientIndex].amount,
25 nonce: 0,
26})
27
28const [distributionAccount, receiptAccount] = await Promise.all([
29 fetchDistribution(umi, distribution),
30 fetchClaimReceipt(umi, receipt),
31])
32
33console.log(distributionAccount.claimCount)
34console.log(receiptAccount.amount)
35
36// claimCount includes this allocation and the receipt stores 100000

常见 MPL-Distro 错误

MPL-Distro 错误标识不匹配的证明、窗口、权限和金库余额。

错误原因解决方法
InvalidClaimProof地址、amount、nonce 或证明与已提交叶子不同从同一份保存的分配记录加载所有值
DistributionNotStarted集群时间戳早于 startTime等待配置的 Unix 时间戳
DistributionEnded集群时间戳晚于 endTime权限方必须创建新分发
AlreadyClaimed领取收据 PDA 已存在将该分配视为已完成
InsufficientFunds记录的分发余额低于领取金额在活动窗口之前、期间或之后存入更多代币,或检查先前提取
RecipientMustSign接收方门控领取缺少接收方签名者以接收方作为签名者提交
InvalidDistributorpermissioned distributor 不匹配使用配置的 distributor 签名者

已验证配置

入门流程基于当前 MPL-Distro 客户端测试和生成的指令构建器。

组件版本
@metaplex-foundation/mpl-distro0.4.x
@metaplex-foundation/umi1.1.x 或更高
@metaplex-foundation/mpl-toolbox0.10.x
Token program原始 SPL Token 程序

注意事项

入门流程演示小型钱包分发。生产交付 涵盖证明存储、领取页面和回收未领取代币。

  • Unix 时间戳以秒计,不是 JavaScript 毫秒。
  • 代币基本单位数量和时间戳使用 bigint
  • prepareDistribution 在 1,000 笔分配时切换到内存优化实现。
  • 在受控 Node.js 进程中运行非常大的分配构建,并在向主网注资前测试证明交付。
  • Permissionless 支付方可以为另一个钱包提交领取,但代币仍只到达该接收方。

常见问题

MPL-Distro 会创建代币 mint 吗?

不会。创建分发前请先创建并注资 SPL 代币 mint。

Merkle 证明应存储在哪里?

程序只存储根,因此请将每个地址、amount、nonce 和证明存入持久数据库或领取文件。参见 生产交付

一个钱包可以收到多笔分配吗?

可以。为每笔其他方面相同的钱包和 amount 分配指定不同的 nonce。

Previous
概述