SDK

JavaScript SDK

Last updated February 25, 2026

The Bubblegum V2 JavaScript SDK (@metaplex-foundation/mpl-bubblegum) is the recommended TypeScript/JavaScript library for creating and managing compressed NFTs on Solana. Built on the Umi framework, it provides type-safe functions for all Bubblegum V2 operations and includes the DAS API plugin automatically.

What You'll Learn

This SDK reference covers:

  • Setting up Umi with the Bubblegum V2 plugin
  • Creating merkle trees for storing cNFTs
  • Minting, transferring, burning, and updating cNFTs
  • Delegating, freezing, and verifying creators
  • Fetching cNFTs using the DAS API
  • Handling transaction size limits and common errors

Summary

The Bubblegum V2 JavaScript SDK wraps all MPL-Bubblegum V2 program instructions in a type-safe API and includes the DAS API plugin for reading cNFT data.

  • Install: npm install @metaplex-foundation/mpl-bubblegum @metaplex-foundation/umi-bundle-defaults
  • Register with Umi using .use(mplBubblegum()) — the DAS API plugin is included automatically
  • Use getAssetWithProof before any write operation (transfer, burn, update, delegate, freeze, verify)
  • Applies to Bubblegum V2 (MPL-Bubblegum 5.x) — not compatible with V1 trees

Maintained by Metaplex Foundation · Last verified February 2026 · Applies to MPL-Bubblegum 5.x · View source on GitHub

Quick Start

Jump to: Setup · Create Tree · Mint · Transfer · Burn · Update · Delegate · Collections · Freeze · Verify Creators · Fetch · Errors · Quick Reference

  1. Install dependencies: npm install @metaplex-foundation/mpl-bubblegum @metaplex-foundation/umi-bundle-defaults
  2. Create a Umi instance with .use(mplBubblegum())
  3. Create a Bubblegum tree with createTree
  4. Mint cNFTs with mintV2; use getAssetWithProof before any subsequent write operation

Installation

Terminal
npm install @metaplex-foundation/mpl-bubblegum @metaplex-foundation/umi-bundle-defaults

TypeDoc API Reference

Full generated API documentation for the SDK.

npm Package

Package on npmjs.com with version history.

Umi Setup

The mplBubblegum plugin registers all Bubblegum V2 instructions and the DAS API plugin with your Umi instance.

setup.ts
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
import { mplBubblegum } from '@metaplex-foundation/mpl-bubblegum'
import { keypairIdentity } from '@metaplex-foundation/umi'
const umi = createUmi('https://api.devnet.solana.com')
.use(mplBubblegum())
.use(keypairIdentity(yourKeypair))

Create a Bubblegum Tree

createTree allocates a new merkle tree on-chain and registers it as a Bubblegum V2 tree. Tree parameters are permanent — choose them carefully before creating.

create-tree.ts
import { createTree } from '@metaplex-foundation/mpl-bubblegum'
import { generateSigner } from '@metaplex-foundation/umi'
const merkleTree = generateSigner(umi)
await createTree(umi, {
merkleTree,
maxDepth: 14, // tree holds 2^14 = 16,384 cNFTs
maxBufferSize: 64, // concurrent writes per block
canopyDepth: 10, // cached upper nodes (reduces proof size in txs)
public: false, // false = only tree creator/delegate can mint
}).sendAndConfirm(umi)
console.log('Tree address:', merkleTree.publicKey)

public: false means only the tree creator (or an approved tree delegate) can mint from the tree. Set public: true to allow anyone to mint. See Create Trees for tree size cost estimates.

Mint a Compressed NFT

Mint Without Collection

mintV2 creates a new cNFT leaf in the specified tree.

mint-cnft.ts
import { mintV2 } from '@metaplex-foundation/mpl-bubblegum'
import { none } from '@metaplex-foundation/umi'
await mintV2(umi, {
leafOwner: umi.identity.publicKey,
merkleTree: merkleTree.publicKey,
metadata: {
name: 'My Compressed NFT',
uri: 'https://example.com/my-nft.json',
sellerFeeBasisPoints: 500, // 5% royalty
collection: none(),
creators: [{ address: umi.identity.publicKey, verified: false, share: 100 }],
},
}).sendAndConfirm(umi)

Mint to a Collection

Pass a coreCollection to associate the cNFT with an MPL-Core collection. The collection must have the BubblegumV2 plugin enabled.

