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.
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(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",src/lib/metaplex.ts L3–28 @ 8e42953 · MIT · originalimport * 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");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, )- 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
- program-examples: explore the full project
- carbon: explore the full project
- chapl1n: explore the full project
- anchor: explore the full project
- Next lesson: Oracles: Pyth and Switchboard
Related lessons
Topics: NFTsGaming · Learning path · Glossary