기본 계정 상태란 무엇인가요?
Token Extensions Program의 DefaultAccountState 민트 확장은 해당 민트에
대해 생성된 모든 새 토큰 계정의 초기 *rsAccountState*를 설정합니다.
이는 새 토큰 계정이 동결된 상태로 시작하도록 하는 데 가장 일반적으로 사용됩니다. 동결된 상태로 시작하는 토큰 계정은 민트의 동결 권한이 해동할 때까지 사용할 수 없습니다.
기본 계정 상태로 민트를 생성하는 방법
기본 계정 상태로 민트를 생성하려면:
- 민트 및
DefaultAccountState확장에 필요한 민트 계정 크기와 rent를 계산합니다. - *rs
CreateAccount*로 민트 계정을 생성하고, *rsDefaultAccountState*를 초기화한 다음, *rsInitializeMint*로 민트를 초기화합니다. - 민트용 토큰 계정을 생성하고 각 새 토큰 계정이 현재 기본 상태로 시작하는지 확인합니다.
- *rs
UpdateDefaultAccountState*를 사용하여 향후 토큰 계정이 시작해야 하는 상태를 변경합니다.
계정 크기 계산
기본 민트와 DefaultAccountState 확장을 위한 민트 계정 크기를 계산합니다.
이것은 *rsCreateAccount*에서 사용되는 크기입니다.
rent 계산
민트와 DefaultAccountState 확장에 필요한 크기를 사용하여 rent를
계산합니다.
민트 계정 생성
계산된 공간과 lamport로 민트 계정을 생성합니다.
DefaultAccountState 초기화
민트에서 DefaultAccountState 확장을 초기화합니다.
민트 초기화
동일한 트랜잭션에서 *rsInitializeMint*를 사용하여 민트를 초기화합니다.
토큰 계정 생성
민트용 토큰 계정을 생성합니다. 현재 기본 상태가 *rsFrozen*이므로 새 토큰
계정은 동결된 상태로 시작됩니다.
기본 계정 상태 업데이트
*rsUpdateDefaultAccountState*를 사용하여 기본 상태를 변경하면 향후 토큰 계정이
더 이상 동결된 상태로 시작되지 않습니다.
명령어 순서
_rsDefaultAccountStateInstruction::Initialize_는 _rsInitializeMint_보다
먼저 실행되어야 합니다. CreateAccount,
DefaultAccountStateInstruction::Initialize, _rsInitializeMint_는
동일한 트랜잭션에 포함되어야 합니다.
소스 참조
| 항목 | 설명 | 소스 |
|---|---|---|
DefaultAccountState | 새 토큰 계정이 상속해야 하는 상태를 저장하는 민트 확장입니다. | 소스 |
DefaultAccountStateInstruction::Initialize | InitializeMint 이전에 민트의 기본 토큰 계정 상태를 초기화하는 명령어입니다. | 소스 |
DefaultAccountStateInstruction::Update | 동결 권한이 향후 토큰 계정의 기본 상태를 변경할 수 있도록 하는 명령어입니다. | 소스 |
process_initialize_default_account_state | 초기화되지 않은 민트에 초기 기본 상태를 기록하는 프로세서 로직입니다. | 소스 |
process_update_default_account_state | 민트에 저장된 기본 상태를 변경하기 전에 동결 권한을 검증하는 프로세서 로직입니다. | 소스 |
Typescript
아래의 Kit 예제는 생성된 명령어를 직접 사용합니다. @solana/web3.js 및
@solana/spl-token를 사용하는 레거시 예제는 참조용으로 포함되어 있습니다.
Kit
import {lamports,createClient,generateKeyPairSigner,unwrapOption} from "@solana/kit";import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";import { getCreateAccountInstruction } from "@solana-program/system";import {AccountState,extension,fetchMint,fetchToken,findAssociatedTokenPda,getCreateAssociatedTokenInstructionAsync,getInitializeDefaultAccountStateInstruction,getInitializeMintInstruction,getMintSize,getUpdateDefaultAccountStateInstruction,isExtension,TOKEN_2022_PROGRAM_ADDRESS} from "@solana-program/token-2022";const client = await createClient().use(generatedPayer()).use(solanaRpc({rpcUrl: "http://localhost:8899",rpcSubscriptionsUrl: "ws://localhost:8900"})).use(rpcAirdrop()).use(airdropPayer(lamports(1_000_000_000n)));const mint = await generateKeyPairSigner();const getAccountStateLabel = (state: AccountState) => {switch (state) {case AccountState.Frozen:return "Frozen";case AccountState.Initialized:return "Initialized";default:return "Uninitialized";}};const defaultAccountStateExtension = extension("DefaultAccountState", {state: AccountState.Frozen});const mintSpace = BigInt(getMintSize([defaultAccountStateExtension]));const mintRent = await client.rpc.getMinimumBalanceForRentExemption(mintSpace).send();await client.sendTransaction([getCreateAccountInstruction({payer: client.payer, // Account funding the new mint account.newAccount: mint, // New mint account to create.lamports: mintRent, // Lamports funding the mint account rent.space: mintSpace, // Account size in bytes for the mint plus DefaultAccountState.programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.}),getInitializeDefaultAccountStateInstruction({mint: mint.address, // Mint account that stores the DefaultAccountState extension.state: AccountState.Frozen // Default state assigned to new token accounts.}),getInitializeMintInstruction({mint: mint.address, // Mint account to initialize.decimals: 0, // Number of decimals for the token.mintAuthority: client.payer.address, // Authority allowed to mint new tokens.freezeAuthority: client.payer.address // Authority allowed to freeze token accounts.})]);const [tokenAccount] = await findAssociatedTokenPda({mint: mint.address,owner: client.payer.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});await client.sendTransaction([await getCreateAssociatedTokenInstructionAsync({payer: client.payer, // Account funding the associated token account creation.mint: mint.address, // Mint for the associated token account.owner: client.payer.address // Owner of the token account.})]);const tokenAccountBeforeUpdate = await fetchToken(client.rpc, tokenAccount);const mintAccountBeforeUpdate = await fetchMint(client.rpc, mint.address);const defaultAccountStateBeforeUpdate = (unwrapOption(mintAccountBeforeUpdate.data.extensions) ?? []).find((item) => isExtension("DefaultAccountState", item));await client.sendTransaction([getUpdateDefaultAccountStateInstruction({mint: mint.address, // Mint account that stores the DefaultAccountState extension.freezeAuthority: client.payer, // Freeze authority authorized to update the default state.state: AccountState.Initialized // New default state assigned to later token accounts.})]);const tokenAccountAfterUpdate = await fetchToken(client.rpc, tokenAccount);const mintAccountAfterUpdate = await fetchMint(client.rpc, mint.address);const defaultAccountStateAfterUpdate = (unwrapOption(mintAccountAfterUpdate.data.extensions) ?? []).find((item) => isExtension("DefaultAccountState", item));console.log("Mint Address:", mint.address);console.log("Default Account State Before Update:",defaultAccountStateBeforeUpdate);console.log("Token Account State Before Update:",getAccountStateLabel(tokenAccountBeforeUpdate.data.state));console.log("Default Account State After Update:",defaultAccountStateAfterUpdate);console.log("Token Account State After Update:",getAccountStateLabel(tokenAccountAfterUpdate.data.state));
Web3.js
import {Connection,Keypair,sendAndConfirmTransaction,SystemProgram,Transaction,LAMPORTS_PER_SOL} from "@solana/web3.js";import {AccountState,ASSOCIATED_TOKEN_PROGRAM_ID,createAssociatedTokenAccountInstruction,createInitializeDefaultAccountStateInstruction,createInitializeMintInstruction,createUpdateDefaultAccountStateInstruction,ExtensionType,getAccount,getAssociatedTokenAddressSync,getDefaultAccountState,getMint,getMintLen,TOKEN_2022_PROGRAM_ID} from "@solana/spl-token";const connection = new Connection("http://localhost:8899", "confirmed");const latestBlockhash = await connection.getLatestBlockhash();const feePayer = Keypair.generate();const getTokenAccountStateLabel = (account: {isInitialized: boolean;isFrozen: boolean;}) => {if (account.isFrozen) return "Frozen";if (account.isInitialized) return "Initialized";return "Uninitialized";};const airdropSignature = await connection.requestAirdrop(feePayer.publicKey,5 * LAMPORTS_PER_SOL);await connection.confirmTransaction({blockhash: latestBlockhash.blockhash,lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,signature: airdropSignature});const extensions = [ExtensionType.DefaultAccountState];const mint = Keypair.generate();const mintLength = getMintLen(extensions);const mintRent = await connection.getMinimumBalanceForRentExemption(mintLength);const createMintAccountInstruction = SystemProgram.createAccount({fromPubkey: feePayer.publicKey, // Account funding the new mint account.newAccountPubkey: mint.publicKey, // New mint account to create.space: mintLength, // Account size in bytes for the mint plus DefaultAccountState.lamports: mintRent, // Lamports funding the mint account rent.programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.});const initializeDefaultStateInstruction =createInitializeDefaultAccountStateInstruction(mint.publicKey, // Mint account that stores the DefaultAccountState extension.AccountState.Frozen, // Default state assigned to new token accounts.TOKEN_2022_PROGRAM_ID // Token program that owns the mint.);const initializeMintInstruction = createInitializeMintInstruction(mint.publicKey, // Mint account to initialize.0, // Number of decimals for the token.feePayer.publicKey, // Authority allowed to mint new tokens.feePayer.publicKey, // Authority allowed to freeze token accounts.TOKEN_2022_PROGRAM_ID // Program that owns the mint account.);await sendAndConfirmTransaction(connection,new Transaction({feePayer: feePayer.publicKey,blockhash: latestBlockhash.blockhash,lastValidBlockHeight: latestBlockhash.lastValidBlockHeight}).add(createMintAccountInstruction,initializeDefaultStateInstruction,initializeMintInstruction),[feePayer, mint]);const tokenAccount = getAssociatedTokenAddressSync(mint.publicKey,feePayer.publicKey,false,TOKEN_2022_PROGRAM_ID,ASSOCIATED_TOKEN_PROGRAM_ID);await sendAndConfirmTransaction(connection,new Transaction().add(createAssociatedTokenAccountInstruction(feePayer.publicKey, // Account funding the associated token account creation.tokenAccount, // Associated token account address to create.feePayer.publicKey, // Owner of the token account.mint.publicKey, // Mint for the associated token account.TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.ASSOCIATED_TOKEN_PROGRAM_ID // Associated Token Program that creates the account.)),[feePayer],{commitment: "confirmed"});const tokenAccountBeforeUpdate = await getAccount(connection,tokenAccount,"confirmed",TOKEN_2022_PROGRAM_ID);const mintAccountBeforeUpdate = await getMint(connection,mint.publicKey,"confirmed",TOKEN_2022_PROGRAM_ID);const defaultAccountStateBeforeUpdate = getDefaultAccountState(mintAccountBeforeUpdate);const updateDefaultStateInstruction =createUpdateDefaultAccountStateInstruction(mint.publicKey, // Mint account that stores the DefaultAccountState extension.AccountState.Initialized, // New default state assigned to later token accounts.feePayer.publicKey, // Freeze authority authorized to update the default state.[], // Additional multisig signers.TOKEN_2022_PROGRAM_ID // Token program that owns the mint.);await sendAndConfirmTransaction(connection,new Transaction().add(updateDefaultStateInstruction),[feePayer],{commitment: "confirmed"});const tokenAccountAfterUpdate = await getAccount(connection,tokenAccount,"confirmed",TOKEN_2022_PROGRAM_ID);const mintAccountAfterUpdate = await getMint(connection,mint.publicKey,"confirmed",TOKEN_2022_PROGRAM_ID);const defaultAccountStateAfterUpdate = getDefaultAccountState(mintAccountAfterUpdate);console.log("Mint Address:", mint.publicKey.toBase58());console.log("Default Account State Before Update:",defaultAccountStateBeforeUpdate);console.log("Token Account State Before Update:",getTokenAccountStateLabel(tokenAccountBeforeUpdate));console.log("Default Account State After Update:",defaultAccountStateAfterUpdate);console.log("Token Account State After Update:",getTokenAccountStateLabel(tokenAccountAfterUpdate));
Rust
use anyhow::Result;use solana_client::nonblocking::rpc_client::RpcClient;use solana_commitment_config::CommitmentConfig;use solana_sdk::{signature::{Keypair, Signer},transaction::Transaction,};use solana_system_interface::instruction::create_account;use spl_associated_token_account_interface::{address::get_associated_token_address_with_program_id,instruction::create_associated_token_account,};use spl_token_2022_interface::{extension::{default_account_state::{instruction::{initialize_default_account_state, update_default_account_state},DefaultAccountState,},BaseStateWithExtensions, ExtensionType, StateWithExtensions,},instruction::initialize_mint,state::{Account, AccountState, Mint},ID as TOKEN_2022_PROGRAM_ID,};fn get_account_state_label(state: AccountState) -> &'static str {match state {AccountState::Frozen => "Frozen",AccountState::Initialized => "Initialized",AccountState::Uninitialized => "Uninitialized",}}#[tokio::main]async fn main() -> Result<()> {let client = RpcClient::new_with_commitment(String::from("http://localhost:8899"),CommitmentConfig::confirmed(),);let fee_payer = Keypair::new();let airdrop_signature = client.request_airdrop(&fee_payer.pubkey(), 5_000_000_000).await?;loop {let confirmed = client.confirm_transaction(&airdrop_signature).await?;if confirmed {break;}}let mint = Keypair::new();let mint_space =ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::DefaultAccountState])?;let mint_rent = client.get_minimum_balance_for_rent_exemption(mint_space).await?;let create_mint_account_instruction = create_account(&fee_payer.pubkey(), // Account funding the new mint account.&mint.pubkey(), // New mint account to create.mint_rent, // Lamports funding the mint account rent.mint_space as u64, // Account size in bytes for the mint plus DefaultAccountState.&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.);let initialize_default_account_state_instruction = initialize_default_account_state(&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.&mint.pubkey(), // Mint account that stores the DefaultAccountState extension.&AccountState::Frozen, // Default state assigned to new token accounts.)?;let initialize_mint_instruction = initialize_mint(&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.&mint.pubkey(), // Mint account to initialize.&fee_payer.pubkey(), // Authority allowed to mint new tokens.Some(&fee_payer.pubkey()), // Authority allowed to freeze token accounts.0, // Number of decimals for the token.)?;let create_mint_transaction = Transaction::new_signed_with_payer(&[create_mint_account_instruction,initialize_default_account_state_instruction,initialize_mint_instruction,],Some(&fee_payer.pubkey()),&[&fee_payer, &mint],client.get_latest_blockhash().await?,);client.send_and_confirm_transaction(&create_mint_transaction).await?;let token_account = get_associated_token_address_with_program_id(&fee_payer.pubkey(),&mint.pubkey(),&TOKEN_2022_PROGRAM_ID,);let create_token_account_transaction = Transaction::new_signed_with_payer(&[create_associated_token_account(&fee_payer.pubkey(), // Account funding the associated token account creation.&fee_payer.pubkey(), // Owner of the token account.&mint.pubkey(), // Mint for the associated token account.&TOKEN_2022_PROGRAM_ID, // Token program that owns the token account.)],Some(&fee_payer.pubkey()),&[&fee_payer],client.get_latest_blockhash().await?,);client.send_and_confirm_transaction(&create_token_account_transaction).await?;let token_account_data_before_update = client.get_account(&token_account).await?;let token_account_state_before_update =StateWithExtensions::<Account>::unpack(&token_account_data_before_update.data)?;let mint_account_before_update = client.get_account(&mint.pubkey()).await?;let mint_state_before_update =StateWithExtensions::<Mint>::unpack(&mint_account_before_update.data)?;let default_account_state_before_update = mint_state_before_update.get_extension::<DefaultAccountState>()?;let update_default_account_state_instruction = update_default_account_state(&TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.&mint.pubkey(), // Mint account that stores the DefaultAccountState extension.&fee_payer.pubkey(), // Freeze authority authorized to update the default state.&[&fee_payer.pubkey()], // Additional multisig signers.&AccountState::Initialized, // New default state assigned to later token accounts.)?;let update_default_account_state_transaction = Transaction::new_signed_with_payer(&[update_default_account_state_instruction],Some(&fee_payer.pubkey()),&[&fee_payer],client.get_latest_blockhash().await?,);client.send_and_confirm_transaction(&update_default_account_state_transaction).await?;let token_account_data_after_update = client.get_account(&token_account).await?;let token_account_state_after_update =StateWithExtensions::<Account>::unpack(&token_account_data_after_update.data)?;let mint_account_after_update = client.get_account(&mint.pubkey()).await?;let mint_state_after_update =StateWithExtensions::<Mint>::unpack(&mint_account_after_update.data)?;let default_account_state_after_update =mint_state_after_update.get_extension::<DefaultAccountState>()?;println!("Mint Address: {}", mint.pubkey());println!("Default Account State Before Update: {:?}",default_account_state_before_update);println!("Token Account State Before Update: {}",get_account_state_label(token_account_state_before_update.base.state));println!("Default Account State After Update: {:?}",default_account_state_after_update);println!("Token Account State After Update: {}",get_account_state_label(token_account_state_after_update.base.state));Ok(())}
Is this page helpful?