mint-to-collection.ts
import { mintV2 } from '@metaplex-foundation/mpl-bubblegum'
import { publicKey } from '@metaplex-foundation/umi'
await mintV2(umi, {
leafOwner: umi.identity.publicKey,
merkleTree: merkleTree.publicKey,
metadata: {
name: 'My Collection cNFT',
uri: 'https://example.com/my-nft.json',
sellerFeeBasisPoints: 500,
collection: publicKey('YourCollectionAddressHere'),
creators: [{ address: umi.identity.publicKey, verified: false, share: 100 }],
},
coreCollection: publicKey('YourCollectionAddressHere'),
}).sendAndConfirm(umi)

Get Asset ID After Minting

Use parseLeafFromMintV2Transaction to retrieve the leaf schema (including the asset ID) after a mint confirms.

parse-mint.ts
import { mintV2, parseLeafFromMintV2Transaction } from '@metaplex-foundation/mpl-bubblegum'
import { none } from '@metaplex-foundation/umi'
const { signature } = await mintV2(umi, {
leafOwner: umi.identity.publicKey,
merkleTree: merkleTree.publicKey,
metadata: {
name: 'My Compressed NFT',
uri: 'https://example.com/my-nft.json',
sellerFeeBasisPoints: 500,
collection: none(),
creators: [],
},
}).sendAndConfirm(umi)
const leaf = await parseLeafFromMintV2Transaction(umi, signature)
console.log('Asset ID:', leaf.id)
console.log('Leaf index:', leaf.nonce)

Transfer a Compressed NFT

transferV2 moves ownership of a cNFT to a new wallet. getAssetWithProof fetches all required proof parameters from the DAS API.

transfer.ts
import { getAssetWithProof, transferV2 } from '@metaplex-foundation/mpl-bubblegum'
import { publicKey } from '@metaplex-foundation/umi'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
await transferV2(umi, {
...assetWithProof,
leafOwner: umi.identity, // current owner as signer
newLeafOwner: publicKey('NewOwnerAddressHere'),
}).sendAndConfirm(umi)

Burn a Compressed NFT

burnV2 permanently destroys a cNFT and removes its leaf from the tree.

burn.ts
import { getAssetWithProof, burnV2 } from '@metaplex-foundation/mpl-bubblegum'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
await burnV2(umi, {
...assetWithProof,
leafOwner: umi.identity, // owner must sign
}).sendAndConfirm(umi)

Update a Compressed NFT

updateMetadataV2 modifies a cNFT's metadata. Update authority depends on whether the cNFT belongs to a collection — see Update cNFTs for authority rules.

update.ts
import {
getAssetWithProof,
updateMetadataV2,
UpdateArgsArgs,
} from '@metaplex-foundation/mpl-bubblegum'
import { some, publicKey } from '@metaplex-foundation/umi'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
const updateArgs: UpdateArgsArgs = {
name: some('Updated Name'),
uri: some('https://example.com/updated.json'),
}
await updateMetadataV2(umi, {
...assetWithProof,
leafOwner: assetWithProof.leafOwner,
currentMetadata: assetWithProof.metadata,
updateArgs,
// If cNFT belongs to a collection, pass the collection address:
coreCollection: publicKey('YourCollectionAddressHere'),
}).sendAndConfirm(umi)

Delegate a Compressed NFT

A leaf delegate can transfer, burn, and freeze a cNFT on the owner's behalf. The delegate resets to the new owner after any transfer.

Approve a Delegate

approve-delegate.ts
import { getAssetWithProof, delegate } from '@metaplex-foundation/mpl-bubblegum'
import { publicKey } from '@metaplex-foundation/umi'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
await delegate(umi, {
...assetWithProof,
leafOwner: umi.identity,
previousLeafDelegate: umi.identity.publicKey, // current delegate (use owner if none)
newLeafDelegate: publicKey('DelegateAddressHere'),
}).sendAndConfirm(umi)

Revoke a Delegate

Set the new delegate to the owner's own address.

revoke-delegate.ts
import { getAssetWithProof, delegate } from '@metaplex-foundation/mpl-bubblegum'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
await delegate(umi, {
...assetWithProof,
leafOwner: umi.identity,
previousLeafDelegate: currentDelegatePublicKey,
newLeafDelegate: umi.identity.publicKey, // revoke by delegating to self
}).sendAndConfirm(umi)

Collections

setCollectionV2 sets, changes, or removes the MPL-Core collection on a cNFT. See Managing Collections for full details.

