使用anchor程序冻结代币
前面一篇中,详细介绍了如何燃烧代币,这篇介绍冻结代币。
因为mint账户的冻结权限(freeze authority)是mint账户自己,所以本操作需要在anchor程序中进行。
创建freeze token指令
在instructions
中添加freeze_token_account.rs
,并粘贴以下代码:
use anchor_lang::prelude::*;
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token::{freeze_account, spl_token, FreezeAccount, Mint, Token, TokenAccount};
use crate::constants::MINT_SEEDS;
use crate::errors::ErrorCode;
#[derive(Accounts)]
#[instruction(token_name: String, token_symbol: String)]
pub struct FreezeTokenAccount<'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> FreezeTokenAccount<'info> {
pub fn freeze_token_account(ctx: Context<FreezeTokenAccount>, token_name: String, token_symbol: String) -> 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[..]];
let freeze_ctx = CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
FreezeAccount {
account: ctx.accounts.destination.to_account_info(),
mint: ctx.accounts.mint.to_account_info(),
authority: ctx.accounts.mint.to_account_info(),
},
&signer,
);
freeze_account(freeze_ctx)?;
msg!("Freeze ata successfully.");
Ok(())
}
}
在lib.rs中调用上面的指令
pub fn freeze_token_account(ctx: Context<FreezeTokenAccount>, token_name: String, token_symbol: String) -> Result<()> {
FreezeTokenAccount::freeze_token_account(ctx, token_name, token_symbol)
}
编译部署
anchor build && anchor deploy
编写测试代码
it("freeze token account", async () => {
// Get user1's token account (ATA)
const tokenAccount = await getAssociatedTokenAddress(mintAccount, user1Account.publicKey, false, TOKEN_PROGRAM_ID);
// Prepare context for freeze instruction
const context = {
mint: mintAccount,
destination: tokenAccount,
user: user1Account.publicKey,
tokenProgram: TOKEN_PROGRAM_ID,
};
// Call freeze_token_account instruction
const tx = await program.methods
.freezeTokenAccount(metadataParams.name, metadataParams.symbol)
.accounts(context)
.signers([user1Account])
.rpc();
console.log("Freeze token account transaction signature:", tx);
// Optional: Verify the token account is frozen
try {
// Attempt to transfer tokens from a frozen account should fail
const sourceAta = await getAssociatedTokenAddress(mintAccount, user1Account.publicKey, false, TOKEN_PROGRAM_ID);
const destinationAta = await getAssociatedTokenAddress(mintAccount, user2Account.publicKey, false, TOKEN_PROGRAM_ID);
const transferIx = createTransferInstruction(
sourceAta,
destinationAta,
user1Account.publicKey,
10 * Math.pow(10, 9) // 10 tokens
);
const tx = new anchor.web3.Transaction().add(transferIx);
await provider.sendAndConfirm(tx, [user1Account]);
console.log("Transfer from frozen account unexpectedly succeeded");
} catch (error) {
console.log("Transfer from frozen account failed as expected:", error);
}
})
运行后,当测试转币时,会出错,说明成功锁定账户。