Een token account aanmaken
Wat is een token account?
Een token account is een accounttype in Solana's Token Programs dat informatie opslaat over het eigendom van een specifieke token (mint) door een individu. Elk token account is gekoppeld aan een enkele mint en houdt details bij zoals het tokensaldo en de eigenaar.
/// 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>,}
Merk op dat in de broncode ontwikkelaars een token account een Account
type
noemen. Zowel het Token
Program
als het Token Extension
Program
delen dezelfde basisimplementatie voor het token account.
Om tokens voor een specifieke mint te bewaren, moeten gebruikers eerst een token account aanmaken. Elk token account houdt bij:
- Een specifieke mint (het tokentype waarvan het token account eenheden bevat)
- Een eigenaar (de autoriteit die tokens van het account kan overmaken)
Hier is een voorbeeld met USD Coin (USDC) op Solana:
- Het USD Coin mint-adres:
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
- Circle (de uitgever van USD Coin) heeft een token account op
3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa
- Dit token account bevat alleen eenheden van de USD Coin token (mint)
- Circle heeft eigenaarsbevoegdheid op
7VHUFJHWu2CuExkJcJrzhQPJ2oygupTWkL2A2For4BmE
en kan deze tokens overmaken
Je kunt de details van dit token account bekijken op Solana Explorer.
De term "eigenaar" wordt in twee verschillende contexten gebruikt:
-
De "eigenaar" van het token account - Dit is een adres dat is opgeslagen in de gegevens van het token account als het "owner"-veld van het Account type gedefinieerd door het Token Program. De eigenaar kan tokens van het account overmaken, verbranden of delegeren. Dit adres wordt soms aangeduid als de "autoriteit" van het token account om het te onderscheiden van de programma-eigenaar.
-
De programma "owner" - Dit verwijst naar het programma dat eigenaar is van de accountgegevens op Solana. Voor token accounts is dit altijd ofwel het Token Program of Token Extension Program, zoals gespecificeerd in het "owner" veld van het basis Solana Account type.
Bij het werken met token accounts verwijst "owner" meestal naar de autoriteit die de tokens kan uitgeven, niet naar het programma dat eigenaar is van het account.
Wat is een Associated Token Account?
Een associated token account is een token account met een adres dat een Program Derived Address (PDA) is, aangemaakt door het Associated Token Program. Je kunt een associated token account zien als het standaard token account voor een gebruiker om eenheden van een specifieke token (mint) te bewaren.
De term "associated token accounts" is alleen van toepassing op token accounts die door het Associated Token Program worden aangemaakt.
Associated token accounts bieden een deterministische manier om het token account van een gebruiker voor elke gegeven mint te vinden. Je kunt de implementatie van de afleiding hier bekijken.
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)}
Deze deterministische afleiding zorgt ervoor dat er voor elke combinatie van walletadres en token mint precies één associated token account adres bestaat. Deze aanpak maakt het eenvoudig om het token account van een gebruiker voor elke gegeven token mint te vinden, waardoor het niet meer nodig is om token account adressen apart bij te houden.
Het Associated Token Program fungeert als een hulpprogramma dat token accounts
aanmaakt met deterministische adressen (PDAs). Bij het aanmaken van een
associated token account, maakt het Associated Token Program een CPI (Cross
Program Invocation) naar ofwel het Token Program of Token Extensions Program.
Het token program is eigenaar van het aangemaakte account dat dezelfde
Account
typestructuur heeft zoals gedefinieerd in het token program. Het
Associated Token Program bewaart geen status - het biedt simpelweg een
gestandaardiseerde manier om token accounts aan te maken op een
deterministisch adres dat een PDA is.
Hoe een token account aanmaken
Om een token account aan te maken, roep je de
InitializeAccount
instructie aan. Je kunt implementaties van deze instructie
hier
vinden.
De transactie om een token account aan te maken heeft twee instructies nodig:
- Roep het System Program aan om ruimte te creëren en toe te wijzen voor een token account en draag het eigendom over aan het Token Program.
- Roep het Token Program aan om de token account gegevens te initialiseren.
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(())}
Hoe een associated token account aanmaken
Om een associated token account aan te maken, roep je de
Create
instructie aan. Je kunt implementaties van deze instructie
hier
vinden.
De instructie om een associated token account aan te maken roept automatisch het System Program aan om het token account te creëren en roept het Token Program aan om de token account gegevens te initialiseren. Dit gebeurt via 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?