Introduction
Getting Started
Last updated August 27, 2026
This guide sends an existing token to two wallets with MPL-Distro and the Umi framework.
Summary
An MPL-Distro launch requires an existing SPL token mint, a preserved off-chain Merkle allocation, and enough tokens in the distribution vault.
- Build the root and proofs with
prepareDistribution. - Create a seven-day
Walletdistribution with permissionless submission. - Deposit the sum of every allocation before claims begin.
- Submit the exact amount, nonce, and proof committed in the tree.
What You Will Build
You will create a two-recipient distribution, deposit 350,000 token base units, and submit the first recipient's 100,000-unit claim.
Create and Fund from the CLI
The Metaplex CLI can create the distribution and deposit or withdraw tokens. Generate Merkle proofs and submit claims with this SDK walkthrough.
Jump to: Prerequisites · Install · Create · Fund · Claim · Errors
Quick Start
The MPL-Distro quick start has four required phases.
- Install the MPL-Distro client and register
mplDistro()with Umi. - Generate and preserve the allocation root, proofs, amounts, and nonces.
- Create the distribution and deposit the complete token allocation.
- Submit a proof with
distributeand verify its claim receipt.
Prerequisites
MPL-Distro requires a funded Solana signer and an existing mint owned by the original SPL Token program.
- Node.js 20 or newer
- A Umi identity with SOL for rent, transaction fees, and the 0.002 SOL claim protocol fee
- An existing SPL token mint and its authority's funded associated token account
- Recipient addresses and allocation amounts expressed in token base units (the mint's smallest denomination; a 6-decimal token uses
1_000_000units per 1.0 token)
The examples do not accept Token-2022 mints. Use an original SPL Token program mint.
Install the MPL-Distro SDK
Install the MPL-Distro client and its Umi peer dependencies in the application that prepares and submits transactions.
npm install @metaplex-foundation/mpl-distro@^0.4 \
@metaplex-foundation/umi@^1.1 \
@metaplex-foundation/umi-bundle-defaults \
@metaplex-foundation/mpl-toolbox@^0.10
Install @metaplex-foundation/mpl-core only when claiming into a Core asset signer.
Create the Wallet Distribution
Create the distribution by committing the recipient list as a Merkle root and storing the returned proofs off-chain.
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>
The seed signer makes the distribution address unique for a mint, so the same token can have more than one distribution. The resulting PDA uses ["distribution", mint, seed], so the seed public key must be retained if the application needs to derive the address again.
Allocation Data Is Immutable During Claims
The authority cannot change the Merkle root, tree height, start time, or claimant count while startTime <= now <= endTime. Validate and back up the complete allocation file before opening claims.
Fund the Wallet Distribution
Fund the distribution by depositing at least the sum of every allocation into its program-owned associated token account. The current distribution authority must sign deposit.
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.
This tutorial deposits tokens only. Optional claim-receipt rent subsidies are covered in Funding and Recovery.
Claim the Wallet Allocation
Claim an allocation by submitting the same recipient, amount, nonce, and proof generated from the committed list.
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.
The program creates the recipient's canonical associated token account when needed, transfers tokens from the vault, and creates a claim receipt. A second transaction with the same allocation fails with AlreadyClaimed.
Verify the MPL-Distro Accounts
Verify a claim by fetching the distribution and deterministic claim receipt after confirmation.
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
Common MPL-Distro Errors
MPL-Distro errors identify mismatched proofs, windows, permissions, and vault balances.
| Error | Cause | Resolution |
|---|---|---|
InvalidClaimProof | Address, amount, nonce, or proof differs from the committed leaf | Load every value from the same preserved allocation record |
DistributionNotStarted | The cluster timestamp is before startTime | Wait for the configured Unix timestamp |
DistributionEnded | The cluster timestamp is after endTime | The authority must create a new distribution |
AlreadyClaimed | The claim receipt PDA already exists | Treat the allocation as completed |
InsufficientFunds | Recorded distribution balance is below the claim amount | Deposit more tokens before, during, or after the active window, or review prior withdrawals |
RecipientMustSign | A recipient-gated claim omitted the recipient signer | Submit with the recipient as a signer |
InvalidDistributor | The permissioned distributor does not match | Use the configured distributor signer |
Tested Configuration
The getting-started flow is based on the current MPL-Distro client tests and generated instruction builders.
| Component | Version |
|---|---|
@metaplex-foundation/mpl-distro | 0.4.x |
@metaplex-foundation/umi | 1.1.x or newer |
@metaplex-foundation/mpl-toolbox | 0.10.x |
| Token program | Original SPL Token program |
Notes
The getting-started flow demonstrates a small wallet distribution. Production Delivery covers proof storage, claim pages, and recovering unclaimed tokens.
- Use Unix timestamps in seconds, not JavaScript milliseconds.
- Use
bigintfor token base-unit amounts and timestamps. prepareDistributionswitches to a memory-optimized implementation at 1,000 allocations.- Run very large allocation builds in a controlled Node.js process and test proof delivery before funding mainnet.
- A permissionless payer can submit a claim for another wallet, but tokens still go only to that recipient.
FAQ
Does MPL-Distro create the token mint?
No. Create and fund an SPL token mint before creating the distribution.
Where should Merkle proofs be stored?
Store each address, amount, nonce, and proof in a durable database or claim file because the program stores only the root. See Production Delivery.
Can one wallet receive multiple allocations?
Yes. Assign a different nonce to each otherwise identical wallet and amount allocation.
