12 min read
Overviewβ
Metaplex recently announced a new lightweight NFT standard on Solana called Metaplex Core. This guide will teach you about Metaplex Core, why it is important, and how to mint your first NFT using Metaplex Core and the Umi JavaScript library.
Let's jump in!
What You Will Doβ
Write a script to mint a Metaplex Core NFT using the Umi JavaScript library.
What You Will Needβ
- Knowledge of Solana NFTs (Guide: Solana NFT Metadata Deep Dive)
- Nodejs (version 16.15 or higher) installed
- Typescript experience and ts-node installed
- Solana CLI installed
Dependencies Used in this Guideβ
| Dependency | Version |
|---|---|
| @metaplex-foundation/umi | ^0.9.1 |
| @metaplex-foundation/umi-bundle-defaults | ^0.9.1 |
| @metaplex-foundation/mpl-core | ^0.2.0 |
| @solana/web3.js | ^1.91.1 |
| solana cli | 1.18.1 |
Let's get started!
What is Metaplex Core?β
Metaplex Core is a lightweight Solana NFT standard that leverages a single-account design. This design allows for more efficient execution, lower minting costs, and increased composability. The original Metaplex standard is built on top of Solana's SPL token standard, which requires many accounts to mint an NFT (e.g., token account, metadata account, and master edition accounts). Their associated programs must be invoked to mint an NFT. Not only can this be tricky to manage, it can also be expensive (more than 0.02 SOL per mint compared to .0037) and utilize a lot of compute. The higher compute requirements can mean that transactions may be more likely to fail (due to exceeding the compute limit) and can be limiting when using Cross-Program Invocations (CPIs) to interact with NFTs in your own programs.
Core utilizes a single account structure with optional add-ons. Let's take a look. The Core account struct is defined as follows:
| Field | Size (bytes) | Description |
|---|---|---|
| key | 1 | The account discriminator. |
| owner | 32 | The owner of the asset. |
| update_authority | 33 | The update authority of the asset. |
| name | 4 + length | The name of the asset. |
| uri | 4 + length | The URI of the asset that points to the off-chain data. |
| seq | 1 + (optional, 8) | The sequence number used for indexing with compression. |
Source: GitHub: MPL Core
In addition to the Core account struct, Core also supports optional add-ons. Plugins can be attached to Core Assets or Collection Assets, allowing plugins to modify the behavior of a single asset or an entire collection. There are three types of plugins:
- Owner-managed plugins: These plugins are managed by the owner of the asset or collection.
- Authority-managed plugins: These plugins are managed by the authority of the asset or collection.
- Permanent: These plugins are permanent and cannot be removed. They must be initialized at the time of creation.
Here's a summary of the Plugins available in Core:
| Plug-in | Type | Available for | Description |
|---|---|---|---|
| Transfer Delegate | Owner-managed | Core Asset | Allows owner to delegate a program that can transfer the asset. |
| Freeze Delegate | Owner-managed | Core Asset | Allows owner to delegate a program that can freeze the asset. |
| Burn Delegate | Owner-managed | Core Asset | Allows owner to delegate a program that can burn the asset. |
| Royalties | Authority-managed | Core or Collection | Set royalties and rules for the asset. |
| Update Delegate | Authority-managed | Core or Collection | Allows authority to delegate a program that can update the asset. |
| Attribute | Authority-managed | Core or Collection | Stores key-value pairs of data (e.g., traits). |
| Permanent Transfer Delegate | Permanent | Core or Collection | Allows owner to delegate a program that can transfer the asset. |
| Permanent Freeze Delegate | Permanent | Core or Collection | Allows owner to delegate a program that can freeze the asset. |
| Permanent Burn Delegate | Permanent | Core or Collection | Allows owner to delegate a program that can burn the asset. |
Source: developers.metaplex.com
Let's try minting a Core NFT!
Create a New Projectβ
mkdir core-demo && cd core-demo && echo > app.ts
Install Solana Web3 dependencies:
yarn init -y
yarn add @metaplex-foundation/umi @metaplex-foundation/umi-bundle-defaults @metaplex-foundation/mpl-inscription @solana/web3.js@1
or
npm init -y
npm install @metaplex-foundation/umi @metaplex-foundation/umi-bundle-defaults @metaplex-foundation/mpl-core @solana/web3.js@1
Set Up Local Environmentβ
Fetch Core Programβ
You are welcome to use Solana Devnet, but we will use Localnet for this guide for faster transaction times. To use Core on Localnet, we must first deploy the Core program on our local network. To do this, we need to use the solana program dump command. This will effectively fetch the Core program from Solana's devnet and store it so we can deploy it on our local network.
In your terminal, run the following command:
solana program dump -u d CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d core.so
This will download the program executable stored on devnet for the Core program (Program ID: CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d) and save core.so in your current directory. Note where this file is saved, as we will need it later.
Import Dependenciesβ
In your project directory, open app.ts and import the necessary dependencies:
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
import {
createV1,
mplCore,
fetchAssetV1,
transferV1,
createCollectionV1,
getAssetV1GpaBuilder,
Key,
updateAuthority,
pluginAuthorityPair,
ruleSet
} from '@metaplex-foundation/mpl-core'
import { TransactionBuilderSendAndConfirmOptions, generateSigner, signerIdentity, sol } from '@metaplex-foundation/umi';
We are importing a few methods from the metaplex libraries. We will cover these in more detail as we use them.
Initialize Umiβ
Next, we will initialize Umi and set up our signer and payer. Add the following code to your app.ts file:
const umi = createUmi('http://127.0.0.1:8899', 'processed').use(mplCore())
const asset = generateSigner(umi);
const payer = generateSigner(umi);
umi.use(signerIdentity(payer));
const txConfig: TransactionBuilderSendAndConfirmOptions = {
send: { skipPreflight: true },
confirm: { commitment: 'processed' },
};
In this code snippet, we are initializing Umi with the localnet endpoint and setting the processed commitment level (to allow for faster processing times in our demonstration). We are also generating a signer for the asset and payer. The payer will be used to pay for the transaction fees (this is set by using umi.use).
We also define a txConfig object that we will pass in our transactions to set the skipPreflight and commitment options.