24 releases (4 stable)

1.3.0 Apr 12, 2024
1.2.0 Jan 12, 2024
1.0.1-beta.4 Dec 29, 2023
1.0.1-beta.3 Oct 12, 2023
0.3.0 Oct 27, 2022

#1 in #cpi

Download history 653/week @ 2024-01-05 902/week @ 2024-01-12 1278/week @ 2024-01-19 1478/week @ 2024-01-26 1360/week @ 2024-02-02 1129/week @ 2024-02-09 1562/week @ 2024-02-16 1439/week @ 2024-02-23 1397/week @ 2024-03-01 1591/week @ 2024-03-08 1338/week @ 2024-03-15 1063/week @ 2024-03-22 1147/week @ 2024-03-29 1204/week @ 2024-04-05 1647/week @ 2024-04-12 1424/week @ 2024-04-19

5,639 downloads per month
Used in 6 crates (4 directly)

Custom license

510KB
12K SLoC

Metaplex Bubblegum SDK

Rust library for interacting with Metaplex Bublegum program.

Getting started

From your project folder:

cargo add mpl-bubblegum

Note If you are using a solana-program version prior to 1.16, first add the solana-program dependency to your project and then add mpl-bubblegum. This will make sure you only have a single copy of the borsh crate.

Structure

The client SDK is divided into several modules:

  • accounts: structs representing the accounts of the program
  • errors: enums representing the program errors
  • instructions: structs to facilitate the creation of instructions, instruction arguments and CPI instructions
  • types: structs representing types used by the program

Instruction Builders

One of the main features of the client SDK is to facilitate the creation of instructions. There are two "types" of instruction builders automatically generated – both support passing accounts by name and optional positional.

Client instruction builders

This are intended to be used by off-chain client code. Each instruction is represented by a corresponding struct – e.g., MintV1:

pub struct MintV1 {
    pub tree_config: solana_program::pubkey::Pubkey,

    pub leaf_owner: solana_program::pubkey::Pubkey,

    pub leaf_delegate: solana_program::pubkey::Pubkey,

    pub merkle_tree: solana_program::pubkey::Pubkey,

    pub payer: solana_program::pubkey::Pubkey,

    pub tree_creator_or_delegate: solana_program::pubkey::Pubkey,

    pub log_wrapper: solana_program::pubkey::Pubkey,

    pub compression_program: solana_program::pubkey::Pubkey,

    pub system_program: solana_program::pubkey::Pubkey,
}

After filling in the instruction account fields, you can use the instruction(...) method to generate the corresponding solana_program::instruction::Instruction:

// instruction args
let metadata = MetadataArgs {
    name,
    uri,
    creators,
    ...
};

// instruction accounts
let mint_ix = MintV1 {
    tree_config,
    leaf_owner,
    leaf_delegate,
    merkle_tree,
    payer,
    tree_creator_or_delegate,
    log_wrapper: spl_noop::ID,
    compression_program: spl_account_compression::ID,
    system_program: system_program::ID,
};

// creates the instruction
let create_ix = create_ix.instruction(
    MintV1InstructionArgs {
        metadata,
    });

Alternatively, you can use the MintV1Builder to create the appropriate instruction:

let mint_ix = MintV1Builder::new()
    .tree_config(tree_config)
    .leaf_owner(leaf_owner)
    .leaf_delegate(leaf_delegate)
    .merkle_tree(merkle_tree)
    .payer(payer_pubkey)
    .tree_creator_or_delegate(tree_creator)
    .metadata(metadata)
    .instruction();

CPI instruction builders

These are builders to be used by on-chain code, which will CPI into Bubblegum. Similarly to "off-chain" builders, each instruction has a struct to invoke CPI instructions – e.g., MintV1Cpi:

pub struct MintV1Cpi<'a, 'b> {
    /// The program to invoke.
    pub __program: &'b solana_program::account_info::AccountInfo<'a>,

    pub tree_config: &'b solana_program::account_info::AccountInfo<'a>,

    pub leaf_owner: &'b solana_program::account_info::AccountInfo<'a>,

    pub leaf_delegate: &'b solana_program::account_info::AccountInfo<'a>,

    pub merkle_tree: &'b solana_program::account_info::AccountInfo<'a>,

    pub payer: &'b solana_program::account_info::AccountInfo<'a>,

    pub tree_creator_or_delegate: &'b solana_program::account_info::AccountInfo<'a>,

    pub log_wrapper: &'b solana_program::account_info::AccountInfo<'a>,

    pub compression_program: &'b solana_program::account_info::AccountInfo<'a>,

    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
    /// The arguments for the instruction.
    pub __args: MintV1InstructionArgs,
}

After filling in the program, instruction accounts and argument fields, you can use the invoke() or invoke_signed(...) method to perform the CPI:

// instruction args
let metadata = MetadataArgs {
    name,
    uri,
    creators,
    ...
    };

// instruction accounts
let cpi_mint = MintV1Cpi::new(
    bubblegum_info,
    MintV1CpiAccounts {
        compression_program: spl_account_compression_info,
        leaf_delegate: authority_info,
        leaf_owner: authority_info,
        log_wrapper: spl_noop_info,
        merkle_tree: merkle_tree_info,
        payer: payer_info,
        system_program: system_program_info,
        tree_config: tree_config_info,
        tree_creator_or_delegate: delegate_info,
    },
    MintV1InstructionArgs { metadata },
);

// performs the CPI
cpi_mint.invoke_signed(&[&signer_seeds])

You can also use the MintV1CpiBuilder to simplify the process:

let cpi_mint = MintV1CpiBuilder::new(ctx.accounts.bubblegum)
    .compression_program(compression_program_info)
    .leaf_delegate(leaf_delegate_info)
    .leaf_owner(leaf_owner_info)
    .log_wrapper(log_wrapper_info)
    .merkle_tree(merkle_tree_info)
    .payer(payer_info)
    .system_program(system_program_info)
    .tree_config(tree_config_info)
    .metadata(metadata);

// performs the CPI
cpi_mint.invoke_signed(&[&signer_seeds])

Note > *Builder provide a simplified way to create the required structs, since they take advantage of any default value set on the Kinobi config and do not require to set a None value to optional fields.

PDA helpers

Account types (e.g., TreeConfig) have associated functions to find PDA or to create PDA TreeConfigs:

impl TreeConfig {
    pub fn create_pda(
        merkle_tree: Pubkey,
        bump: u8,
    ) -> Result<solana_program::pubkey::Pubkey, solana_program::pubkey::PubkeyError> {
        solana_program::pubkey::Pubkey::create_program_address(
            &[merkle_tree.as_ref(), &[bump]],
            &crate::MPL_BUBBLEGUM_ID,
        )
    }

    pub fn find_pda(merkle_tree: &Pubkey) -> (solana_program::pubkey::Pubkey, u8) {
        solana_program::pubkey::Pubkey::find_program_address(
            &[merkle_tree.as_ref()],
            &crate::MPL_BUBBLEGUM_ID,
        )
    }
}

If a bump seed is known, it is cheaper (in terms of compute units) to use the create_pda function, in particular for on-chain code.

Testing

To run the SDK tests, run the following from the root directory of the repository:

pnpm install

and then:

pnpm clients:rust:test

Documentation

The crate documentation can be found here.

Dependencies

~16–25MB
~418K SLoC