Create your own
Lesson illustration

Minting New Tokens to a Token Account

Hello! Welcome back to our module on working with Solana tokens.

In our previous lessons, we've laid all the necessary groundwork. We learned how to:

  1. Create a token mint, which acts as the blueprint for our new token.
  2. Create an Associated Token Account (ATA), which gives a user a dedicated place to hold that token.

Now, we've reached the most rewarding part: putting tokens into circulation. The goal for this lesson is to implement the program logic to mint new tokens to a token account. This is the process of creating tokens out of thin air and depositing them into a user's account, a fundamental action for everything from gaming rewards to decentralized finance (DeFi) protocols.

We will achieve this by performing another Cross-Program Invocation (CPI), this time calling the mint_to instruction on the SPL Token Program.

The Authority to Mint

Before we write any code, let's address the most important question: who is allowed to mint new tokens?

When we created our token mint in a previous lesson, we assigned its mint_authority to a Program Derived Address (PDA) owned by our program. This was a deliberate and crucial design choice. It means that only our program can authorize the creation of new tokens. No outside user or program can do it.

How does our program prove it has this authority? When we make a CPI to the SPL Token Program's mint_to instruction, our program must "sign" the transaction on behalf of its PDA. This special type of signature is achieved by providing the seeds and bump used to derive the PDA, which only our program knows. Anchor makes this process straightforward, as we'll see.

Minting Tokens with an Anchor CPI

Let's explore a practical example. We'll look at a simple game program where defeating an enemy rewards the player with one token. This is a perfect, self-contained use case for minting.

The following guide from the official Solana documentation walks through this exact scenario.

How to interact with tokens in programs

Please read the following guide, 'How to interact with tokens in programs'. It contains an excellent example of a token-minting instruction called kill_enemy.

Focus on the section titled 'Kill Enemy Instruction'. Read through the description of the required accounts and study the Rust code for both the kill_enemy function and the KillEnemy Accounts struct.

Let's break down the key components from that example.

1. The KillEnemy Accounts Struct

The #[derive(Accounts)] struct defines all the accounts our instruction needs to perform the mint.

#[derive(Accounts)]
pub struct KillEnemy<'info> {
    #[account(mut)]
    pub player: Signer<'info>, // The payer for ATA creation if needed

    // ... other accounts ...

    // The destination for the new tokens. 
    // `init_if_needed` is used here just like in our last lesson.
    #[account(
        init_if_needed,
        payer = player,
        associated_token::mint = reward_token_mint,
        associated_token::authority = player
    )]
    pub player_token_account: Account<'info, TokenAccount>,

    // The mint account of the token we are minting.
    // It must be mutable because its `supply` field will be updated.
    #[account(
        mut,
        seeds = [b"reward"], // The PDA seeds for our mint account
        bump,
    )]
    pub reward_token_mint: Account<'info, Mint>,

    // The SPL Token program, which we will be calling via CPI.
    pub token_program: Program<'info, Token>,
    // ... other programs ...
}

Key points here:

  • reward_token_mint: This is the mint account we created previously. It's marked mut because the mint_to instruction will increase its total supply. Its PDA derivation is specified with seeds and bump.
  • player_token_account: This is the user's ATA where the newly minted tokens will be deposited. It is also mut because its balance will increase.
  • token_program: We must include the Token program itself, as it's the target of our CPI.

2. The kill_enemy Instruction Logic

The function body contains the logic for making the CPI.

pub fn kill_enemy(ctx: Context<KillEnemy>) -> Result<()> {
    // ... (health subtraction logic) ...

    // 1. Define the seeds for the PDA that is the mint authority.
    let seeds = b"reward";
    let bump = *ctx.bumps.get("reward_token_mint").unwrap();
    let signer: &[&[&[u8]]] = &[&[seeds, &[bump]]];

    // 2. Create the CpiContext for the `mint_to` instruction.
    let cpi_ctx = CpiContext::new_with_signer(
        ctx.accounts.token_program.to_account_info(), // Target program
        MintTo {                                     // Instruction accounts
            mint: ctx.accounts.reward_token_mint.to_account_info(),
            to: ctx.accounts.player_token_account.to_account_info(),
            authority: ctx.accounts.reward_token_mint.to_account_info(), // The PDA is the authority
        },
        signer,                                      // The PDA's "signature"
    );

    // 3. Handle token decimals.
    let amount = (1u64)
        .checked_mul(10u64.pow(ctx.accounts.reward_token_mint.decimals as u32))
        .unwrap();

    // 4. Execute the CPI.
    mint_to(cpi_ctx, amount)?;

    Ok(())
}

