Sol.College

Compressed NFTs and state compression

Compressed NFTs store a hash of each asset in a concurrent Merkle tree account instead of in separate accounts, which makes minting millions of assets cheap. Asset data is reconstructed by indexers through the DAS API, and transfers need a Merkle proof. Metaplex Bubblegum is the program that mints and manages them.

Reference: Solana docs: State Compression · Metaplex Bubblegum

Real code, line by line

4 excerpts from different projects. Each caption links the exact lines at the fork's pinned commit. The notes below each excerpt explain the lines that matter.

solana-foundation/program-examples · compression/cnft-vault/anchor/programs/cnft-vault/src/lib.rs L23–48 @ de09019 · MIT · original
 /// Transfer instruction discriminator from mpl-bubblegumconst TRANSFER_DISCRIMINATOR: [u8; 8] = [163, 52, 200, 231, 140, 3, 69, 186]; /// Instruction arguments for mpl-bubblegum Transfer, serialized with borsh#[derive(BorshSerialize)]struct TransferArgs {    root: [u8; 32],    data_hash: [u8; 32],    creator_hash: [u8; 32],    nonce: u64,    index: u32,} #[derive(Clone)]pub struct SPLCompression; impl anchor_lang::Id for SPLCompression {    fn id() -> Pubkey {        SPL_ACCOUNT_COMPRESSION_ID    }} /// Build a mpl-bubblegum Transfer instruction from pubkeys and args./// This avoids using mpl-bubblegum's CPI wrapper which requires solana-program 2.x AccountInfo.fn build_transfer_instruction(
sevenlabs-hq/carbon · decoders/mpl-token-metadata-decoder/src/instructions/postgres/bubblegum_set_collection_size_row.rs L27–52 @ bebd108 · MIT · original
    }} impl TryFrom<BubblegumSetCollectionSizeRow>    for crate::instructions::bubblegum_set_collection_size::BubblegumSetCollectionSize{    type Error = carbon_core::error::Error;    fn try_from(source: BubblegumSetCollectionSizeRow) -> Result<Self, Self::Error> {        Ok(Self {            set_collection_size_args: source.set_collection_size_args.0,        })    }} impl carbon_core::postgres::operations::Table    for crate::instructions::bubblegum_set_collection_size::BubblegumSetCollectionSize{    fn table() -> &'static str {        "bubblegum_set_collection_size_instruction"    }     fn columns() -> Vec<&'static str> {        vec![            "__signature",            "__instruction_index",            "__stack_height",
sakkom/chapl1n · src/lib/metaplex.ts L3–28 @ 8e42953 · MIT · original
import * as web3 from "@solana/web3.js";import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';import { createSignerFromKeypair, signerIdentity, generateSigner, createGenericFileFromBrowserFile, percentAmount } from '@metaplex-foundation/umi';import { getKeypairFromEnvironment } from "@solana-developers/helpers";import { createTree } from '@metaplex-foundation/mpl-bubblegum';import { createNft, fetchDigitalAsset, mplTokenMetadata,  } from '@metaplex-foundation/mpl-token-metadata'import { irysUploader } from "@metaplex-foundation/umi-uploader-irys";import { mintToCollectionV1 } from '@metaplex-foundation/mpl-bubblegum';// import { base58 } from '@metaplex-foundation/umi-serializers'; export type HistoryNFT = {  id: string;  content: { json_uri: string };  grouping: [{ group_key: "collection", group_value: string }];  ownership: { owner: string };  image: string;  filmPda: web3.PublicKey;}; export const initializeNodeUmi = () => {  const umi = createUmi(    'https://devnet.helius-rpc.com/?api-key=1210bef3-8110-4b7f-af32-f30426f47781'    // "https://devnet-rpc.shyft.to?api_key=aEoNRy0ZFiWQX_Lv"  )   const web3Keypair = getKeypairFromEnvironment("NODEWALLET_PRIVATE_KEY");
otter-sec/anchor · spl/src/metadata.rs L31–56 @ 541295d · Apache-2.0 · original
    )    .map_err(Into::into)} pub fn bubblegum_set_collection_size<'info>(    ctx: CpiContext<'_, '_, '_, 'info, BubblegumSetCollectionSize<'info>>,    collection_authority_record: Option<Pubkey>,    size: u64,) -> Result<()> {    let ix = mpl_token_metadata::instructions::BubblegumSetCollectionSize {        collection_metadata: *ctx.accounts.metadata_account.key,        collection_authority: *ctx.accounts.update_authority.key,        collection_mint: *ctx.accounts.mint.key,        bubblegum_signer: *ctx.accounts.bubblegum_signer.key,        collection_authority_record,    }    .instruction(        mpl_token_metadata::instructions::BubblegumSetCollectionSizeInstructionArgs {            set_collection_size_args: mpl_token_metadata::types::SetCollectionSizeArgs { size },        },    );    anchor_lang::solana_program::program::invoke_signed(        &ix,        &ToAccountInfos::to_account_infos(&ctx),        ctx.signer_seeds,    )
  1. Line 52 anchor_lang::solana_program::program::invoke_signed(: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address.

Go further

Related lessons

Topics: NFTsGaming · Learning path · Glossary