Δημιουργία λογαριασμού token
Τι είναι ένας λογαριασμός token;
Ένας token account είναι ένας τύπος λογαριασμού στα Token Programs του Solana που αποθηκεύει πληροφορίες σχετικά με την κυριότητα ενός συγκεκριμένου token (mint) από ένα άτομο. Κάθε token account συνδέεται με ένα μόνο mint και παρακολουθεί λεπτομέρειες όπως το υπόλοιπο των token και τον ιδιοκτήτη.
/// 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.
Για να διατηρήσουν tokens για ένα συγκεκριμένο mint, οι χρήστες πρέπει πρώτα να δημιουργήσουν έναν token account. Κάθε token account παρακολουθεί:
- Ένα συγκεκριμένο mint (τον τύπο token που διατηρεί ο token account)
- Έναν ιδιοκτήτη (την αρχή που μπορεί να μεταφέρει tokens από τον λογαριασμό)
Ακολουθεί ένα παράδειγμα χρησιμοποιώντας το USD Coin (USDC) στο Solana:
- Η διεύθυνση mint του USD Coin:
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
- Η Circle (ο εκδότης του USD Coin) έχει έναν token account στη διεύθυνση
3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa
- Αυτός ο token account διατηρεί μόνο μονάδες του token USD Coin (mint)
- Η Circle έχει δικαιώματα ιδιοκτήτη στη διεύθυνση
7VHUFJHWu2CuExkJcJrzhQPJ2oygupTWkL2A2For4BmE
και μπορεί να μεταφέρει αυτά τα tokens
Μπορείτε να δείτε τις λεπτομέρειες αυτού του token account στον Solana Explorer.
Ο όρος "ιδιοκτήτης" χρησιμοποιείται σε δύο διαφορετικά πλαίσια:
-
Ο "ιδιοκτήτης" του token account - Αυτή είναι μια διεύθυνση που αποθηκεύεται στα δεδομένα του token account ως το πεδίο "owner" του τύπου Account που ορίζεται από το Token Program. Ο ιδιοκτήτης μπορεί να μεταφέρει, να κάψει ή να αναθέσει tokens από τον λογαριασμό. Αυτή η διεύθυνση μερικές φορές αναφέρεται ως η "αρχή" του token account για να διακρίνεται από τον ιδιοκτήτη του προγράμματος.
-
Ο "owner" του προγράμματος - Αυτό αναφέρεται στο πρόγραμμα που κατέχει τα δεδομένα του λογαριασμού στο Solana. Για τους token account, αυτό είναι πάντα είτε το Token Program είτε το Token Extension Program, όπως καθορίζεται στο πεδίο "owner" του βασικού Solana Account τύπου.
Όταν εργάζεστε με token account, ο όρος "owner" συνήθως αναφέρεται στην αρχή που μπορεί να ξοδέψει τα tokens, όχι στο πρόγραμμα που κατέχει τον λογαριασμό.
Τι είναι ένα Associated Token Account;
Ένα associated token account είναι ένα token account με διεύθυνση που είναι ένα Program Derived Address (PDA) που δημιουργείται από το Associated Token Program. Μπορείτε να σκεφτείτε ένα associated token account ως τον προεπιλεγμένο token account για έναν χρήστη για να κρατά μονάδες ενός συγκεκριμένου token (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
Για να δημιουργήσετε ένα Token Account, καλέστε την εντολή
InitializeAccount
.
Μπορείτε να βρείτε υλοποιήσεις αυτής της εντολής
εδώ.
Η συναλλαγή για τη δημιουργία ενός token account χρειάζεται δύο εντολές:
- Κλήση του System Program για τη δημιουργία και την κατανομή χώρου για ένα token account και μεταφορά της ιδιοκτησίας στο Token Program.
- Κλήση του Token Program για την αρχικοποίηση των δεδομένων του token account.
Typescript
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 account και καλεί το Token Program για την αρχικοποίηση των δεδομένων του token account. Αυτό συμβαίνει μέσω Cross Program Invocations (CPI).
Typescript
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);
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_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?