Set or Change a Collection

set-collection.ts
import {
getAssetWithProof,
setCollectionV2,
MetadataArgsV2Args,
} from '@metaplex-foundation/mpl-bubblegum'
import { unwrapOption, publicKey } from '@metaplex-foundation/umi'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
const collection = unwrapOption(assetWithProof.metadata.collection)
const metadata: MetadataArgsV2Args = {
...assetWithProof.metadata,
collection: collection?.key ?? null,
}
await setCollectionV2(umi, {
...assetWithProof,
metadata,
newCollectionAuthority: newCollectionUpdateAuthority,
newCoreCollection: publicKey('NewCollectionAddressHere'),
}).sendAndConfirm(umi)

Remove a Collection

remove-collection.ts
import { getAssetWithProof, setCollectionV2 } from '@metaplex-foundation/mpl-bubblegum'
import { unwrapOption } from '@metaplex-foundation/umi'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
const collection = unwrapOption(assetWithProof.metadata.collection)
await setCollectionV2(umi, {
...assetWithProof,
authority: collectionAuthoritySigner,
coreCollection: collection!.key,
}).sendAndConfirm(umi)

Freeze and Thaw

Two freeze mechanisms are available. See Freeze cNFTs for the full breakdown of asset-level vs collection-level freeze.

Freeze a cNFT (Leaf Delegate)

freeze.ts
import {
getAssetWithProof,
delegateAndFreezeV2,
} from '@metaplex-foundation/mpl-bubblegum'
import { publicKey } from '@metaplex-foundation/umi'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
// Delegates and freezes in one instruction
await delegateAndFreezeV2(umi, {
...assetWithProof,
leafOwner: umi.identity,
newLeafDelegate: publicKey('FreezeAuthorityAddressHere'),
}).sendAndConfirm(umi)

Thaw a cNFT

thaw.ts
import { getAssetWithProof, thawV2 } from '@metaplex-foundation/mpl-bubblegum'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
await thawV2(umi, {
...assetWithProof,
leafDelegate: umi.identity, // freeze authority must sign
}).sendAndConfirm(umi)

Create a Soulbound cNFT

A soulbound cNFT is permanently non-transferable. The collection must have the PermanentFreezeDelegate plugin enabled. See Freeze cNFTs for setup details.

soulbound.ts
import { getAssetWithProof, setNonTransferableV2 } from '@metaplex-foundation/mpl-bubblegum'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
await setNonTransferableV2(umi, {
...assetWithProof,
// permanent freeze delegate must sign
}).sendAndConfirm(umi)

setNonTransferableV2 is irreversible. The cNFT cannot be made transferable again after this call.

Verify Creators

verifyCreatorV2 sets the verified flag on a creator entry. The creator being verified must sign the transaction. See Verify Creators for details.

Verify a Creator

verify-creator.ts
import {
getAssetWithProof,
verifyCreatorV2,
MetadataArgsV2Args,
} from '@metaplex-foundation/mpl-bubblegum'
import { unwrapOption, none } from '@metaplex-foundation/umi'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
const collectionOption = unwrapOption(assetWithProof.metadata.collection)
const metadata: MetadataArgsV2Args = {
name: assetWithProof.metadata.name,
uri: assetWithProof.metadata.uri,
sellerFeeBasisPoints: assetWithProof.metadata.sellerFeeBasisPoints,
collection: collectionOption ? collectionOption.key : none(),
creators: assetWithProof.metadata.creators,
}
await verifyCreatorV2(umi, {
...assetWithProof,
metadata,
creator: umi.identity, // the creator being verified must sign
}).sendAndConfirm(umi)

Unverify a Creator

unverify-creator.ts
import {
getAssetWithProof,
unverifyCreatorV2,
MetadataArgsV2Args,
} from '@metaplex-foundation/mpl-bubblegum'
import { unwrapOption, none } from '@metaplex-foundation/umi'
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })
const metadata: MetadataArgsV2Args = {
name: assetWithProof.metadata.name,
uri: assetWithProof.metadata.uri,
sellerFeeBasisPoints: assetWithProof.metadata.sellerFeeBasisPoints,
collection: unwrapOption(assetWithProof.metadata.collection)?.key ?? none(),
creators: assetWithProof.metadata.creators,
}
await unverifyCreatorV2(umi, {
...assetWithProof,
metadata,
creator: umi.identity,
}).sendAndConfirm(umi)

