Jacky Gu

Solana开发笔记: 使用anchor程序铸造代币

01 Jan 2025 Share to

使用anchor程序铸造代币

前面一篇中,详细介绍了初始化一个Mint铸币厂,在这篇里,我们完成铸造代币。

创建mint token指令

在之前基础上,mint token的指令相对简单,在instructions目录下创建mint_tokens.rs,代码如下:

use anchor_lang::prelude::*;

use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token::{spl_token, Mint, mint_to, MintTo, Token, TokenAccount};
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,
    mint::authority = mint,
    mint::decimals = 9,
  )]
  pub mint: Box<Account<'info, Mint>>,
  #[account(
    init_if_needed,
    payer = user,
    associated_token::mint = mint,
    associated_token::authority = user, // destination is the ata account and user is the owner
  )]
  pub destination: Box<Account<'info, TokenAccount>>, // ata account
  #[account(mut)]
  pub user: Signer<'info>,
  pub rent: Sysvar<'info, Rent>,
  pub token_program: Program<'info, Token>,
  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::ID == ctx.accounts.token_program.key(), ErrorCode::WrongTokenProgram);

    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,
    ).map_err(|e| {
      msg!("Mint tokens to miner's ATA failed: {:?}", e);
      e
    })?;
    msg!("Mint tokens to miner's ATA successfully.");
    Ok(())
  }
}

上面代码比较好理解,要注意的是,destination是token account账户,也就是ATA账户。

在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 token", async () => {
    // mint tokens to user1
    const destinationAta = await getAssociatedTokenAddress(mintAccount, user1Account.publicKey, false, TOKEN_PROGRAM_ID);
    const context = {
      mint: mintAccount,
      destination: destinationAta,
      user: user1Account.publicKey,
      tokenProgram: TOKEN_PROGRAM_ID,
    };
    const amount = new anchor.BN(1000).mul(new anchor.BN(1000000000)); // 1000枚代币
    const tx = await program.methods.mintTokens(metadataParams.name, metadataParams.symbol, amount).accounts(context).signers([user1Account]).rpc();
    console.log("Your transaction signature", tx);
  })

上面测试代码中,使用getAssociatedTokenAddress获取ATA账户。

注意:这个时候是不确定destination是否已经初始化的,所以在程序中用了init_if_needed

另一个需要注意的地方是如果要使用init_if_needed,需要在Cargo.toml中配置:anchor-lang = {version = "0.30.1", features = ["init-if-needed"]}

运行测试脚本

anchor test --skip-local-validator

得到交易hash,把hash输入区块链浏览器,查看交易是否正常。

上图中显示铸币成功。