Theme

Rust

Metaplex Rust SDKs

Introduction

Metaplex provides Rust SDKs for most of our programs which have consistent and predictable outputs and functionality leading to improved integration times for developers working with our products.

Modules

The Core Rust SDKs our organized into several modules:

  • accounts: represents the program's accounts.
  • instructions: facilitates the creation of instructions, instruction arguments, and CPI instructions.
  • errors: enumerates the program's errors.
  • types: represents types used by the program.

Accounts

The accounts module is generated based on on-chain account state generation and their structs. These can be deserialized using a number of different methods based on if you are using RAW program generation or using a framework such as Anchor.

These can be accessed from <crate_name>::accounts. In the case of mpl-core you could access the accounts as follows;

mpl_core::accounts

Instructions

Each SDK comes with an instructions module that comes with multiple versions of the supplied instructions from the given program that strips away a lot of the boiler plate depending on your needs.

An example below shows all the CreateV1 instructions coming from the mpl-core crate.

CreateV1
CreateV1Builder
CreateV1Cpi
CreateV1CpiAccounts
CreateV1CpiBuilder
CreateV1InstructionArgs
CreateV1InstructionData

These can be accessed from <crate_name>::instructions. In the case of mpl-core you could access the accounts as follows;

mpl_core::instructions

Types

Each of the Metaplex Rust SDKs comes with a types module that supplies all the necessary extra types that may not be in the initial accounts module structs.

These can be accessed from <crate_name>::types. In the case of mpl-core you could access the accounts as follows;

mpl_core::types

Errors

While an errors module is generated for every SDK this just holds the error list for that specific program and users do not need to interact with this module.

Instruction Builders

Metaplex Rust SDKs will also currently come with two a Builder versions of each instruction which you can import. This abstracts a massive amount code for you and will return you an instruction that's ready to send.

These include:

  • Builder
  • CpiBuilder

In the case of CreateV1 from the mpl-Core crate docs these instructions are currently available to us.

CreateV1
CreateV1Builder
CreateV1Cpi
CreateV1CpiAccounts
CreateV1CpiBuilder
CreateV1InstructionArgs
CreateV1InstructionData

Each instruction that comes from a Metaplex Rust crate

Lets take the CreateV1 instruction from Core as an example (this applies to all other instructions from this Crate and all other Metaplex crates too).

If we look through the instructions in the we can see we have a number of instructions available to us.

Builder

Builder instructions are designed to be used via

The one we are interested in here is the CreateV1Builder.

To initialize the builder we can call new.

CreateV1Builder::new();

From this point we can ctrl + click (pc) or cmd + click (mac) into the new function generated from the Builder:: which positions us at the pub fn new() for the builder. If you scroll up slightly you'll then see the pub struct for the CreateV1Builder as outlined below.

pub struct CreateV1Builder {
    asset: Option<solana_program::pubkey::Pubkey>,
    collection: Option<solana_program::pubkey::Pubkey>,
    authority: Option<solana_program::pubkey::Pubkey>,
    payer: Option<solana_program::pubkey::Pubkey>,
    owner: Option<solana_program::pubkey::Pubkey>,
    update_authority: Option<solana_program::pubkey::Pubkey>,
    system_program: Option<solana_program::pubkey::Pubkey>,
    log_wrapper: Option<solana_program::pubkey::Pubkey>,
    data_state: Option<DataState>,
    name: Option<String>,
    uri: Option<String>,
    plugins: Option<Vec<PluginAuthorityPair>>,
    __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
}

These are your arguments of publickeys and data that will need to be passed into the builder. Some accounts may also be optional. These optional accounts my not be required at all by the program or could possibly default to another address if left out. This behaviour can vary from instruction to instruction.

If you click through to the new() function again and scroll down this time you'll see the individual functions with additional comments. In the below case you can see that the owner will default to payer, so we don't need to pass in owner if in this case if the payer is also going to be the owner of the Asset.

/// `[optional account]`
    /// The owner of the new asset. Defaults to the authority if not present.
    #[inline(always)]
    pub fn owner(&mut self, owner: Option<solana_program::pubkey::Pubkey>) -> &mut Self {
        self.owner = owner;
        self
    }

Here is an example using the CreateV1Builder that returns an instruction using .instruction() to close out the Builder.

let create_asset_ix = CreateV1Builder::new()
        .asset(asset.pubkey())
        .collection(collection.pubkey())
        .payer(payer.pubkey())
        .name("My Nft".into())
        .uri("https://example.com/my-nft.json".into())
.       .instruction();

Now that we have our instruction ready we need to create a normal Solana transaction to send to our RPC. This includes a blockhash and signers.

Full Builder Example

This is a full example of creating a instruction using a Metaplex Builder function and sending that transaction off to the chain.

use mpl_core::instructions::CreateV1Builder;
use solana_client::nonblocking::rpc_client;
use solana_sdk::{signature::Keypair, signer::Signer, transaction::Transaction};

let rpc_client = rpc_client::RpcClient::new("https://api.devnet.solana.com".to_string());

    let payer = Keypair::new();
    let asset = Keypair::new();

    let create_asset_ix = CreateV1Builder::new()
        .asset(asset.pubkey())
        .payer(payer.pubkey())
        .name("My Nft".into())
        .uri("https://example.com/my-nft.json".into())
        .instruction();

    let signers = vec![&asset, &payer];

    let last_blockhash = rpc_client.get_latest_blockhash().await.unwrap();

    let create_asset_tx = Transaction::new_signed_with_payer(
        &[create_asset_ix],
        Some(&payer.pubkey()),
        &signers,
        last_blockhash,
    );

    let res = rpc_client.send_and_confirm_transaction(&create_asset_tx).await.unwrap();

    println!("Signature: {:?}", res)

CpiBuilder

The CpiBuilder instructions are designed to be used when you wish to call and execute instructions from a Metaplex program from your own program.

We have a full separate guide discussing CpiBuilders which can be viewed here;

CPI Into a Metaplex Program

Previous
Getting Started with Rust