Was ist der Standard-Kontenstatus?
Die DefaultAccountState Mint-Erweiterung des Token Extensions Program legt
den initialen AccountState für jedes neue Token-Konto fest, das für dieses
Mint erstellt wird.
Dies wird am häufigsten verwendet, um neue Token-Konten im eingefrorenen Zustand zu starten. Token-Konten, die eingefroren starten, können erst verwendet werden, nachdem die Freeze-Authority des Mint sie aufgetaut hat.
So erstellen Sie ein Mint mit einem Standard-Kontenstatus
Um ein Mint mit einem Standard-Kontenstatus zu erstellen:
- Berechnen Sie die Mint-Kontengröße und die benötigte Miete für das Mint und
die
DefaultAccountStateErweiterung. - Erstellen Sie das Mint-Konto mit
CreateAccount, initialisieren SieDefaultAccountStateund initialisieren Sie das Mint mitInitializeMint. - Erstellen Sie Token-Konten für das Mint und beobachten Sie, dass jedes neue Token-Konto im aktuellen Standardzustand startet.
- Verwenden Sie
UpdateDefaultAccountState, um den Zustand zu ändern, in dem zukünftige Token-Konten starten sollen.
Kontengröße berechnen
Berechnen Sie die Mint-Kontengröße für das Basis-Mint plus die
DefaultAccountState Erweiterung. Dies ist die Größe, die in
CreateAccount verwendet wird.
Miete berechnen
Berechnen Sie die Miete anhand der Größe, die für das Mint plus die
DefaultAccountState Erweiterung benötigt wird.
Das Mint-Konto erstellen
Erstellen Sie das Mint-Konto mit dem berechneten Speicherplatz und den Lamports.
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 recipient = await generateKeyPairSigner();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,newAccount: mint,lamports: mintRent,space: mintSpace,programAddress: TOKEN_2022_PROGRAM_ADDRESS})]);
DefaultAccountState initialisieren
Initialisieren Sie die DefaultAccountState-Erweiterung auf dem Mint.
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 recipient = await generateKeyPairSigner();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,newAccount: mint,lamports: mintRent,space: mintSpace,programAddress: TOKEN_2022_PROGRAM_ADDRESS}),getInitializeDefaultAccountStateInstruction({mint: mint.address,state: AccountState.Frozen})]);
Mint initialisieren
Initialisieren Sie den Mint mit InitializeMint in derselben Transaktion.
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 recipient = await generateKeyPairSigner();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,newAccount: mint,lamports: mintRent,space: mintSpace,programAddress: TOKEN_2022_PROGRAM_ADDRESS}),getInitializeDefaultAccountStateInstruction({mint: mint.address,state: AccountState.Frozen}),getInitializeMintInstruction({mint: mint.address,decimals: 0,mintAuthority: client.payer.address,freezeAuthority: client.payer.address})]);
Ein token account erstellen
Erstellen Sie ein token account für den Mint. Da der aktuelle Standardzustand
Frozen ist, beginnt das neue token account im eingefrorenen Zustand.
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 recipient = await generateKeyPairSigner();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,newAccount: mint,lamports: mintRent,space: mintSpace,programAddress: TOKEN_2022_PROGRAM_ADDRESS}),getInitializeDefaultAccountStateInstruction({mint: mint.address,state: AccountState.Frozen}),getInitializeMintInstruction({mint: mint.address,decimals: 0,mintAuthority: client.payer.address,freezeAuthority: client.payer.address})]);const [tokenAccount] = await findAssociatedTokenPda({mint: mint.address,owner: recipient.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});await client.sendTransaction([await getCreateAssociatedTokenInstructionAsync({payer: client.payer,mint: mint.address,owner: recipient.address})]);
Standardkontenstatus aktualisieren
Verwenden Sie UpdateDefaultAccountState, um den Standardzustand zu ändern,
sodass zukünftige token accounts nicht mehr eingefroren starten.
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 recipient = await generateKeyPairSigner();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,newAccount: mint,lamports: mintRent,space: mintSpace,programAddress: TOKEN_2022_PROGRAM_ADDRESS}),getInitializeDefaultAccountStateInstruction({mint: mint.address,state: AccountState.Frozen}),getInitializeMintInstruction({mint: mint.address,decimals: 0,mintAuthority: client.payer.address,freezeAuthority: client.payer.address})]);const [tokenAccount] = await findAssociatedTokenPda({mint: mint.address,owner: recipient.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});await client.sendTransaction([await getCreateAssociatedTokenInstructionAsync({payer: client.payer,mint: mint.address,owner: recipient.address})]);await client.sendTransaction([getUpdateDefaultAccountStateInstruction({mint: mint.address,freezeAuthority: client.payer,state: AccountState.Initialized})]);
Reihenfolge der Anweisungen
DefaultAccountStateInstruction::Initialize muss vor InitializeMint
kommen. CreateAccount, DefaultAccountStateInstruction::Initialize
und InitializeMint müssen in derselben Transaktion enthalten sein.
Quellenreferenz
| Element | Beschreibung | Quelle |
|---|---|---|
DefaultAccountState | Mint-Erweiterung, die den Zustand speichert, den neue token accounts erben sollen. | Quelle |
DefaultAccountStateInstruction::Initialize | Anweisung, die den Standard-Token-Kontenstatus des Mints vor InitializeMint initialisiert. | Quelle |
DefaultAccountStateInstruction::Update | Anweisung, die der Freeze-Autorität ermöglicht, den Standardzustand für zukünftige token accounts zu ändern. | Quelle |
process_initialize_default_account_state | Prozessorlogik, die den anfänglichen Standardzustand auf einen nicht initialisierten Mint schreibt. | Quelle |
process_update_default_account_state | Prozessorlogik, die die Freeze-Autorität überprüft, bevor der gespeicherte Standardzustand des Mints geändert wird. | Quelle |
Typescript
Das nachfolgende Beispiel Kit verwendet die generierten Anweisungen direkt.
Legacy-Beispiele mit @solana/web3.js und @solana/spl-token sind als Referenz
enthalten.
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?