Fetching cNFTs

The DAS API plugin is automatically registered by mplBubblegum(). See Fetch cNFTs for the full breakdown of available methods.

Fetch a Single cNFT

fetch-asset.ts
import { publicKey } from '@metaplex-foundation/umi'
const asset = await umi.rpc.getAsset(assetId)
console.log('Owner:', asset.ownership.owner)
console.log('Name:', asset.content.metadata.name)

Fetch cNFTs by Owner

fetch-by-owner.ts
import { publicKey } from '@metaplex-foundation/umi'
const result = await umi.rpc.getAssetsByOwner({
owner: publicKey('OwnerAddressHere'),
})
console.log('cNFTs owned:', result.items.length)

Fetch cNFTs by Collection

fetch-by-collection.ts
import { publicKey } from '@metaplex-foundation/umi'
const result = await umi.rpc.getAssetsByGroup({
groupKey: 'collection',
groupValue: publicKey('CollectionAddressHere'),
})
console.log('cNFTs in collection:', result.items.length)

Derive Leaf Asset ID from Tree and Index

find-asset-id.ts
import { findLeafAssetIdPda } from '@metaplex-foundation/mpl-bubblegum'
const [assetId] = await findLeafAssetIdPda(umi, {
merkleTree: merkleTree.publicKey,
leafIndex: 0,
})

Transaction Patterns

Handling "Transaction Too Large" Errors

Merkle proofs grow with tree depth. Pass truncateCanopy: true to getAssetWithProof to automatically remove proof nodes already cached in the canopy, reducing transaction size.

truncate-canopy.ts
import { getAssetWithProof } from '@metaplex-foundation/mpl-bubblegum'
// truncateCanopy fetches tree config and removes redundant proof nodes
const assetWithProof = await getAssetWithProof(umi, assetId, { truncateCanopy: true })

For very deep trees where even a truncated proof exceeds the transaction limit, use versioned transactions with Address Lookup Tables.

Send and Confirm

send-and-confirm.ts
const result = await mintV2(umi, { ... }).sendAndConfirm(umi)
console.log('Signature:', result.signature)

Build Without Sending

build-only.ts
const tx = await mintV2(umi, { ... }).buildAndSign(umi)
// send later: await umi.rpc.sendTransaction(tx)

Common Errors

Transaction too large

The merkle proof exceeds the 1232-byte transaction limit. Use { truncateCanopy: true } in getAssetWithProof, or implement versioned transactions with Address Lookup Tables.

Invalid proof

The proof is stale — the tree was modified after you fetched the proof. Always call getAssetWithProof immediately before submitting a write transaction.

Leaf already exists / Invalid leaf

The asset ID or leaf index is incorrect. Re-derive the asset ID using findLeafAssetIdPda or re-fetch via getAssetsByOwner.

InvalidAuthority

You are not the owner, delegate, or required authority for this instruction. Verify the correct signer is set as leafOwner or leafDelegate.

Tree is full

The merkle tree has reached its maxDepth capacity (2^maxDepth leaves). Create a new tree to continue minting.

Account not found on DAS fetch

Your RPC provider may not support the Metaplex DAS API. Switch to a compatible RPC provider.

Notes

  • getAssetWithProof is required before nearly every write instruction. Always call it immediately before submitting to avoid stale proof errors.
  • Proofs fetched via DAS can become stale if the tree is modified between fetch and submit. High-concurrency scenarios should fetch and submit in the same atomic flow.
  • setNonTransferableV2 (soulbound) is irreversible. There is no way to restore transferability once set.
  • Delegate authority resets to the new owner after any transferV2. The new owner must re-delegate if needed.
  • This SDK targets Bubblegum V2 (LeafSchemaV2). It is not compatible with Bubblegum V1 trees or the decompression workflow.
  • Collections used with cNFTs must have the BubblegumV2 plugin enabled. Standard MPL-Core collections without this plugin cannot be used.

Quick Reference

Bubblegum V2 Functions

