铸造代币
学习如何使用 Anchor 在 Solana 程序中铸造代币。涵盖通过跨程序调用(CPI)向 Token Program 创建新代币的代码示例。
如何铸造代币
铸造代币是指通过调用 Token Program 上的 mint_to
指令创建新代币单位的过程。只有被指定为铸币账户上的铸币权限的地址才能铸造新代币。该指令还要求在代币接收地址上存在一个目标代币账户。
Token Program 和 Token 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。该函数需要:
-
MintTo
结构体,指定所需的账户:mint
- 用于创建新代币单位的铸币账户to
- 用于接收新铸造代币的目标代币账户authority
- 拥有铸造代币权限的铸币授权
-
铸造代币的
amount
,以代币的基本单位(根据小数位数调整)表示。(例如:如果铸币有2位小数,amount 为100 = 1个代币)
传递给 mint_to
指令的铸币授权必须与铸币账户上存储的 mint_authority
匹配。此外,铸币授权必须是交易中的签名者。例如:
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>,
}
至少需要以下账户:
#[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
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 的种子“签名”来编程化地铸造代币。当你希望程序本身(而非外部钱包)控制代币铸造时,这种模式非常有用。
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 控制铸造。
#[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
方法来实现。
pub fn mint_tokens(ctx: Context<MintTokens>, amount: u64)