Jacky Gu

Solana开发笔记: Token2022 - 铸造代币

01 Jan 2025 Share to

Token2022 - 铸造代币

这里,我们给出例子来说明如何从程序中铸造代币。

创建mint_tokens指令

instructions中添加mint_tokens.rs,并粘贴以下代码:

use anchor_lang::prelude::*;
use anchor_spl::token_2022::{
  mint_to, spl_token_2022, MintTo,
};
use anchor_spl::{
  token_interface::{
      Token2022, TokenAccount, Mint
  },
  associated_token::AssociatedToken,
};
use crate::constants::MINT_SEEDS;
use crate::errors::ErrorCode;

#[derive(Accounts)]
#[instruction(token_name: String, token_symbol: String)]
pub struct MintTokens<'info> {
  #[account(
    mut,
    seeds = [MINT_SEEDS.as_bytes(), token_name.as_bytes(), token_symbol.as_bytes()],
    bump
  )]
  pub mint: Box<InterfaceAccount<'info, Mint>>,
  #[account(mut)]
  pub destination: Box<InterfaceAccount<'info, TokenAccount>>, // ata account for token
  #[account(mut)]
  pub user: Signer<'info>,
  pub rent: Sysvar<'info, Rent>,
  pub token_program: Program<'info, Token2022>,
  pub system_program: Program<'info, System>,
  pub associated_token_program: Program<'info, AssociatedToken>,
}

impl<'info> MintTokens<'info> {
  pub fn mint_tokens(ctx: Context<MintTokens>, token_name: String, token_symbol: String, amount: u64) -> Result<()> {
    require!(spl_token_2022::ID == ctx.accounts.token_program.key(), ErrorCode::WrongTokenProgram);

    // Mint tokens to miner's ATA
    let seeds = &[MINT_SEEDS.as_bytes(), token_name.as_bytes(), token_symbol.as_bytes(), &[ctx.bumps.mint]];
    let signer = [&seeds[..]];
    mint_to(
      CpiContext::new_with_signer(
        ctx.accounts.token_program.to_account_info(),
        MintTo {
          mint: ctx.accounts.mint.to_account_info(),
          to: ctx.accounts.destination.to_account_info(),
          authority: ctx.accounts.mint.to_account_info(),
        },
        &signer,
      ),
      amount.try_into().unwrap(),
    )?;
    Ok(())
  }
}

在lib.rs中调用上面的指令

pub fn mint_tokens(ctx: Context<MintTokens>, token_name: String, token_symbol: String, amount: u64) -> Result<()> {
    MintTokens::mint_tokens(ctx, token_name, token_symbol, amount)
}

编译部署

anchor build && anchor deploy

编写测试代码

  it("Mint tokens by user1!", async () => {
    const destinationAta = await getAssociatedTokenAddress(mintAccount, user1Account.publicKey, false, TOKEN_2022_PROGRAM_ID);
    const destinationAtaInfo = await provider.connection.getAccountInfo(destinationAta);
    const context = {
      user: user1Account.publicKey,
      mint: mintAccount,
      destination: destinationAta,
      tokenProgram: TOKEN_2022_PROGRAM_ID,
    }

    const amount = new anchor.BN(100).mul(new anchor.BN(1000000000)); // mint 100个
    const ix = await program.methods
      .mintTokens(metadataParams.name, metadataParams.symbol, amount)
      .accounts(context)
      .instruction();
    const tx = new anchor.web3.Transaction();
    if (destinationAtaInfo == null) {
      // 如果目标账户没有创建Token account,需要先创建
      const instructionDestinationAta = createAssociatedTokenAccountInstruction(
        user1Account.publicKey,
        destinationAta,
        user1Account.publicKey,
        mintAccount,
        TOKEN_2022_PROGRAM_ID,
      )
      tx.add(instructionDestinationAta);
    }
    tx.add(ix);
    const signature = await provider.sendAndConfirm(tx, [user1Account]);
    console.log("Minted token tx:", signature);
  })

检查

将上面运行后的tx输入区块链浏览器,如下图:

从前端发送了两个指令(Instruction),一个是用于在目标账户尚未创建Token account,需要先创建,另一个是用于mint tokens。