Hello! Welcome back to our module on working with Solana tokens.
In our last lesson, we established the fundamental architecture of the SPL Token program. You learned that a Mint Account serves as the unique blueprint for a token, defining its total supply and decimals, while Token Accounts (specifically Associated Token Accounts or ATAs) are what users own to hold their balances of that token.
Today, we transition from theory to practice. This lesson's goal is to perform a Cross-Program Invocation (CPI) to the SPL Token program to create a new token mint. We will write an Anchor program that not only creates the mint but also attaches the necessary metadata (like the token's name and symbol) using the Metaplex Token Metadata program, a standard practice in the Solana ecosystem.
The Anchor Advantage: Simplifying Mint Creation
If you were to create a mint account without Anchor, you would need to manually construct two separate instructions in a transaction:
- A call to the System Program to create an account with the correct size and assign its ownership to the Token Program.
- A call to the Token Program to run its
InitializeMintinstruction on that new account, setting its decimals and authorities.
This is where Anchor's power becomes evident. It abstracts this common two-step pattern into a single, declarative constraint within your Accounts struct.
Let's look at how we define the accounts context for our init_token instruction. The following guide provides a clean and professional example.
How to Create and Mint Fungible SPL Tokens Using Anchor
This guide from QuickNode, 'How to Create and Mint Fungible SPL Tokens Using Anchor', provides an excellent template for our program. We'll start by examining the account's context.
Please read the section 'Create the Init Token Context and Instruction'. Focus on the Rust code for the InitToken struct. Pay close attention to the #[account(...)] attributes, especially for the mint account.
The key innovation here is the #[account(init, ...)] macro on the mint field. Let's break down what it does for us:
init: This tells Anchor to create the account. Anchor will automatically make a CPI to the System Program before our instruction runs.payer = payer: Specifies that thepayeraccount in our context (which will be the user calling the instruction) should pay the rent for the new mint account.seeds = [b"mint"], bump: We're making our mint account a PDA derived from the string "mint". This gives our program a predictable address for the token mint it creates.mint::decimals = params.decimals: This is a constraint that passes thedecimalsvalue from our instruction parameters directly to the underlyingInitializeMintinstruction of the SPL Token program.mint::authority = mint: This sets the mint authority of the new token to be the mint account itself. This is a common pattern for program-controlled tokens. You could also set it to thepayer.
By using these declarative constraints, Anchor handles the complex CPI to both the System and Token programs, letting us focus on our program's unique logic.
Attaching Metadata with Metaplex
A token mint on its own only contains on-chain data like supply and authority. To make it useful in wallets and on explorers, it needs metadata: a name, a symbol, and a URI pointing to more information (like a JSON file with an image link).
This is handled by another standard program: the Metaplex Token Metadata program. Our Anchor program will make a second CPI, this time to the Metaplex program, to create a metadata account and link it to our new mint.
Implementing the init_token Instruction
Now let's see how this all comes together in the instruction logic. We'll continue with the QuickNode guide, but also use a video walkthrough that builds a similar program from scratch. The video provides a dynamic, step-by-step view, while the article serves as a clear, textual reference.
First, let's review the complete init_token function from the QuickNode guide you just read. The core of this function is the CPI to the Metaplex program.
The logic follows these steps:
- Prepare PDA Signer Seeds: It prepares the seeds for our
mintPDA, which is needed to sign the CPI on the program's behalf. - Define Metadata: It creates a
DataV2struct, which holds the name, symbol, and URI that we want to attach to our token. - Build CPI Context: It constructs a
CpiContextfor calling thecreate_metadata_accounts_v3function from the Metaplex program. This context meticulously lists all the accounts Metaplex needs, such as thepayer, themintwe are creating, themetadataaccount to be created, and the various required programs. - Execute CPI: It calls
create_metadata_accounts_v3, passing in the context and the token data.
To see this in action, the following video walks through building a very similar program. Watching this developer code the logic can help solidify your understanding of how the pieces connect.
Launch Your Own Solana SPL Token - Mint - Transfer - FINAL
This video from the net2dev channel, 'Launch Your Own Solana SPL Token', demonstrates the full implementation of a token creation program.
Watch the segment from 22:20 to 29:52. The instructor explains and writes the code for the initiate_token function. Notice the similarities to the QuickNode guide: defining structs for parameters and accounts, preparing a CPI context, and calling a function to create the metadata.
Here is the essential code snippet for the CPI from the QuickNode article, which is a bit more streamlined than the video's version:
// 1. Define the metadata for our token
let token_data: DataV2 = DataV2 {
name: metadata.name,
symbol: metadata.symbol,
uri: metadata.uri,
seller_fee_basis_points: 0,
creators: None,
collection: None,
uses: None,
};
// 2. Build the CPI context for the Metaplex program
let metadata_ctx = CpiContext::new_with_signer(
ctx.accounts.token_metadata_program.to_account_info(),
CreateMetadataAccountsV3 {
payer: ctx.accounts.payer.to_account_info(),
update_authority: ctx.accounts.mint.to_account_info(),
mint: ctx.accounts.mint.to_account_info(),
metadata: ctx.accounts.metadata.to_account_info(),
mint_authority: ctx.accounts.mint.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
rent: ctx.accounts.rent.to_account_info(),
},
&signer // The PDA seeds for our mint account
);
// 3. Execute the CPI to create the metadata account
create_metadata_accounts_v3(
metadata_ctx,
token_data,
false, // is_mutable
true, // update_authority_is_signer
None, // collection_details
)?;
This sequence is a cornerstone pattern in Solana development: using Anchor's init for creating SPL accounts and then making explicit CPIs to other programs like Metaplex to add functionality.
Testing the Mint Creation
With your background in front-end development, you'll appreciate how seamlessly Anchor generates a client for interacting with the program. Let's see how to write a TypeScript test to call our new init_token instruction.
The test will:
- Define the metadata for the token (name, symbol, etc.).
- Derive the PDA for the mint account on the client side, so we can pass it to the instruction.
- Construct and send the transaction.
- Assert that the mint account was actually created.
The QuickNode guide provides a perfect, concise example of this.
How to Create and Mint Fungible SPL Tokens Using Anchor
Let's look at the client-side test for our instruction.
Read the 'Test Init Token' section. Notice how pg.program.methods.initToken(metadata).accounts(context) is used to build the transaction. This directly corresponds to the Rust program you just reviewed.
This test demonstrates the full end-to-end flow, from a client-side request to on-chain execution, fulfilling our goal of creating a new token mint.
Test your understanding!
In the InitToken struct from the QuickNode guide, the mint account is defined with mint::authority = mint.
- What does this specific constraint achieve?
- What would be the implication if you changed it to
mint::authority = payer?
Show answer
mint::authority = mintsets the mint authority of the new token to be the mint account's PDA itself. This means that only our program, which can sign for its own PDA, has the power to mint new tokens in the future. This is a common pattern for creating tokens whose supply is controlled by program logic (e.g., a rewards token).- If you changed it to
mint::authority = payer, the mint authority would be assigned to the user who called theinit_tokeninstruction. This user's wallet would then have the direct power to mint new tokens using standard SPL Token CLI or library calls, completely outside of our program. This is useful if you want to create a token and give minting control to a specific individual or multi-sig wallet.
Conclusion
In this lesson, you've successfully bridged the gap between theory and practice by learning how to create a new SPL Token mint using Anchor.
Here are the key takeaways:
- Anchor's
initConstraint: You learned how the#[account(init, ...)]macro is a powerful abstraction that handles the CPIs to both the System Program (to create an account) and the SPL Token Program (to initialize it as a mint). - CPIs for Functionality: Creating a fully-featured token requires CPIs to multiple programs. We focused on the essential CPI to the Metaplex Token Metadata program to attach a name, symbol, and URI.
- The
CpiContext: You saw how to construct aCpiContextto securely pass accounts and signer privileges from your program to another program during a CPI. - End-to-End Flow: You reviewed the full development cycle, from writing the on-chain Rust program to testing it with a client-side TypeScript script.
In our next lesson, we will continue building out our token program. Now that we have a mint, we need to provide a way for users to own and store these tokens. We will implement the logic to perform a CPI to the Associated Token Account program to create a token account for a user.
Can't find a good explanation? Sign up and we'll make it for you
Sign up