Introduction

Using Metaplex Core in Anchor

Last updated January 31, 2026

Build on-chain programs that interact with Core Assets using Anchor. This guide covers installation, account deserialization, plugin access, and CPI patterns.

What You'll Learn

  • Install and configure mpl-core in Anchor projects
  • Deserialize Core Assets and Collections in your programs
  • Access plugin data (Attributes, Freeze, etc.)
  • Make CPI calls to create, transfer, and manage Assets

Summary

The mpl-core Rust crate provides everything needed to interact with Core from Anchor programs. Enable the anchor feature flag for native Anchor account deserialization.

  • Add mpl-core with features = ["anchor"]
  • Deserialize Assets/Collections in Accounts structs
  • Use fetch_plugin() to read plugin data
  • CPI builders simplify instruction calls

Out of Scope

Client-side JavaScript SDK (see JavaScript SDK), standalone Rust clients (see Rust SDK), and creating Core Assets from clients.

Quick Start

Jump to: Installation · Account Deserialization · Plugin Access · CPI Examples

  1. Add mpl-core = { version = "x.x.x", features = ["anchor"] } to Cargo.toml
  2. Deserialize Assets with Account<'info, BaseAssetV1>
  3. Access plugins with fetch_plugin::<BaseAssetV1, PluginType>()
  4. Make CPI calls with CreateV2CpiBuilder, TransferV1CpiBuilder, etc.

Installation

To start using Core in an Anchor project, first ensure that you have added the latest version of the crate to your project by running:

cargo add mpl-core

Alternatively, you can manually specify the version in your cargo.toml file:

[dependencies]
mpl-core = "x.x.x"

Feature Flag

With the Core crate you can enable the anchor feature flag in the mpl-core crate to access Anchor-specific features by modifying the dependency entry in your cargo.toml:

[dependencies]
mpl-core = { version = "x.x.x", features = [ "anchor" ] }

Core Rust SDK Modules

The Core Rust SDK is organized into several modules:

  • accounts: represents the program's accounts.
  • errors: enumerates the program's errors.
  • instructions: facilitates the creation of instructions, instruction arguments, and CPI instructions.
  • types: represents types used by the program. For more detailed information on how different instructions are called and used, refer to the mpl-core docs.rs website or you can use cmd + left click (mac) or ctrl + left click (windows) on the instruction to expand it.

Accounts Deserialization

Deserializable Accounts

The following account structs are available for deserialization within the mpl-core crate:

- BaseAssetV1
- BaseCollectionV1
- HashedAssetV1
- PluginHeaderV1
- PluginRegistryV1

There are two ways to deserialize Core accounts within Anchor.

  • Using Anchors Account list struct (recommended in most cases),
  • Directly in the instruction functions body using <Account>::from_bytes().

Anchor Accounts List Method

By activating the anchor flag you'll be able to deserialize both the BaseAssetV1 and BaseCollectionV1 accounts directly in the Anchor Accounts list struct:

Accounts Deserialization

#[derive(Accounts)]
pub struct ExampleAccountStruct<'info> {
...
pub asset: Account<'info, BaseAssetV1>,
}

Account from_bytes() Method

Borrow the data inside the asset/collection account using the try_borrow_data() function and create the asset/collection struct from those bytes:

Accounts Deserialization

let data = ctx.accounts.asset.try_borrow_data()?;
let base_asset: BaseAssetV1 = BaseAssetV1::from_bytes(&data.as_ref())?;

Deserializing Plugins

To access individual plugins within an Asset or Collection account, use the fetch_plugin() function. This function will either return the plugin data or a null response without throwing an hard error, allowing you to check if a plugin exists without having to access its data. The fetch_plugin() function is used for both Assets and Collections accounts and can handle every plugin type by specifying the appropriate typing. If you want to access the data inside a plugin, use the middle value returned by this function.

Plugins Deserialization

let (_, attribute_list, _) = fetch_plugin::<BaseAssetV1, Attributes>(&ctx.accounts.asset.to_account_info(), mpl_core::types::PluginType::Attributes)?;

Note: The fetch_plugin() function is only used for non-external plugins. To read external plugins, use the fetch_external_plugin() function, which operates in the same way as fetch_plugin().

The CPI Instruction Builders

Each instruction from the Core crate comes with a CpiBuilder version. The CpiBuilder version is created using name of the instruction + CpiBuilder and simplifies the code significantly abstracting a lot of boilerplate code away! If you want to learn more about all the possible instruction available in Core, you can find them on the mpl-core docs.rs website

CPI Example

Let's take the CreateCollectionV2CpiBuilder instruction as an example Initialize the builder by calling new on the CpiBuilder and passing in the core program as AccountInfo:

CreateCollectionV2CpiBuilder::new(ctx.accounts.mpl_core_program.to_account_info);

Use then Cmd + left click (Ctrl + left click for Windows users) to view all the CPI arguments required for this CPI call:

CreateCollectionV2CpiBuilder::new(&ctx.accounts.core_program)
.collection(&ctx.accounts.collection)
.payer(&ctx.accounts.payer)
.system_program(&ctx.accounts.system_program)
.name("Test Collection".to_string())
.uri("https://test.com".to_string())
.invoke()?;

Common Errors

AccountNotInitialized

The Asset or Collection account doesn't exist or hasn't been created yet.

PluginNotFound

The plugin you're trying to fetch doesn't exist on the Asset. Check with fetch_plugin() which returns None safely.

InvalidAuthority

The signer doesn't have permission for this operation. Verify the correct authority is signing.

Notes

  • Always enable features = ["anchor"] for native deserialization
  • Use fetch_plugin() for built-in plugins, fetch_external_plugin() for external
  • CPI builders abstract away account ordering complexity
  • Check docs.rs/mpl-core for complete API reference

Quick Reference

Common CPI Builders

OperationCPI Builder
Create AssetCreateV2CpiBuilder
Create CollectionCreateCollectionV2CpiBuilder
Transfer AssetTransferV1CpiBuilder
Burn AssetBurnV1CpiBuilder
Update AssetUpdateV1CpiBuilder
Add PluginAddPluginV1CpiBuilder
Update PluginUpdatePluginV1CpiBuilder

Account Types

AccountStruct
AssetBaseAssetV1
CollectionBaseCollectionV1
Hashed AssetHashedAssetV1
Plugin HeaderPluginHeaderV1
Plugin RegistryPluginRegistryV1

FAQ

Do I need the anchor feature flag?

Yes, for direct deserialization in Accounts structs. Without it, use from_bytes() manually.

How do I check if a plugin exists?

Use fetch_plugin() which returns Option - it won't throw an error if the plugin doesn't exist.

Can I access external plugins (Oracle, AppData)?

Yes. Use fetch_external_plugin() instead of fetch_plugin() with the appropriate key.

Where can I find all available instructions?

See the mpl-core docs.rs instructions module.

Glossary

TermDefinition
CPICross-Program Invocation - calling one program from another
CpiBuilderHelper struct for constructing CPI calls
BaseAssetV1Core Asset account struct for deserialization
fetch_plugin()Function to read plugin data from accounts
anchor featureCargo feature enabling Anchor-native deserialization