This sequence is a fundamental pattern in Anchor:

  1. Prepare the Signer: We assemble the seeds and bump into a signer slice. This is how our program will sign on behalf of the reward_token_mint PDA.
  2. Build the CpiContext: We use CpiContext::new_with_signer because a PDA is signing. We pass it:
    • The token_program we are calling.
    • A MintTo struct, which is part of anchor_spl::token and maps directly to the accounts required by the SPL Token program's mint_to instruction. The authority is our reward_token_mint account itself, which is the PDA.
    • The signer seeds we just prepared.
  3. Calculate the Amount: This is a subtle but critical step. SPL tokens have a decimals property. A token with 9 decimals means that 1 whole token is represented by the integer 1,000,000,000. The code correctly calculates the raw integer amount to mint 1 whole token by accounting for these decimals.
  4. Invoke mint_to: Finally, we call the mint_to function from the anchor_spl::token crate, passing our context and the amount. Anchor handles the invoke_signed call under the hood.
Test your understanding!

In the MintTo struct within the CPI context, the authority is specified as ctx.accounts.reward_token_mint.to_account_info(). We know this account is a PDA.

What would happen if you tried to set the authority to ctx.accounts.player.to_account_info() instead, thinking the player should authorize the minting to themselves?

Show answer

The transaction would fail with an authority error (specifically, ConstraintMintAuthority).

The SPL Token program doesn't care who is receiving the tokens. It only cares about who is authorizing the creation of new tokens. It checks the signatures passed to the instruction (player's signature, in this hypothetical) against the mint_authority field stored on the mint account data. Since the mint account's authority is the PDA (reward_token_mint), and not the player, the signature check fails, and the CPI is rejected.

Generalizing the Pattern

The pattern of using CpiContext::new_with_signer to call mint_to is not specific to gaming. It's used in any scenario where a program needs to distribute tokens, such as distributing staking rewards.

The following resource provides another concise example of this same pattern.

A Developer's Guide to Solana's CPI Patterns

To see how versatile this pattern is, let's look at another example from 'A Developer's Guide to Solana's CPI Patterns'. This shows the same mint_to CPI but in the context of a DeFi staking protocol.

Quickly review the code snippet under 'The Staking Rewards Pattern'. Notice the nearly identical structure: a CpiContext::new_with_signer is created, it's passed to token::mint_to, and a PDA's seeds are used for the signer.

This reinforces that once you understand the core mint_to CPI pattern, you can apply it across many different types of Solana programs.

Conclusion

Congratulations! You have now learned how to complete the initial lifecycle of a token: creating the mint, creating an account to hold it, and now, minting tokens into that account.

Here are the key takeaways from this lesson:

  • Minting new SPL tokens is done via a CPI to the mint_to instruction of the SPL Token Program.
  • To maintain control, the mint_authority of the token mint should be a PDA owned by your program.
  • In Anchor, you use CpiContext::new_with_signer to call mint_to, providing the PDA's seeds as the signer. This allows your program to authorize the mint.
  • The MintTo struct from anchor_spl::token provides a type-safe way to specify the accounts for the CPI: the mint, the destination to account, and the authority PDA.
  • Always remember to account for the token's decimals when specifying the amount to mint.

We've focused on bringing tokens into existence and giving them to a user. In our next and final lesson of this module, we will learn how to enable the movement of these tokens between users by implementing a token transfer instruction.

Can't find a good explanation? Sign up and we'll make it for you

Sign up