Features
Minting Assets
As we discussed in the Token Metadata overview, digital assets on Solana are composed of several onchain accounts and off-chain data describing the token. On this page, we'll go over the process of minting these assets.
The minting process
Whether we want to mint a Fungible, Semi-Fungible or Non-Fungible asset, the overall process is the same:
- Upload off-chain data. First, we must ensure our off-chain data is ready. This means we must have a JSON file stored somewhere that describes our asset. It doesn't matter how or where that JSON file is stored, as long as it's accessible via a URI.
- Create onchain accounts. Then, we must create the onchain accounts that will hold our asset's data. Which exact accounts will be created depends on the Token Standard of our asset, but in all cases, a Metadata account will be created and will store the URI of our off-chain data.
- Mint tokens. Finally, we must mint the tokens associated with all these accounts. For Non-Fungible assets, that simply means minting from 0 to 1, since Non-Fungibility forbids us to have a supply greater than 1. For Fungible or Semi-Fungible assets, we may mint however many tokens we want.
Let's dig into these steps in more detail, whilst providing concrete code examples.
Uploading off-chain data
You may use any service to upload your off-chain data or simply store it on your own server but it is worth noting that the Umi SDK can help with that. It uses a plugin system that allows you to select the uploader of your choice and offers a unified interface for you to upload your data.
Upload assets and JSON data
const [imageUri] = await umi.uploader.upload([imageFile])
const uri = await umi.uploader.uploadJson({
name: 'My NFT',
description: 'This is my NFT',
image: imageUri,
// ...
})
Now that we have our URI, we can move on to the next step.
The next steps show how to create accounts and mint the tokens in two steps. At the bottom of the page there are code examples for helpers that combine those steps and make creating different token types easier.
Creating Mint and Metadata accounts
To create all the onchain accounts required by the Token Standard of your choice, you may simply use the Create V1 instruction. It will adapt to the requested Token Standard and create the right accounts accordingly.
For instance, NonFungible assets will have a Metadata account and a MasterEdition account created, whereas Fungible assets will only have a Metadata account created.
Additionally, if the provided Mint account does not exist, it will be created for us. That way, we don't even need to call the underlying Token program to prepare our token before adding metadata to it.
This instruction accepts a variety of parameters and our SDKs do their best to provide default values to them so you don't need to fill all of them every single time. That being said, here is a list of parameters that you may be interested in:
- Mint: The Mint account of the asset. If it doesn't exist, it must be provided as a Signer as it will be initialized. Typically, we generate a new keypair for this purpose.
- Authority: The authority of the Mint account. This is the account that is or will be allowed to mint tokens from the Mint account. This will default to the "Identity" wallet — i.e. the connected wallet — if supported by the SDK.
- Name, URI, Seller Fee Basis Points, Creators, etc.: The data of the asset to store on the Metadata account.
- Token Standard: The Token Standard of the asset.
createV1 is a helper function that can initialize the Mint Account and create the Metadata Account. If the mint exists already it will only create the metadata Account. If you are looking for how to use createMetadataAccountV3 you should be using this function instead.
1import { generateSigner, percentAmount } from '@metaplex-foundation/umi';
2import {
3 createV1,
4 TokenStandard,
5} from '@metaplex-foundation/mpl-token-metadata';
6
7// Assuming umi is set up with mplTokenMetadata plugin
8// See getting-started for full setup
9
10const mint = generateSigner(umi);
11
12// Create the onchain accounts (Mint + Metadata + MasterEdition for NFTs)
13await createV1(umi, {
14 mint,
15 authority: umi.identity,
16 name: 'My NFT',
17 uri: 'https://example.com/my-nft.json',
18 sellerFeeBasisPoints: percentAmount(5.5),
19 tokenStandard: TokenStandard.NonFungible,
20}).sendAndConfirm(umi);
21
22console.log('Created NFT accounts');
23console.log('Mint:', mint.publicKey);
1import { generateKeyPairSigner } from '@solana/kit';
2import {
3 getCreateV1InstructionAsync,
4 TokenStandard,
5} from '@metaplex-foundation/mpl-token-metadata-kit';
6
7// Assuming rpc, rpcSubscriptions, and sendAndConfirm are set up
8// See getting-started for full setup
9
10const mint = await generateKeyPairSigner();
11const authority = await generateKeyPairSigner(); // Your wallet
12
13// Create the onchain accounts (Mint + Metadata + MasterEdition for NFTs)
14const createIx = await getCreateV1InstructionAsync({
15 mint,
16 authority,
17 payer: authority,
18 name: 'My NFT',
19 uri: 'https://example.com/my-nft.json',
20 sellerFeeBasisPoints: 550, // 5.5%
21 tokenStandard: TokenStandard.NonFungible,
22});
23
24// Send the transaction
25await sendAndConfirm({
26 instructions: [createIx],
27 payer: authority,
28});
29
30console.log('Created NFT accounts');
31console.log('Mint:', mint.address);
1use mpl_token_metadata::{
2 accounts::Metadata,
3 instructions::CreateV1CpiBuilder,
4 types::{PrintSupply, TokenStandard},
5};
6
7// 1. every account is specified by a reference to their AccountInfo
8
9let create_cpi = CreateV1CpiBuilder::new(token_metadata_program_info)
10 .metadata(metadata_info)
11 .mint(mint_info, true)
12 .authority(payer_info)
13 .payer(payer_info)
14 .update_authority(update_authority_info, false)
15 .master_edition(Some(master_edition_info))
16 .system_program(system_program_info)
17 .sysvar_instructions(sysvar_instructions_info)
18 .spl_token_program(spl_token_program_info)
19 .token_standard(TokenStandard::NonFungible)
20 .name(String::from("My NFT"))
21 .uri(uri)
22 .seller_fee_basis_points(550)
23 .token_standard(TokenStandard::NonFungible)
24 .print_supply(PrintSupply::Zero);
25
26create_cpi.invoke();
Note that when setting the mint account in Rust, it is required to specify a bool flag to indicate whether the account will be a signer or not – it needs to be a signer if the mint account does not exist.
Minting Tokens
Once all onchain accounts are created for our asset, we can mint tokens for it. If the asset is Non-Fungible we will simply mint its one and only token, otherwise we can mint as many tokens as we want. Note that a Non-Fungible asset is only valid once its unique token has been minted so it is a mandatory step for that Token Standard.
We can use the Mint V1 instruction of the Token Metadata program to achieve this. It requires the following parameters:
- Mint: The address of the asset's Mint account.
- Authority: The authority that can authorize this instruction. For Non-Fungible assets, this is the update authority of the Metadata account, otherwise, this refers to the Mint Authority of the Mint account.
- Token Owner: The address of the wallet to receive the token(s).
- Amount: The number of tokens to mint. For Non-Fungible assets, this may only be 1.
- Token Standard: The Token Standard of the asset (required for our JavaScript SDK). The program does not require this argument but our SDK do so they can provide adequate default values for most of the other parameters.
1import { mintV1, TokenStandard } from '@metaplex-foundation/mpl-token-metadata';
2
3// Assuming umi is set up with mplTokenMetadata plugin
4// mint from createV1
5
6const mintPublicKey = mint.publicKey; // From the created mint
7const tokenOwner = umi.identity.publicKey; // Wallet to receive the token
8
9// Mint the NFT token
10await mintV1(umi, {
11 mint: mintPublicKey,
12 authority: umi.identity,
13 amount: 1,
14 tokenOwner,
15 tokenStandard: TokenStandard.NonFungible,
16}).sendAndConfirm(umi);
17
18console.log('Minted NFT to:', tokenOwner);
1import {
2 getMintV1InstructionAsync,
3 TokenStandard,
4} from '@metaplex-foundation/mpl-token-metadata-kit';
5
6// Assuming rpc, rpcSubscriptions, and sendAndConfirm are set up
7// mint and authority from createV1
8
9const mintAddress = mint.address; // From the created mint
10const tokenOwner = authority.address; // Wallet to receive the token
11
12// Mint the NFT token
13const mintIx = await getMintV1InstructionAsync({
14 mint: mintAddress,
15 authority,
16 payer: authority,
17 amount: 1,
18 tokenOwner,
19 tokenStandard: TokenStandard.NonFungible,
20});
21
22await sendAndConfirm({
23 instructions: [mintIx],
24 payer: authority,
25});
26
27console.log('Minted NFT to:', tokenOwner);
1use mpl_token_metadata::instructions::MintV1CpiBuilder;
2
3// 1. every account is specified by a reference to their AccountInfo
4
5let mint_cpi = MintV1CpiBuilder::new(token_metadata_program_info)
6 .token(token_info)
7 .token_owner(Some(token_owner_info))
8 .metadata(metadata_info)
9 .master_edition(Some(master_edition_info))
10 .mint(mint_info)
11 .payer(payer_info)
12 .authority(update_authority_info)
13 .system_program(system_program_info)
14 .sysvar_instructions(sysvar_instructions_info)
15 .spl_token_program(spl_token_program_info)
16 .spl_ata_program(spl_ata_program_info)
17 .amount(1);
18
19mint_cpi.invoke();
We are setting the master_edition since it is required to mint a NonFungible; the token_owner is required if the token account does not exist and one will be initialized.
Create Helpers
Since creating digital assets is such an important part of Token Metadata, our SDKs provide helper methods to make the process easier. Namely, these helper methods combine the Create V1 and Mint V1 instructions together in different ways, depending on the Token Standard we want to create.
Create helpers
