Building with Metaplex Core: The Next-Gen NFT Standard on Solana
Metaplex Core reduces NFT minting costs by 80% compared to the legacy Token Metadata standard. With a single-account architecture and built-in plugin system, Core represents the future of digital assets on Solana. This guide walks you through creating, managing, and extending NFTs with the new standard.
TL;DR
- **Core uses a single account** per asset instead of 5+ accounts (80% cost reduction)
- **Plugins replace metadata extensions** - royalties, freeze authority, and custom behaviors built-in
- **Collections are first-class citizens** - native collection management without separate verification
- **Lifecycle hooks** enable programmable behaviors on transfer, burn, and update
- **Full backwards compatibility** - existing tools and marketplaces support Core assets
Prerequisites
Before building with Metaplex Core, ensure you have:
- Node.js 18+ and npm/yarn installed
- Basic TypeScript knowledge
- A Solana wallet with devnet SOL
- Understanding of Solana's account model
Package versions used in this guide:
- `@metaplex-foundation/mpl-core`: 1.7.0
- `@metaplex-foundation/umi`: 0.9.2
- `@metaplex-foundation/umi-bundle-defaults`: 0.9.2
- `@solana/web3.js`: 1.98.4
Why Metaplex Core?
The legacy Token Metadata standard served Solana well, but its multi-account architecture created friction:
| Aspect | Token Metadata | Metaplex Core |
|--------|---------------|---------------|
| Accounts per NFT | 5-7 | 1 |
| Mint Cost | ~0.02 SOL | ~0.004 SOL |
| Collection Verification | Separate TX | Built-in |
| Royalty Enforcement | Off-chain | On-chain plugins |
| Custom Behaviors | Not supported | Lifecycle hooks |
Core's single-account design stores everything - ownership, metadata, plugins - in one place.
Setting Up Your Environment
Install Dependencies
npm install @metaplex-foundation/mpl-core \
@metaplex-foundation/umi \
@metaplex-foundation/umi-bundle-defaults \
@metaplex-foundation/umi-uploader-irys \
@solana/web3.jsInitialize Umi Client
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import { mplCore } from '@metaplex-foundation/mpl-core';
import { irysUploader } from '@metaplex-foundation/umi-uploader-irys';
import {
keypairIdentity,
generateSigner,
percentAmount
} from '@metaplex-foundation/umi';
import { base58 } from '@metaplex-foundation/umi/serializers';
// Initialize Umi with Core plugin
const umi = createUmi('https://api.devnet.solana.com')
.use(mplCore())
.use(irysUploader());
// Load your wallet keypair
// In production, use a secure key management solution
import { readFileSync } from 'fs';
const walletFile = JSON.parse(
readFileSync('/path/to/keypair.json', 'utf-8')
);
const keypair = umi.eddsa.createKeypairFromSecretKey(
new Uint8Array(walletFile)
);
umi.use(keypairIdentity(keypair));
console.log('Umi initialized with wallet:', umi.identity.publicKey);Creating Your First Core Asset
Step 1: Upload Metadata to Arweave
import { createGenericFile } from '@metaplex-foundation/umi';
// Upload the image first
const imageFile = createGenericFile(
readFileSync('./my-nft-image.png'),
'my-nft-image.png',
{ contentType: 'image/png' }
);
const [imageUri] = await umi.uploader.upload([imageFile]);
console.log('Image uploaded:', imageUri);
// Create and upload metadata JSON
const metadata = {
name: 'Builderz Genesis #1',
symbol: 'BLDZ',
description: 'The first NFT in the Builderz Genesis collection',
image: imageUri,
attributes: [
{ trait_type: 'Background', value: 'Cosmic Purple' },
{ trait_type: 'Rarity', value: 'Legendary' },
{ trait_type: 'Edition', value: '1 of 100' }
],
properties: {
files: [{ uri: imageUri, type: 'image/png' }],
category: 'image'
}
};
const metadataUri = await umi.uploader.uploadJson(metadata);
console.log('Metadata uploaded:', metadataUri);Step 2: Create the Core Asset
import { create, fetchAsset } from '@metaplex-foundation/mpl-core';
// Generate a new keypair for the asset
const asset = generateSigner(umi);
// Create the Core asset
const tx = await create(umi, {
asset,
name: 'Builderz Genesis #1',
uri: metadataUri,
}).sendAndConfirm(umi);
const signature = base58.deserialize(tx.signature)[0];
console.log('Asset created!');
console.log('Signature:', signature);
console.log('Asset address:', asset.publicKey);
// Fetch and verify the asset
const fetchedAsset = await fetchAsset(umi, asset.publicKey);
console.log('Asset details:', {
name: fetchedAsset.name,
uri: fetchedAsset.uri,
owner: fetchedAsset.owner,
updateAuthority: fetchedAsset.updateAuthority
});Working with Collections
Collections in Core are native - no separate verification transactions needed.
Create a Collection
import {
createCollection,
fetchCollection
} from '@metaplex-foundation/mpl-core';
const collection = generateSigner(umi);
// Upload collection metadata
const collectionMetadata = {
name: 'Builderz Genesis Collection',
symbol: 'BLDZ',
description: 'The official Builderz Genesis NFT collection',
image: imageUri,
external_url: 'https://builderz.dev'
};
const collectionUri = await umi.uploader.uploadJson(collectionMetadata);
// Create the collection
const collectionTx = await createCollection(umi, {
collection,
name: 'Builderz Genesis Collection',
uri: collectionUri,
}).sendAndConfirm(umi);
console.log('Collection created:', collection.publicKey);Mint Assets to a Collection
import { create } from '@metaplex-foundation/mpl-core';
const collectedAsset = generateSigner(umi);
const mintTx = await create(umi, {
asset: collectedAsset,
name: 'Builderz Genesis #2',
uri: metadataUri,
collection: collection.publicKey, // Reference the collection
}).sendAndConfirm(umi);
console.log('Asset minted to collection:', collectedAsset.publicKey);Plugins: Extending Asset Functionality
Plugins are Core's superpower - they enable custom behaviors without external programs.
Built-in Plugin Types
| Plugin | Purpose |
|--------|---------|
| Royalties | Enforce creator royalties on transfers |
| FreezeDelegate | Allow delegated freezing of assets |
| TransferDelegate | Enable delegated transfers |
| BurnDelegate | Allow delegated burning |
| UpdateDelegate | Delegate metadata updates |
| Attributes | On-chain key-value storage |
| Edition | Print edition management |
Adding Royalty Plugin
import {
create,
ruleSet,
addPlugin
} from '@metaplex-foundation/mpl-core';
const assetWithRoyalties = generateSigner(umi);
// Create asset with royalties plugin
const tx = await create(umi, {
asset: assetWithRoyalties,
name: 'Royalty-Enabled Asset',
uri: metadataUri,
plugins: [
{
type: 'Royalties',
basisPoints: 500, // 5% royalty
creators: [
{
address: umi.identity.publicKey,
percentage: 100
}
],
ruleSet: ruleSet('None'), // No additional rules
}
]
}).sendAndConfirm(umi);
console.log('Asset with royalties created:', assetWithRoyalties.publicKey);Adding Freeze Delegate Plugin
import {
addPlugin,
freezeAsset,
thawAsset
} from '@metaplex-foundation/mpl-core';
// Add freeze delegate plugin to existing asset
await addPlugin(umi, {
asset: asset.publicKey,
plugin: {
type: 'FreezeDelegate',
frozen: false,
authority: {
type: 'Address',
address: umi.identity.publicKey
}
}
}).sendAndConfirm(umi);
// Freeze the asset
await freezeAsset(umi, {
asset: asset.publicKey,
delegate: umi.identity
}).sendAndConfirm(umi);
console.log('Asset frozen');
// Thaw the asset later
await thawAsset(umi, {
asset: asset.publicKey,
delegate: umi.identity
}).sendAndConfirm(umi);
console.log('Asset thawed');On-Chain Attributes Plugin
Store game state, achievements, or any key-value data directly on-chain:
import { addPlugin, updatePlugin } from '@metaplex-foundation/mpl-core';
// Add attributes plugin
await addPlugin(umi, {
asset: asset.publicKey,
plugin: {
type: 'Attributes',
attributeList: [
{ key: 'level', value: '1' },
{ key: 'experience', value: '0' },
{ key: 'lastPlayed', value: Date.now().toString() }
]
}
}).sendAndConfirm(umi);
// Update attributes (e.g., after gameplay)
await updatePlugin(umi, {
asset: asset.publicKey,
plugin: {
type: 'Attributes',
attributeList: [
{ key: 'level', value: '5' },
{ key: 'experience', value: '2500' },
{ key: 'lastPlayed', value: Date.now().toString() }
]
}
}).sendAndConfirm(umi);Transferring Core Assets
import { transfer, fetchAsset } from '@metaplex-foundation/mpl-core';
import { publicKey } from '@metaplex-foundation/umi';
const recipientAddress = publicKey('RECIPIENT_WALLET_ADDRESS');
// Transfer the asset
const transferTx = await transfer(umi, {
asset: asset.publicKey,
newOwner: recipientAddress,
}).sendAndConfirm(umi);
console.log('Asset transferred to:', recipientAddress);
// Verify the transfer
const updatedAsset = await fetchAsset(umi, asset.publicKey);
console.log('New owner:', updatedAsset.owner);Burning Core Assets
import { burn } from '@metaplex-foundation/mpl-core';
// Burn the asset (only owner can burn)
const burnTx = await burn(umi, {
asset: asset.publicKey,
}).sendAndConfirm(umi);
console.log('Asset burned successfully');Advanced: Lifecycle Hooks
Lifecycle hooks allow external programs to execute logic on Core asset events.
import { create, ExternalPluginAdapterSchema } from '@metaplex-foundation/mpl-core';
const hookAsset = generateSigner(umi);
// Create asset with lifecycle hook
const tx = await create(umi, {
asset: hookAsset,
name: 'Hooked Asset',
uri: metadataUri,
plugins: [
{
type: 'LifecycleHook',
hookedProgram: publicKey('YOUR_HOOK_PROGRAM_ID'),
// Define which events trigger the hook
schema: ExternalPluginAdapterSchema.Binary,
// Pass data to your hook program
data: new Uint8Array([1, 2, 3])
}
]
}).sendAndConfirm(umi);Complete Example: Mint a Collection with Royalties
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import { mplCore } from '@metaplex-foundation/mpl-core';
import { irysUploader } from '@metaplex-foundation/umi-uploader-irys';
import {
keypairIdentity,
generateSigner,
percentAmount,
publicKey
} from '@metaplex-foundation/umi';
import {
create,
createCollection,
fetchAsset,
fetchCollection,
ruleSet
} from '@metaplex-foundation/mpl-core';
import { base58 } from '@metaplex-foundation/umi/serializers';
import { readFileSync } from 'fs';
async function mintCollection() {
// Initialize Umi
const umi = createUmi('https://api.devnet.solana.com')
.use(mplCore())
.use(irysUploader());
// Load wallet
const walletFile = JSON.parse(
readFileSync(process.env.WALLET_PATH!, 'utf-8')
);
const keypair = umi.eddsa.createKeypairFromSecretKey(
new Uint8Array(walletFile)
);
umi.use(keypairIdentity(keypair));
console.log('Wallet:', umi.identity.publicKey);
// Step 1: Create Collection
const collection = generateSigner(umi);
const collectionMetadata = {
name: 'Builderz Genesis',
symbol: 'BLDZ',
description: 'Premium Solana development NFT collection',
image: 'https://arweave.net/your-collection-image',
external_url: 'https://builderz.dev'
};
const collectionUri = await umi.uploader.uploadJson(collectionMetadata);
await createCollection(umi, {
collection,
name: collectionMetadata.name,
uri: collectionUri,
}).sendAndConfirm(umi);
console.log('Collection created:', collection.publicKey);
// Step 2: Mint NFTs to collection with royalties
const mintCount = 5;
const assets: string[] = [];
for (let i = 1; i <= mintCount; i++) {
const asset = generateSigner(umi);
const nftMetadata = {
name: `Builderz Genesis #${i}`,
symbol: 'BLDZ',
description: `Genesis NFT #${i} from the Builderz collection`,
image: 'https://arweave.net/your-nft-image',
attributes: [
{ trait_type: 'Edition', value: i.toString() },
{ trait_type: 'Collection', value: 'Genesis' }
]
};
const nftUri = await umi.uploader.uploadJson(nftMetadata);
await create(umi, {
asset,
name: nftMetadata.name,
uri: nftUri,
collection: collection.publicKey,
plugins: [
{
type: 'Royalties',
basisPoints: 500, // 5%
creators: [
{
address: umi.identity.publicKey,
percentage: 100
}
],
ruleSet: ruleSet('None')
}
]
}).sendAndConfirm(umi);
assets.push(asset.publicKey);
console.log(`Minted NFT #${i}:`, asset.publicKey);
}
// Step 3: Verify collection
const fetchedCollection = await fetchCollection(umi, collection.publicKey);
console.log('\nCollection Summary:');
console.log('Name:', fetchedCollection.name);
console.log('URI:', fetchedCollection.uri);
console.log('Assets minted:', assets.length);
return {
collection: collection.publicKey,
assets
};
}
mintCollection()
.then(result => console.log('\nMinting complete!', result))
.catch(console.error);Common Pitfalls
- **Forgetting to fund your wallet** - Core operations require SOL for rent and transaction fees
- **Using wrong network** - Ensure your RPC matches your intended network (devnet/mainnet)
- **Plugin authority confusion** - Each plugin has its own authority; verify you're using the correct signer
- **Collection authority** - Only the collection authority can add assets to a collection
- **Immutable assets** - Once an asset has no update authority, it cannot be modified
Migration from Token Metadata
If you have existing Token Metadata NFTs, Metaplex provides migration tools:
// Migration is handled by Metaplex infrastructure
// Contact Metaplex for large-scale migration support
// Individual assets can be burned and re-minted as CoreNext Steps and Resources
Continue Learning:
- [Metaplex Core Documentation](https://developers.metaplex.com/core)
- [Umi Framework Guide](https://developers.metaplex.com/umi)
- [Core GitHub Repository](https://github.com/metaplex-foundation/mpl-core)
Build Something:
- Create a generative art collection with Core
- Implement a gaming item system with on-chain attributes
- Build a marketplace integration with royalty enforcement
Get Support:
- [Metaplex Discord](https://discord.gg/metaplex)
- [Solana Stack Exchange](https://solana.stackexchange.com)
Ready to build your NFT project on Metaplex Core? Builderz has extensive experience with the new standard. Contact us to discuss your project.



