L'Extension Default Account State
L'extension default_account_state
(état par défaut du compte) est une extension de compte de Mint
qui permet de geler par défaut tous les comptes de Token
nouvellement créés pour un mint précis. La Freeze Authority
du mint peut ensuite débloquer (dégeler) ces comptes de Token
afin qu'ils puissent être utilisés.
Initialisation du Compte de Mint
Comme Anchor
ne possède aucune macro pour l'extension default_account_state
nous allons créer un compte de Mint
à l'aide de CPIs bruts.
Voici comment créer un compte de mint avec l'extension 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(())
}
Déblocage d'un compte de Jeton
L'extension DefaultAccountState
signifie que tous les comptes de Token
initialisés seront frozen
par défaut.
Cela signifie qu'il n'est pas possible d'effectuer des opérations de Mint
, Transfer
ou pratiquement toute autre opération avec ces comptes de Token
si nous ne les débloquons (dégelons) pas.
Nous pouvons facilement débloquer un compte de Token
à l'aide de l'instruction thawAccount
comme ceci :
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(())
}
Modification du Default Account State
Une fois que nous n'avons plus besoin d'exercer un contrôle important sur la distribution des jetons et que nous souhaitons permettre à tout le monde de les échanger librement, nous pouvons modifier l'état par défaut du compte à l'aide de l'instruction updateDefaultAccountState
comme ceci :
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,
}
}
}