Розширення стану облікового запису за замовчуванням
Розширення DefaultAccountState
є розширенням Mint, яке дозволяє всім новоствореним обліковим записам Token
для цього конкретного mint бути замороженими за замовчуванням. Потім Freeze Authority
mint може розморозити ці облікові записи Token
, щоб вони стали придатними для використання.
Initializing the Mint Account
Оскільки Anchor
не має жодних макросів для розширення default_account_state
, ми створимо обліковий запис Mint
за допомогою необроблених CPI.
Ось як створити mint з розширенням Default Account State:
use anchor_lang::prelude::*;
use anchor_lang::system_program::{create_account, CreateAccount};
use anchor_spl::{
token_2022::{
initialize_mint2,
spl_token_2022::{extension::ExtensionType, pod::PodMint, state::AccountState},
InitializeMint2,
},
token_interface::{
default_account_state_initialize, DefaultAccountStateInitialize, Mint, Token2022,
},
};
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(mut)]
pub mint_account: Signer<'info>,
pub token_program: Program<'info, Token2022>,
pub system_program: Program<'info, System>,
}
pub fn initialize_default_account_state_(
ctx: Context<Initialize>,
) -> Result<()> {
// Calculate space required for mint and extension data
let mint_size =
ExtensionType::try_calculate_account_len::<PodMint>(&[ExtensionType::DefaultAccountState])?;
// Calculate minimum lamports required for size of mint account with extensions
let lamports = (Rent::get()?).minimum_balance(mint_size);
// Invoke System Program to create new account with space for mint and extension data
create_account(
CpiContext::new(
ctx.accounts.system_program.to_account_info(),
CreateAccount {
from: ctx.accounts.payer.to_account_info(),
to: ctx.accounts.mint_account.to_account_info(),
},
),
lamports, // Lamports
mint_size as u64, // Space
&ctx.accounts.token_program.key(), // Owner Program
)?;
// Initialize the NonTransferable extension
// This instruction must come before the instruction to initialize the mint data
default_account_state_initialize(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
DefaultAccountStateInitialize {
token_program_id: ctx.accounts.token_program.to_account_info(),
mint: ctx.accounts.mint_account.to_account_info(),
},
),
&AccountState::Frozen, // default frozen token accounts
)?;
// Initialize the standard mint account data
initialize_mint2(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
InitializeMint2 {
mint: ctx.accounts.mint_account.to_account_info(),
},
),
2, // decimals
&ctx.accounts.payer.key(), // mint authority
Some(&ctx.accounts.payer.key()), // freeze authority
)?;
Ok(())
}
Thawing the Token Account
Наявність розширення DefaultAccountState
означає, що всі облікові записи Token
, які ініціалізуються, будуть frozen
за замовчуванням.
Це означає, що неможливо Mint
, Transfer
або робити практично будь-які дії з цими обліковими записами Token
, якщо ми їх не розморозимо.
Ми можемо легко розморозити обліковий запис Token
за допомогою інструкції thaw_account()
таким чином:
use anchor_lang::prelude::*;
use anchor_spl::{
token_interface::{thaw_account, ThawAccount, Mint, Token, Token2022}
};
#[derive(Accounts)]
pub struct ThawAccount<'info> {
#[account(mut)]
pub freeze_authority: Signer<'info>,
#[account(
mut,
mint::freeze_authority = freeze_authority,
)]
pub mint_account: InterfaceAccount<'info, Mint>,
pub token_account: InterfaceAccount<'info, Token>,
pub token_program: Program<'info, Token2022>,
pub system_program: Program<'info, System>,
}
pub fn thaw_account(
ctx: Context<UpdateState>,
) -> Result<()> {
thaw_account(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
ThawAccount {
account: ctx.accounts.token_account.to_account_info(),
mint: ctx.accounts.mint_account.to_account_info(),
freeze_authority: ctx.accounts.freeze_authority.to_account_info(),
},
),
&account_state,
)?;
Ok(())
}
Changing the Default Account State
Коли нам більше не потрібен посилений контроль над розповсюдженням токенів і ми хочемо дозволити всім вільно торгувати нашим токеном, ми можемо змінити стан облікового запису за замовчуванням за допомогою інструкції default_account_state_update()
таким чином:
use anchor_lang::prelude::*;
use anchor_spl::{
token_interface::{default_account_state_update, DefaultAccountStateUpdate, Mint, Token2022}
spl_token_2022::state::AccountState,
};
#[derive(Accounts)]
pub struct UpdateState<'info> {
#[account(mut)]
pub freeze_authority: Signer<'info>,
#[account(
mut,
mint::freeze_authority = freeze_authority,
)]
pub mint_account: InterfaceAccount<'info, Mint>,
pub token_program: Program<'info, Token2022>,
pub system_program: Program<'info, System>,
}
pub fn update_state(
ctx: Context<UpdateState>,
account_state: AnchorAccountState,
) -> Result<()> {
// Convert AnchorAccountState to spl_token_2022::state::AccountState
let account_state = account_state.to_spl_account_state();
default_account_state_update(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
DefaultAccountStateUpdate {
token_program_id: ctx.accounts.token_program.to_account_info(),
mint: ctx.accounts.mint_account.to_account_info(),
freeze_authority: ctx.accounts.freeze_authority.to_account_info(),
},
),
&account_state,
)?;
Ok(())
}
// Custom enum to implement AnchorSerialize and AnchorDeserialize
// This is required to pass the enum as an argument to the instruction
#[derive(AnchorSerialize, AnchorDeserialize)]
pub enum AnchorAccountState {
Uninitialized,
Initialized,
Frozen,
}
// Implement conversion from AnchorAccountState to spl_token_2022::state::AccountState
impl AnchorAccountState {
pub fn to_spl_account_state(&self) -> AccountState {
match self {
AnchorAccountState::Uninitialized => AccountState::Uninitialized,
AnchorAccountState::Initialized => AccountState::Initialized,
AnchorAccountState::Frozen => AccountState::Frozen,
}
}
}