LogoAnchor 中文文档

铸造代币

学习如何使用 Anchor 在 Solana 程序中铸造代币。涵盖通过跨程序调用(CPI)向 Token Program 创建新代币的代码示例。

如何铸造代币

铸造代币是指通过调用 Token Program 上的 mint_to 指令创建新代币单位的过程。只有被指定为铸币账户上的铸币权限的地址才能铸造新代币。该指令还要求在代币接收地址上存在一个目标代币账户。

Token ProgramToken Extension Program 通过类似的实现来达到相同的功能。

示例

要通过 Anchor 程序铸造代币,你需要向 Token Program 或 Token Extension Program 的 mint_to 指令发起跨程序调用(CPI)。

这意味着你要从程序中的指令调用 Token Program 或 Token Extension Program 上的 mint_to 指令。你的程序作为中介,向代币程序传递所需的账户、指令数据和签名。

通过 CPI 铸造代币

使用 token_interface::mint_to 函数向 Token Program 或 Token Extension Program 发起 CPI。该函数需要:

  1. MintTo 结构体,指定所需的账户:

    • mint - 用于创建新代币单位的铸币账户
    • to - 用于接收新铸造代币的目标代币账户
    • authority - 拥有铸造代币权限的铸币授权
  2. 铸造代币的 amount,以代币的基本单位(根据小数位数调整)表示。(例如:如果铸币有2位小数,amount 为100 = 1个代币)

传递给 mint_to 指令的铸币授权必须与铸币账户上存储的 mint_authority 匹配。此外,铸币授权必须是交易中的签名者。例如:

lib.rs
use anchor_lang::prelude::*;
use anchor_spl::{
    token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface},
};
 
declare_id!("3pX5NKLru1UBDVckynWQxsgnJeUN3N1viy36Gk9TSn8d");
 
#[program]
pub mod token_example {
    use super::*;
 
    pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64) -> Result<()> {
        let cpi_accounts = MintTo {
            mint: ctx.accounts.mint.to_account_info(),
            to: ctx.accounts.token_account.to_account_info(),
            authority: ctx.accounts.signer.to_account_info(),
        };
        let cpi_program = ctx.accounts.token_program.to_account_info();
        let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
        token_interface::mint_to(cpi_context, amount)?;
        Ok(())
    }
}
 
#[derive(Accounts)]
pub struct MintTokens<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(mut)]
    pub mint: InterfaceAccount<'info, Mint>,
    #[account(mut)]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    pub token_program: Interface<'info, TokenInterface>,
}

至少需要以下账户:

snippet
#[derive(Accounts)]
pub struct MintTokens<'info> {
    // 铸币授权
    #[account(mut)]
    pub signer: Signer<'info>,
    // 铸币账户
    #[account(mut)]
    pub mint: InterfaceAccount<'info, Mint>,
    // 目标代币账户
    #[account(mut)]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    // 代币程序
    pub token_program: Interface<'info, TokenInterface>,
}

在指令逻辑中,使用:

  • MintTo 结构体指定所需的账户
  • token_interface::mint_to 函数发起 CPI
snippet
pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64) -> Result<()> {
    // 创建 MintTo 结构体,包含 CPI 所需的账户
    let cpi_accounts = MintTo {
        mint: ctx.accounts.mint.to_account_info(),
        to: ctx.accounts.token_account.to_account_info(),
        authority: ctx.accounts.signer.to_account_info(),
    };
 
    // 在 CPI 中调用的程序
    let cpi_program = ctx.accounts.token_program.to_account_info();
 
    // 将账户和程序组合成 "CpiContext"
    let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
 
    // 调用 Token Program 上的 mint_to 指令
    token_interface::mint_to(cpi_context, amount)?;
    Ok(())
}

通过 CPI 使用 PDA 铸币授权铸造代币

你可以创建一个以 Program Derived Address (PDA) 作为铸币授权的铸币账户。这允许你的程序通过跨程序调用(CPI)中使用 PDA 的种子“签名”来编程化地铸造代币。当你希望程序本身(而非外部钱包)控制代币铸造时,这种模式非常有用。

lib.rs
use anchor_lang::prelude::*;
use anchor_spl::{
    associated_token::AssociatedToken,
    token_interface::{self, Mint, MintTo, TokenAccount, TokenInterface},
};
 
declare_id!("3pX5NKLru1UBDVckynWQxsgnJeUN3N1viy36Gk9TSn8d");
 
#[program]
pub mod token_example {
    use super::*;
 
    pub fn create_mint(ctx: Context<CreateMint>) -> Result<()> {
        msg!("Created Mint Account: {:?}", ctx.accounts.mint.key());
        Ok(())
    }
 
    pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64) -> Result<()> {
        let signer_seeds: &[&[&[u8]]] = &[&[b"mint", &[ctx.bumps.mint]]];
 
        let cpi_accounts = MintTo {
            mint: ctx.accounts.mint.to_account_info(),
            to: ctx.accounts.token_account.to_account_info(),
            authority: ctx.accounts.mint.to_account_info(),
        };
        let cpi_program = ctx.accounts.token_program.to_account_info();
        let cpi_context = CpiContext::new(cpi_program, cpi_accounts).with_signer(signer_seeds);

        token_interface::mint_to(cpi_context, amount)?;
        Ok(())
    }
}
 
#[derive(Accounts)]
pub struct CreateMint<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(
        init,
        payer = signer,
        mint::decimals = 6,
        mint::authority = mint,
        mint::freeze_authority = mint,
        seeds = [b"mint"],
        bump
    )]
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub system_program: Program<'info, System>,
}
 
#[derive(Accounts)]
pub struct MintTokens<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(
        init_if_needed,
        payer = signer,
        associated_token::mint = mint,
        associated_token::authority = signer,
        associated_token::token_program = token_program,
    )]
    pub token_account: InterfaceAccount<'info, TokenAccount>,
    #[account(
        mut,
        seeds = [b"mint"],
        bump
    )]
    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub system_program: Program<'info, System>,
}

在这个示例中,铸币授权被设置为一个 Program Derived Address (PDA)。PDA 使用种子 b"mint" 派生。这意味着程序本身通过这个 PDA 控制铸造。

snippet
#[derive(Accounts)]
pub struct CreateMint<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(
        init,
        payer = signer,
        mint::decimals = 6,


        mint::authority = mint,
        mint::freeze_authority = mint,
        seeds = [b"mint"],
        bump
    )]

    pub mint: InterfaceAccount<'info, Mint>,
    pub token_program: Interface<'info, TokenInterface>,
    pub system_program: Program<'info, System>,
}

要铸造代币,程序必须通过包含种子和 bump 的 CPI 上下文“签名”。这通过在创建 CPI 上下文时将种子和 bump 传递给 with_signer 方法来实现。

snippet
pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64)

On this page

在GitHub上编辑