FunctionPurpose
createTreeCreate a new Bubblegum V2 merkle tree
mintV2Mint a new compressed NFT
transferV2Transfer cNFT ownership
burnV2Permanently destroy a cNFT
updateMetadataV2Update cNFT metadata (name, URI, creators, royalties)
delegateApprove or revoke a leaf delegate
setTreeDelegateApprove or revoke a tree delegate
setCollectionV2Set, change, or remove an MPL-Core collection
freezeV2Freeze a cNFT (requires existing leaf delegate)
thawV2Thaw a frozen cNFT
delegateAndFreezeV2Delegate and freeze in a single instruction
setNonTransferableV2Make a cNFT permanently soulbound (irreversible)
verifyCreatorV2Set verified flag on a creator entry
unverifyCreatorV2Remove verified flag from a creator entry
getAssetWithProofFetch all proof parameters needed for write instructions
findLeafAssetIdPdaDerive a cNFT asset ID from tree address and leaf index
parseLeafFromMintV2TransactionExtract leaf schema (including asset ID) from a mint transaction

Minimum Dependencies

package.json
{
"dependencies": {
"@metaplex-foundation/mpl-bubblegum": "^5.0.0",
"@metaplex-foundation/umi": "^1.0.0",
"@metaplex-foundation/umi-bundle-defaults": "^1.0.0"
}
}

Program Addresses

ProgramAddress
MPL-Bubblegum V2BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY
SPL Account CompressioncmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK
SPL Noopnoopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV

Tree Size Reference

Max DepthCapacityApprox. Cost
1416,384~0.34 SOL
17131,072~1.1 SOL
201,048,576~8.5 SOL
2416,777,216~130 SOL
301,073,741,824~2,000 SOL

FAQ

What is the Bubblegum V2 JavaScript SDK?

The Bubblegum V2 JavaScript SDK (@metaplex-foundation/mpl-bubblegum) is a TypeScript library for creating and managing compressed NFTs on Solana. Built on the Umi framework, it provides type-safe wrappers for all MPL-Bubblegum V2 program instructions and includes the DAS API plugin automatically.

Do I need a special RPC provider to use this SDK?

Yes. Compressed NFTs require an RPC provider supporting the Metaplex DAS API to index and fetch cNFT data. Standard Solana RPCs do not support DAS. See the RPC Providers page for compatible options (Helius, Triton, Shyft, and others).

How do I get the asset ID of a cNFT after minting?

Use parseLeafFromMintV2Transaction with the confirmed transaction signature. It decodes the mint transaction and returns the full leaf schema including leaf.id (the asset ID) and leaf.nonce (the leaf index).

Why do I get a "Transaction too large" error?

Merkle proofs grow with tree depth. Pass { truncateCanopy: true } to getAssetWithProof to automatically remove proof nodes cached in the on-chain canopy. For very deep trees, use versioned transactions with Address Lookup Tables.

Can I use this SDK with Bubblegum V1 trees?

No. This SDK targets Bubblegum V2 which uses LeafSchemaV2 and V2 merkle trees. Use the legacy Bubblegum SDK for V1 trees. V2 trees and V1 trees are not cross-compatible.

What is getAssetWithProof and why do I need it?

getAssetWithProof is a helper that calls both getAsset and getAssetProof on the DAS API and parses the responses into the exact parameter shape expected by Bubblegum V2 write instructions. Almost every mutation instruction (transfer, burn, update, delegate, freeze, verify) requires these parameters. Always call it immediately before submitting to avoid stale proof errors.

Glossary

TermDefinition
UmiMetaplex's framework for building Solana applications; handles wallet connections, RPC, and transaction building
mplBubblegumThe Umi plugin that registers all Bubblegum V2 instructions and the DAS API plugin
cNFTCompressed NFT — stored as a hashed leaf in an on-chain merkle tree instead of a dedicated account
Merkle TreeOn-chain account storing hashed NFT data as leaves; created with createTree
LeafA single cNFT entry in the merkle tree, identified by its leaf index
ProofA list of sibling hashes enabling cryptographic verification that a leaf belongs to a tree
CanopyCached upper nodes of the merkle tree stored on-chain to reduce required proof size in transactions
LeafSchemaV2The V2 leaf data structure containing id, owner, delegate, nonce, data hash, creator hash, collection hash, asset data hash, and flags
getAssetWithProofSDK helper that fetches and parses all DAS API data needed for write instructions
DAS APIDigital Asset Standard API — RPC extension for indexing and fetching cNFT data
TreeConfigPDA derived from the merkle tree address storing Bubblegum tree configuration
Leaf DelegateAccount authorized by the cNFT owner to transfer, burn, or freeze the cNFT
Tree DelegateAccount authorized by the tree creator to mint cNFTs from a private tree
SoulboundA permanently non-transferable cNFT set via setNonTransferableV2 — irreversible
Previous
FAQ