إنشاء حساب Token
ما هو حساب Token؟
token account هو نوع حساب في برامج Token الخاصة بسولانا يخزن معلومات حول ملكية الفرد لرمز معين (mint). يرتبط كل token account بـ mint واحد ويتتبع تفاصيل مثل رصيد الرمز والمالك.
/// Account data.#[repr(C)]#[derive(Clone, Copy, Debug, Default, PartialEq)]pub struct Account {/// The mint associated with this accountpub mint: Pubkey,/// The owner of this account.pub owner: Pubkey,/// The amount of tokens this account holds.pub amount: u64,/// If `delegate` is `Some` then `delegated_amount` represents/// the amount authorized by the delegatepub delegate: COption<Pubkey>,/// The account's statepub state: AccountState,/// If `is_native.is_some`, this is a native token, and the value logs the/// rent-exempt reserve. An Account is required to be rent-exempt, so/// the value is used by the Processor to ensure that wrapped SOL/// accounts do not drop below this threshold.pub is_native: COption<u64>,/// The amount delegatedpub delegated_amount: u64,/// Optional authority to close the account.pub close_authority: COption<Pubkey>,}
لاحظ أنه في الكود المصدري، يطلق المطورون على حساب Token اسم Account
كنوع. كل
من Token
Program
وToken Extension
Program
يشتركان في نفس التنفيذ الأساسي لحساب Token.
للاحتفاظ بالرموز لـ mint محدد، يجب على المستخدمين أولاً إنشاء token account. يتتبع كل token account:
- mint محدد (نوع الرمز الذي يحتفظ به token account)
- مالك (السلطة التي يمكنها تحويل الرموز من الحساب)
إليك مثال باستخدام USD Coin (USDC) على سولانا:
- عنوان mint الخاص بـ USD Coin:
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
- Circle (مصدر USD Coin) لديه token account في
3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa
- يحتفظ token account هذا فقط بوحدات من رمز USD Coin (mint)
- Circle لديه سلطة المالك في
7VHUFJHWu2CuExkJcJrzhQPJ2oygupTWkL2A2For4BmE
ويمكنه تحويل هذه الرموز
يمكنك عرض تفاصيل token account هذا على Solana Explorer.
يتم استخدام مصطلح "المالك" في سياقين مختلفين:
-
"مالك" token account - هذا هو العنوان المخزن في بيانات token account كحقل "owner" من النوع Account المحدد بواسطة Token Program. يمكن للمالك تحويل أو حرق أو تفويض الرموز من الحساب. يشار إلى هذا العنوان أحيانًا باسم "السلطة" الخاصة بـ token account لتمييزه عن مالك البرنامج.
-
"مالك" البرنامج - يشير هذا إلى البرنامج الذي يمتلك بيانات الحساب على سولانا. بالنسبة لـ token account، يكون هذا دائمًا إما Token Program أو Token Extension Program، كما هو محدد في حقل "owner" من نوع سولانا الأساسي Account.
عند العمل مع token accounts، يشير مصطلح "مالك" عادةً إلى السلطة التي يمكنها إنفاق الرموز، وليس البرنامج الذي يمتلك الحساب.
ما هو Associated Token Account؟
Associated token account هو token account بعنوان يكون Program Derived Address (PDA) تم إنشاؤه بواسطة Associated Token Program. يمكنك اعتبار associated token account بمثابة token account الافتراضي للمستخدم لحفظ وحدات من رمز معين (mint).
مصطلح "associated token accounts" ينطبق فقط على token accounts التي ينشئها Associated Token Program.
توفر associated token accounts طريقة حتمية للعثور على token account الخاص بالمستخدم لأي mint معين. يمكنك فحص تنفيذ الاشتقاق هنا.
pub fn get_associated_token_address_and_bump_seed_internal(wallet_address: &Pubkey,token_mint_address: &Pubkey,program_id: &Pubkey,token_program_id: &Pubkey,) -> (Pubkey, u8) {Pubkey::find_program_address(&[&wallet_address.to_bytes(), // Owner's public key&token_program_id.to_bytes(), // Token Program or Token Extension Program&token_mint_address.to_bytes(), // Token mint address],program_id, // Associated Token Program ID)}
يضمن هذا الاشتقاق الحتمي أنه لأي مجموعة من عنوان المحفظة و token mint، يوجد بالضبط عنوان associated token account واحد. يجعل هذا النهج من السهل العثور على token account الخاص بالمستخدم لأي token mint معين، مما يلغي الحاجة إلى تتبع عناوين token account بشكل منفصل.
يعمل Associated Token Program كبرنامج مساعد ينشئ token accounts بعناوين حتمية
(PDAs). عند إنشاء associated token account، يقوم Associated Token Program
بإجراء CPI (Cross-Program Invocation) إلى Token Program أو Token Extension
Program. يمتلك token program الحساب المنشأ الذي له نفس بنية نوع Account
كما
هو محدد في token program. لا يحتفظ Associated Token Program بأي حالة - فهو
يقدم ببساطة طريقة موحدة لإنشاء token accounts في عنوان حتمي يكون PDA.
كيفية إنشاء حساب توكن
لإنشاء حساب token account، قم باستدعاء التعليمة
InitializeAccount
يمكنك العثور على تطبيقات هذه التعليمة
هنا.
تحتاج المعاملة لإنشاء حساب توكن إلى تعليمتين:
- استدعاء System Program لإنشاء وتخصيص مساحة لحساب توكن ونقل الملكية إلى Token Program.
- استدعاء Token Program لتهيئة بيانات حساب التوكن.
تايبسكريبت
import {airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners} from "@solana/kit";import { getCreateAccountInstruction } from "@solana-program/system";import {getInitializeAccount2Instruction,getInitializeMintInstruction,getMintSize,getTokenSize,TOKEN_2022_PROGRAM_ADDRESS} from "@solana-program/token-2022";// Create Connection, local validator in this exampleconst rpc = createSolanaRpc("http://127.0.0.1:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Get latest blockhash to include in transactionconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// Generate keypairs for fee payerconst feePayer = await generateKeyPairSigner();// Fund fee payerawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: feePayer.address,lamports: lamports(1_000_000_000n),commitment: "confirmed"});// Generate keypair to use as address of mintconst mint = await generateKeyPairSigner();// Get default mint account size (in bytes), no extensions enabledconst space = BigInt(getMintSize());// Get minimum balance for rent exemptionconst rent = await rpc.getMinimumBalanceForRentExemption(space).send();// Instruction to create new account for mint (token 2022 program)// Invokes the system programconst createAccountInstruction = getCreateAccountInstruction({payer: feePayer,newAccount: mint,lamports: rent,space,programAddress: TOKEN_2022_PROGRAM_ADDRESS});// Instruction to initialize mint account data// Invokes the token 2022 programconst initializeMintInstruction = getInitializeMintInstruction({mint: mint.address,decimals: 2,mintAuthority: feePayer.address});const instructions = [createAccountInstruction, initializeMintInstruction];// Create transaction messageconst transactionMessage = pipe(createTransactionMessage({ version: 0 }), // Create transaction message(tx) => setTransactionMessageFeePayerSigner(feePayer, tx), // Set fee payer(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), // Set transaction blockhash(tx) => appendTransactionMessageInstructions(instructions, tx) // Append instructions);// Sign transaction message with required signers (fee payer and mint keypair)const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);// Send and confirm transactionawait sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction,{ commitment: "confirmed" });// Get transaction signatureconst transactionSignature = getSignatureFromTransaction(signedTransaction);console.log("Mint Address: ", mint.address);console.log("Transaction Signature: ", transactionSignature);// Generate keypair to use as address of token accountconst tokenAccount = await generateKeyPairSigner();// Get token account size (in bytes)const tokenAccountSpace = BigInt(getTokenSize());// Get minimum balance for rent exemptionconst tokenAccountRent = await rpc.getMinimumBalanceForRentExemption(tokenAccountSpace).send();// Instruction to create new account for token account (token 2022 program)// Invokes the system programconst createTokenAccountInstruction = getCreateAccountInstruction({payer: feePayer,newAccount: tokenAccount,lamports: tokenAccountRent,space: tokenAccountSpace,programAddress: TOKEN_2022_PROGRAM_ADDRESS});// Instruction to initialize token account data// Invokes the token 2022 programconst initializeTokenAccountInstruction = getInitializeAccount2Instruction({account: tokenAccount.address,mint: mint.address,owner: feePayer.address});const instructions2 = [createTokenAccountInstruction,initializeTokenAccountInstruction];// Create transaction message for token account creationconst tokenAccountMessage = pipe(createTransactionMessage({ version: 0 }), // Create transaction message(tx) => setTransactionMessageFeePayerSigner(feePayer, tx), // Set fee payer(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), // Set transaction blockhash(tx) => appendTransactionMessageInstructions(instructions2, tx) // Append instructions);// Sign transaction message with required signers (fee payer and token account keypair)const signedTokenAccountTx =await signTransactionMessageWithSigners(tokenAccountMessage);// Send and confirm transactionawait sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTokenAccountTx,{ commitment: "confirmed" });// Get transaction signatureconst tokenAccountTxSignature =getSignatureFromTransaction(signedTokenAccountTx);console.log("Token Account Address:", tokenAccount.address);console.log("Transaction Signature:", tokenAccountTxSignature);
Rust
use anyhow::Result;use solana_client::rpc_client::RpcClient;use solana_sdk::{commitment_config::CommitmentConfig,program_pack::Pack,signature::{Keypair, Signer},system_instruction::create_account,transaction::Transaction,};use spl_token_2022::{id as token_2022_program_id,instruction::{initialize_account, initialize_mint},state::{Account, Mint},};fn main() -> Result<()> {// Create connection to local validatorlet client = RpcClient::new_with_commitment(String::from("http://127.0.0.1:8899"),CommitmentConfig::confirmed(),);let recent_blockhash = client.get_latest_blockhash()?;// Generate a new keypair for the fee payerlet fee_payer = Keypair::new();// Airdrop 1 SOL to fee payerlet airdrop_signature = client.request_airdrop(&fee_payer.pubkey(), 1_000_000_000)?;client.confirm_transaction(&airdrop_signature)?;loop {let confirmed = client.confirm_transaction(&airdrop_signature)?;if confirmed {break;}}// Generate keypair to use as address of mintlet mint = Keypair::new();// Get default mint account size (in bytes), no extensions enabledlet mint_space = Mint::LEN;let mint_rent = client.get_minimum_balance_for_rent_exemption(mint_space)?;// Instruction to create new account for mint (token 2022 program)let create_account_instruction = create_account(&fee_payer.pubkey(), // payer&mint.pubkey(), // new account (mint)mint_rent, // lamportsmint_space as u64, // space&token_2022_program_id(), // program id);// Instruction to initialize mint account datalet initialize_mint_instruction = initialize_mint(&token_2022_program_id(),&mint.pubkey(), // mint&fee_payer.pubkey(), // mint authoritySome(&fee_payer.pubkey()), // freeze authority2, // decimals)?;// Create transaction and add instructionslet transaction = Transaction::new_signed_with_payer(&[create_account_instruction, initialize_mint_instruction],Some(&fee_payer.pubkey()),&[&fee_payer, &mint],recent_blockhash,);// Send and confirm transactionlet transaction_signature = client.send_and_confirm_transaction(&transaction)?;println!("Mint Address: {}", mint.pubkey());println!("Transaction Signature: {}", transaction_signature);// Generate keypair to use as address of token accountlet token_account = Keypair::new();// Get token account size (in bytes)let token_account_space = Account::LEN;let token_account_rent = client.get_minimum_balance_for_rent_exemption(token_account_space)?;// Instruction to create new account for token account (token 2022 program)let create_token_account_instruction = create_account(&fee_payer.pubkey(), // payer&token_account.pubkey(), // new account (token account)token_account_rent, // lamportstoken_account_space as u64, // space&token_2022_program_id(), // program id);// Instruction to initialize token account datalet initialize_token_account_instruction = initialize_account(&token_2022_program_id(),&token_account.pubkey(), // account&mint.pubkey(), // mint&fee_payer.pubkey(), // owner)?;// Create transaction and add instructionslet transaction = Transaction::new_signed_with_payer(&[create_token_account_instruction,initialize_token_account_instruction,],Some(&fee_payer.pubkey()),&[&fee_payer, &token_account],recent_blockhash,);// Send and confirm transactionlet transaction_signature = client.send_and_confirm_transaction(&transaction)?;println!("Token Account Address: {}", token_account.pubkey());println!("Transaction Signature: {}", transaction_signature);Ok(())}
كيفية إنشاء associated token account
لإنشاء associated token account، قم باستدعاء التعليمة
Create
يمكنك العثور على تطبيقات هذه التعليمة
هنا.
تقوم التعليمة لإنشاء associated token account تلقائيًا باستدعاء System Program لإنشاء حساب التوكن واستدعاء Token Program لتهيئة بيانات حساب التوكن. يحدث هذا من خلال Cross Program Invocations (CPI).
تايبسكريبت
import {airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners} from "@solana/kit";import { getCreateAccountInstruction } from "@solana-program/system";import {getCreateAssociatedTokenInstructionAsync,getInitializeMintInstruction,getMintSize,TOKEN_2022_PROGRAM_ADDRESS,findAssociatedTokenPda} from "@solana-program/token-2022";// Create Connection, local validator in this exampleconst rpc = createSolanaRpc("http://127.0.0.1:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate keypairs for fee payerconst feePayer = await generateKeyPairSigner();// Fund fee payerawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: feePayer.address,lamports: lamports(1_000_000_000n),commitment: "confirmed"});// Generate keypair to use as address of mintconst mint = await generateKeyPairSigner();// Get default mint account size (in bytes), no extensions enabledconst space = BigInt(getMintSize());// Get minimum balance for rent exemptionconst rent = await rpc.getMinimumBalanceForRentExemption(space).send();// Get latest blockhash to include in transactionconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// Instruction to create new account for mint (token 2022 program)// Invokes the system programconst createAccountInstruction = getCreateAccountInstruction({payer: feePayer,newAccount: mint,lamports: rent,space,programAddress: TOKEN_2022_PROGRAM_ADDRESS});// Instruction to initialize mint account data// Invokes the token 2022 programconst initializeMintInstruction = getInitializeMintInstruction({mint: mint.address,decimals: 2,mintAuthority: feePayer.address});const instructions = [createAccountInstruction, initializeMintInstruction];// Create transaction messageconst transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(feePayer, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions(instructions, tx));// Sign transaction message with all required signersconst signedTransaction =await signTransactionMessageWithSigners(transactionMessage);// Send and confirm transactionawait sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction,{ commitment: "confirmed" });// Get transaction signatureconst transactionSignature = getSignatureFromTransaction(signedTransaction);console.log("Mint Address: ", mint.address.toString());console.log("Transaction Signature: ", transactionSignature);// Use findAssociatedTokenPda to derive the ATA addressconst [associatedTokenAddress] = await findAssociatedTokenPda({mint: mint.address,owner: feePayer.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});console.log("Associated Token Account Address: ",associatedTokenAddress.toString());// Get a fresh blockhash for the second transactionconst { value: latestBlockhash2 } = await rpc.getLatestBlockhash().send();// Create instruction to create the associated token accountconst createAtaInstruction = await getCreateAssociatedTokenInstructionAsync({payer: feePayer,mint: mint.address,owner: feePayer.address});// Create transaction messageconst transactionMessage2 = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(feePayer, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash2, tx),(tx) => appendTransactionMessageInstructions([createAtaInstruction], tx));// Sign transaction message with all required signersconst signedTransaction2 =await signTransactionMessageWithSigners(transactionMessage2);// Send and confirm transactionawait sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction2,{ commitment: "confirmed" });// Get transaction signatureconst transactionSignature2 = getSignatureFromTransaction(signedTransaction2);console.log("Transaction Signature: ", transactionSignature2);
راست
use anyhow::Result;use solana_client::rpc_client::RpcClient;use solana_sdk::{commitment_config::CommitmentConfig,program_pack::Pack,signature::{Keypair, Signer},system_instruction::create_account,transaction::Transaction,};use spl_associated_token_account::{get_associated_token_address_with_program_id, instruction::create_associated_token_account,};use spl_token_2022::{id as token_2022_program_id, instruction::initialize_mint, state::Mint};fn main() -> Result<()> {// Create connection to local validatorlet client = RpcClient::new_with_commitment(String::from("http://127.0.0.1:8899"),CommitmentConfig::confirmed(),);let recent_blockhash = client.get_latest_blockhash()?;// Generate a new keypair for the fee payerlet fee_payer = Keypair::new();// Airdrop 1 SOL to fee payerlet airdrop_signature = client.request_airdrop(&fee_payer.pubkey(), 1_000_000_000)?;client.confirm_transaction(&airdrop_signature)?;loop {let confirmed = client.confirm_transaction(&airdrop_signature)?;if confirmed {break;}}// Generate keypair to use as address of mintlet mint = Keypair::new();// Get default mint account size (in bytes), no extensions enabledlet mint_space = Mint::LEN;let mint_rent = client.get_minimum_balance_for_rent_exemption(mint_space)?;// Instruction to create new account for mint (token 2022 program)let create_account_instruction = create_account(&fee_payer.pubkey(), // payer&mint.pubkey(), // new account (mint)mint_rent, // lamportsmint_space as u64, // space&token_2022_program_id(), // program id);// Instruction to initialize mint account datalet initialize_mint_instruction = initialize_mint(&token_2022_program_id(),&mint.pubkey(), // mint&fee_payer.pubkey(), // mint authoritySome(&fee_payer.pubkey()), // freeze authority2, // decimals)?;// Create transaction and add instructionslet transaction = Transaction::new_signed_with_payer(&[create_account_instruction, initialize_mint_instruction],Some(&fee_payer.pubkey()),&[&fee_payer, &mint],recent_blockhash,);// Send and confirm transactionlet transaction_signature = client.send_and_confirm_transaction(&transaction)?;println!("Mint Address: {}", mint.pubkey());println!("Transaction Signature: {}", transaction_signature);// Get the latest blockhash for the next transactionlet recent_blockhash = client.get_latest_blockhash()?;// Derive the associated token account address for fee_payerlet associated_token_account = get_associated_token_address_with_program_id(&fee_payer.pubkey(), // owner&mint.pubkey(), // mint&token_2022_program_id(), // program_id);// Instruction to create associated token accountlet create_ata_instruction = create_associated_token_account(&fee_payer.pubkey(), // funding address&fee_payer.pubkey(), // wallet address&mint.pubkey(), // mint address&token_2022_program_id(), // program id);// Create transaction and add instructionlet transaction = Transaction::new_signed_with_payer(&[create_ata_instruction],Some(&fee_payer.pubkey()),&[&fee_payer],recent_blockhash,);// Send and confirm transactionlet transaction_signature = client.send_and_confirm_transaction(&transaction)?;println!("Associated Token Account Address: {}",associated_token_account);println!("Transaction Signature: {}", transaction_signature);Ok(())}
Is this page helpful?