Was ist ein Token-Konto
Ein token account speichert Token für eine Mint und einen Token-Kontoinhaber auf Solana.
/// 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>,}
Hier bedeutet owner die Berechtigung, die Token vom token account
übertragen, verbrennen oder delegieren kann. Der program account-Eigentümer des
Konten ist weiterhin das Token Program oder Token Extensions Program.
Jedes token account ist an genau eine Mint gebunden, was bedeutet, dass das
token account nur Einheiten eines einzigen Tokens halten kann – des Tokens, das
durch das Feld mint des token account identifiziert wird.
Was ist ein Associated Token Account
Ein associated token account (ATA) ist das Standard-Token-Konto für eine Wallet und eine Mint. Das Associated Token Program leitet die ATA-Adresse von der Wallet-Adresse, der Token-Programm-Adresse und der Mint-Adresse ab.
Nur Token-Konten, die vom Associated Token Program erstellt wurden, werden als associated token accounts bezeichnet.
Das Associated Token Program ist eine Methode, um ein token account an einer standardisierten, deterministischen Adresse zu erstellen. Das resultierende Konto ist weiterhin ein token account im Besitz des Token Program oder Token Extensions Program, nicht des Associated Token Program.
Das Associated Token Program leitet die ATA-Adresse wie in address.rs gezeigt ab:
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)}
Für jede Kombination aus Wallet, Token-Programm und Mint gibt es genau eine
ATA-Adresse. Das Associated Token Program erstellt ein Standard-Token-Konto an
dieser Adresse, und das resultierende Konto verwendet weiterhin den Typ
Account, der vom Token Program oder Token Extensions Program definiert
wird.
So erstellen Sie ein Associated Token Account
Das Erstellen eines Associated Token Accounts verwendet die Anweisung
Create oder CreateIdempotent des Associated Token Program. Das
Associated Token Program leitet die ATA-Adresse ab, erstellt das Konto unter der
ATA-Adresse und initialisiert das Konto als Token-Konto, das dem Token Program
oder Token Extension Program gehört.
Empfohlen
Für die meisten Anwendungen sollten Token-Konten über das Associated Token Program erstellt werden, anstatt sie direkt durch Aufrufen der System Program- und Token Program- Anweisungen zu erstellen. Ein Associated Token Account verwendet eine deterministische Adresse, die vom Eigentümer, Token-Programm und Mint abgeleitet wird, wodurch das Standard-Token-Konto für einen bestimmten Mint für Wallets und Anwendungen leichter zu finden ist.
Quellenreferenz
| Element | Beschreibung | Quelle |
|---|---|---|
Create | Erstellt ein Associated Token Account unter der abgeleiteten ATA-Adresse. | Quelle |
CreateIdempotent | Erstellt das Associated Token Account, ist aber dennoch erfolgreich, wenn dieses ATA bereits für denselben Eigentümer und Mint existiert. | Quelle |
process_create_associated_token_account | Leitet die ATA-Adresse ab und verwendet create_pda_account plus CPIs zum ausgewählten Token-Programm, um das Token-Konto zu initialisieren. Wenn das ausgewählte Programm das Token Extension Program ist, initialisiert der ATA-Prozessor auch die unveränderliche Eigentümer-Erweiterung. | Quelle |
create_pda_account | Hilfsfunktion, die von process_create_associated_token_account verwendet wird, um das PDA-Konto per CPI in das System Program zu erstellen. | Quelle |
Typescript
Die folgenden Kit-Beispiele zeigen den empfohlenen Ansatz unter Verwendung von
@solana/kit. Legacy-Beispiele mit @solana/web3.js sind als Referenz
enthalten.
Kit
import { generateKeyPairSigner } from "@solana/kit";import { createLocalClient } from "@solana/kit-client-rpc";import {associatedTokenProgram,findAssociatedTokenPda,tokenProgram,TOKEN_PROGRAM_ADDRESS} from "@solana-program/token";const client = await createLocalClient().use(tokenProgram()).use(associatedTokenProgram());const result = await client.associatedToken.instructions.createAssociatedToken({payer: client.payer, // Account funding account creation.mint: mint.address, // Mint for the token this account holds.owner: client.payer.address // Account that owns the token account.}).sendTransaction();const [associatedTokenAddress] = await findAssociatedTokenPda({mint: mint.address,owner: client.payer.address,tokenProgram: TOKEN_PROGRAM_ADDRESS});const tokenAccountData = await client.token.accounts.token.fetch(associatedTokenAddress);console.log("Mint Address:", mint.address);console.log("\nAssociated Token Account Address:", associatedTokenAddress);console.log("Associated Token Account:", tokenAccountData.data);console.log("\nTransaction Signature:", result.context.signature);
Web3.js
import { Connection, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js";import {createAssociatedTokenAccount,createMint,getAccount,TOKEN_PROGRAM_ID} from "@solana/spl-token";const associatedTokenAccount = await createAssociatedTokenAccount(connection, // Connection to the local validator.feePayer, // Account funding account creation.mintPubkey, // Mint for the token this account holds.feePayer.publicKey, // Account that owns the token account.{commitment: "confirmed" // Confirmation options for the transaction.},TOKEN_PROGRAM_ID // Token program to invoke.);const tokenAccountData = await getAccount(connection,associatedTokenAccount,"confirmed",TOKEN_PROGRAM_ID);console.log("Mint Address:", mintPubkey.toBase58());console.log("\nAssociated Token Account Address:",associatedTokenAccount.toBase58());console.log("Associated Token Account:", tokenAccountData);
Rust
use anyhow::Result;use solana_client::nonblocking::rpc_client::RpcClient;use solana_commitment_config::CommitmentConfig;use solana_sdk::{program_pack::Pack,signature::{Keypair, Signer},transaction::Transaction,};use solana_system_interface::instruction::create_account;use spl_associated_token_account_interface::{address::get_associated_token_address, instruction::create_associated_token_account,};use spl_token_interface::{id as token_program_id,instruction::initialize_mint,state::{Account, Mint},};#[tokio::main]async fn main() -> Result<()> {let client = RpcClient::new_with_commitment(String::from("http://localhost:8899"),CommitmentConfig::confirmed(),);let transaction = Transaction::new_signed_with_payer(&[create_associated_token_account(&fee_payer.pubkey(), // Account funding account creation.&fee_payer.pubkey(), // Account that owns the token account.&mint.pubkey(), // Mint for the token this account holds.&token_program_id(), // Token program that owns the account.),],Some(&fee_payer.pubkey()),&[&fee_payer],latest_blockhash,);let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;let associated_token_account = get_associated_token_address(&fee_payer.pubkey(), &mint.pubkey());let token_account = client.get_account(&associated_token_account).await?;let token_data = Account::unpack(&token_account.data)?;println!("Mint Address: {}", mint.pubkey());println!("\nAssociated Token Account Address: {}",associated_token_account);println!("Associated Token Account: {:#?}", token_data);println!("\nTransaction Signature: {}", transaction_signature);Ok(())}
Python
#!/usr/bin/env python3import asyncioimport jsonfrom solana.rpc.async_api import AsyncClientfrom solders.keypair import Keypairfrom solders.message import Messagefrom solders.pubkey import Pubkeyfrom solders.system_program import create_account, CreateAccountParamsfrom solders.transaction import Transactionfrom spl.token.async_client import AsyncTokenfrom spl.token.instructions import (create_associated_token_account,get_associated_token_address,initialize_mint,InitializeMintParams,)from spl.token.constants import MINT_LEN, TOKEN_PROGRAM_IDasync def main():rpc = AsyncClient("http://localhost:8899")fee_payer = Keypair()owner = Keypair()async with rpc:create_associated_token_account_instruction = create_associated_token_account(payer=fee_payer.pubkey(), # Account funding account creation.owner=owner.pubkey(), # Account that owns the token account.mint=mint.pubkey(), # Mint for the token this account holds.token_program_id=TOKEN_PROGRAM_ID, # Token program that owns the new token account.)latest_blockhash = await rpc.get_latest_blockhash()transaction = Transaction([fee_payer],Message([create_associated_token_account_instruction], fee_payer.pubkey()),latest_blockhash.value.blockhash,)result = await rpc.send_transaction(transaction)token_account_info = await token.get_account_info(associated_token_account)token_account = {key: str(value) if isinstance(value, Pubkey) else valuefor key, value in token_account_info._asdict().items()}print("Mint Address:", mint.pubkey())print("\nToken Account Address:", associated_token_account)print("Token Account:")print(json.dumps(token_account, indent=2))print("\nTransaction Signature:", result.value)if __name__ == "__main__":asyncio.run(main())
So erstellen Sie ein Token-Konto
Das Erstellen eines Token-Kontos erfordert zwei Anweisungen:
- Die
CreateAccount-Anweisung des System Program erstellt ein neues mietbefreites Konto und weist das Token Program als Programm-Eigentümer des neuen Kontos zu. - Die
InitializeAccount-,InitializeAccount2- oderInitializeAccount3-Anweisung des Token Program initialisiert das neue Konto für eine Mint und einen Eigentümer.
Fügen Sie die CreateAccount-Anweisung und die
Token-Konto-Initialisierungs-Anweisung in derselben Transaktion ein.
Während der Token-Konto-Initialisierung prüft das Token Program, ob das Konto nicht bereits initialisiert ist und mietbefreit ist.
Der nachstehende Abschnitt zeigt, wie Sie ein Token-Konto erstellen, indem Sie direkt die Anweisungen des System Program und Token Program aufrufen. Für die meisten Anwendungen verwenden Sie stattdessen das Associated Token Program. Verwenden Sie direkte System Program- und Token Program-Aufrufe, wenn Sie einen bestimmten Grund haben, kein associated token account zu verwenden oder wenn Sie benutzerdefinierte PDA-Token-Konten erstellen müssen, indem Sie CPIs an die Anweisungen des System Program und Token Program aus Ihrem eigenen Solana-Programm durchführen.
Quellenreferenz
| Element | Beschreibung | Token Program | Token Extensions Program |
|---|---|---|---|
Account | Die grundlegenden Token-Konto-Felder, die in jedem Token-Konto gespeichert sind. | Quelle | Quelle |
InitializeAccount | Eine Token-Konto-Initialisierungs-Anweisung, die den Eigentümer und das Rent-Sysvar-Konto in ihrer Kontenliste erwartet. | Quelle | Quelle |
InitializeAccount2 | Eine Token-Konto-Initialisierungs-Anweisung, die den Eigentümer in den instruction data statt in der Kontenliste übergibt. | Quelle | Quelle |
InitializeAccount3 | Eine Token-Konto-Initialisierungs-Anweisung, die den Eigentümer in den instruction data übergibt und das Rent-Sysvar-Konto nicht benötigt. | Quelle | Quelle |
_process_initialize_account | Gemeinsame Prozessor-Logik für die Token-Konto-Initialisierung. | Quelle | Quelle |
process_initialize_account | Öffentlicher Handler für InitializeAccount. | Quelle | Quelle |
process_initialize_account2 | Öffentlicher Handler für InitializeAccount2. | Quelle | Quelle |
process_initialize_account3 | Öffentlicher Handler für InitializeAccount3. | Quelle | Quelle |
Typescript
Die folgenden Kit-Beispiele zeigen den empfohlenen Ansatz mit @solana/kit.
Legacy-Beispiele mit @solana/web3.js sind zur Referenz enthalten.
Kit
import { generateKeyPairSigner } from "@solana/kit";import { createLocalClient } from "@solana/kit-client-rpc";import { systemProgram } from "@solana-program/system";import {getTokenSize,tokenProgram,TOKEN_PROGRAM_ADDRESS} from "@solana-program/token";const client = await createLocalClient().use(systemProgram()).use(tokenProgram());const result = await client.sendTransaction([client.system.instructions.createAccount({newAccount: tokenAccount, // New token account to create.lamports: rent, // Lamports funding the new account rent.space, // Account size in bytes.programAddress: TOKEN_PROGRAM_ADDRESS // Program that owns the new account.}),client.token.instructions.initializeAccount({account: tokenAccount.address, // Token account to initialize.mint: mint.address, // Mint for the token this account holds.owner: client.payer.address // Account that owns the token account.})]);const tokenAccountData = await client.token.accounts.token.fetch(tokenAccount.address);console.log("Mint Address:", mint.address);console.log("\nToken Account Address:", tokenAccount.address);console.log("Token Account:", tokenAccountData.data);console.log("\nTransaction Signature:", result.context.signature);
Web3.js
import { Connection, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js";import {createAccount,createMint,getAccount,TOKEN_PROGRAM_ID} from "@solana/spl-token";const tokenAccount = await createAccount(connection, // Connection to the local validator.feePayer, // Account paying transaction fees.mintPubkey, // Mint for the token this account holds.feePayer.publicKey, // Account that owns the token account.Keypair.generate(), // New token account to create.{commitment: "confirmed" // Confirmation options for the transaction.},TOKEN_PROGRAM_ID // Token program to invoke.);const tokenAccountData = await getAccount(connection,tokenAccount,"confirmed",TOKEN_PROGRAM_ID);console.log("Mint Address:", mintPubkey.toBase58());console.log("\nToken Account Address:", tokenAccount.toBase58());console.log("Token Account:", tokenAccountData);
Rust
use anyhow::Result;use solana_client::nonblocking::rpc_client::RpcClient;use solana_commitment_config::CommitmentConfig;use solana_sdk::{program_pack::Pack,signature::{Keypair, Signer},transaction::Transaction,};use solana_system_interface::instruction::create_account;use spl_token_interface::{id as token_program_id,instruction::{initialize_account, initialize_mint},state::{Account, Mint},};#[tokio::main]async fn main() -> Result<()> {let client = RpcClient::new_with_commitment(String::from("http://localhost:8899"),CommitmentConfig::confirmed(),);let token_account = Keypair::new();let token_account_rent = client.get_minimum_balance_for_rent_exemption(Account::LEN).await?;let transaction = Transaction::new_signed_with_payer(&[create_account(&fee_payer.pubkey(), // Account funding account creation.&token_account.pubkey(), // New token account to create.token_account_rent, // Lamports funding the new account rent.Account::LEN as u64, // Account size in bytes.&token_program_id(), // Program that owns the new account.),initialize_account(&token_program_id(),&token_account.pubkey(), // Token account to initialize.&mint.pubkey(), // Mint for the token this account holds.&fee_payer.pubkey(), // Account that owns the token account.)?,],Some(&fee_payer.pubkey()),&[&fee_payer, &token_account],latest_blockhash,);let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;let token_account_data = client.get_account(&token_account.pubkey()).await?;let token_data = Account::unpack(&token_account_data.data)?;println!("Mint Address: {}", mint.pubkey());println!("\nToken Account Address: {}", token_account.pubkey());println!("Token Account: {:#?}", token_data);println!("\nTransaction Signature: {}", transaction_signature);Ok(())}
Python
#!/usr/bin/env python3import asyncioimport jsonfrom solana.rpc.async_api import AsyncClientfrom solders.keypair import Keypairfrom solders.message import Messagefrom solders.pubkey import Pubkeyfrom solders.system_program import create_account, CreateAccountParamsfrom solders.transaction import Transactionfrom spl.token.async_client import AsyncTokenfrom spl.token.instructions import (initialize_account,InitializeAccountParams,initialize_mint,InitializeMintParams,)from spl.token.constants import ACCOUNT_LEN, MINT_LEN, TOKEN_PROGRAM_IDasync def main():rpc = AsyncClient("http://localhost:8899")async with rpc:token_account = Keypair()token_account_rent = (await rpc.get_minimum_balance_for_rent_exemption(ACCOUNT_LEN)).valuecreate_token_account_instructions = [create_account(CreateAccountParams(from_pubkey=fee_payer.pubkey(), # Account funding account creation.to_pubkey=token_account.pubkey(), # New token account to create.lamports=token_account_rent, # Lamports funding the new account rent.space=ACCOUNT_LEN, # Account size in bytes.owner=TOKEN_PROGRAM_ID, # Program that owns the new token account.)),initialize_account(InitializeAccountParams(program_id=TOKEN_PROGRAM_ID, # Token program to invoke.account=token_account.pubkey(), # Token account to initialize.mint=mint.pubkey(), # Mint for the token this account holds.owner=fee_payer.pubkey(), # Account that owns the token account.)),]latest_blockhash = await rpc.get_latest_blockhash()transaction = Transaction([fee_payer, token_account],Message(create_token_account_instructions, fee_payer.pubkey()),latest_blockhash.value.blockhash,)result = await rpc.send_transaction(transaction)token_account_info = await token.get_account_info(token_account.pubkey())token_account_data = {key: str(value) if isinstance(value, Pubkey) else valuefor key, value in token_account_info._asdict().items()}print("Mint Address:", mint.pubkey())print("\nToken Account Address:", token_account.pubkey())print("Token Account:")print(json.dumps(token_account_data, indent=2))print("\nTransaction Signature:", result.value)if __name__ == "__main__":asyncio.run(main())
Is this page helpful?