Czym jest konto tokenów
Konto tokenów przechowuje tokeny dla jednego mint i jednego właściciela konta tokenów w sieci 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>,}
Tutaj owner oznacza uprawnioną stronę, która może przenosić, niszczyć lub
delegować tokeny z konta tokenów. Właścicielem programu konta nadal jest Token
Program lub Token Extensions Program.
Każde konto tokenów jest powiązane z dokładnie jednym mint, co oznacza, że token
account może przechowywać jednostki tylko jednego tokenu - tokenu
identyfikowanego przez pole mint konta tokenów.
Czym jest powiązane konto tokenów
Powiązane konto tokenów (ATA) to domyślne token account dla portfela i mint. Associated Token Program wyprowadza adres ATA z adresu portfela, adresu programu tokenów oraz adresu mint.
Tylko konta tokenów utworzone przez Associated Token Program są nazywane associated token account.
Associated Token Program to sposób na utworzenie konta tokenów pod standardowym, deterministycznym adresem. Powstałe konto nadal jest kontem tokenów posiadanym przez Token Program lub Token Extensions Program, a nie przez Associated Token Program.
Associated Token Program wyprowadza adres ATA w sposób pokazany w address.rs:
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)}
Dla każdej kombinacji portfela, programu tokenów i mint istnieje dokładnie jeden
adres ATA. Associated Token Program tworzy standardowe token account pod tym
adresem, a powstałe konto nadal wykorzystuje typ Account zdefiniowany
przez Token Program lub Token Extensions Program.
Jak utworzyć powiązane konto tokenów
Tworzenie powiązanego konta tokenów wykorzystuje instrukcję Create lub
CreateIdempotent programu Associated Token Program. Program Associated
Token Program wyprowadza adres ATA, tworzy konto pod adresem ATA i inicjalizuje
konto jako konto tokenów należące do Token Program lub Token Extension Program.
Zalecane
W większości aplikacji zaleca się tworzenie kont tokenów za pomocą programu Associated Token Program zamiast tworzenia ich przez bezpośrednie wywoływanie instrukcji System Program i Token Program. Powiązane konto tokenów używa deterministycznego adresu wyprowadzonego z właściciela, programu tokenów i jednostki emisyjnej, co ułatwia portfelom i aplikacjom znalezienie domyślnego konta tokenów dla danej jednostki emisyjnej.
Źródło odniesienia
| Element | Opis | Źródło |
|---|---|---|
Create | Tworzy powiązane konto tokenów pod wyprowadzonym adresem ATA. | Źródło |
CreateIdempotent | Tworzy powiązane konto tokenów, ale nadal kończy się powodzeniem, jeśli to ATA już istnieje dla tego samego właściciela i jednostki emisyjnej. | Źródło |
process_create_associated_token_account | Wyprowadza adres ATA i używa create_pda_account oraz CPI do wybranego programu tokenów w celu zainicjalizowania konta tokenów. Gdy wybranym programem jest Token Extension Program, procesor ATA inicjalizuje również rozszerzenie niezmiennego właściciela. | Źródło |
create_pda_account | Funkcja pomocnicza używana przez process_create_associated_token_account do utworzenia konta PDA poprzez CPI do System Program. | Źródło |
Typescript
Poniższe przykłady Kit pokazują zalecane podejście wykorzystujące
@solana/kit. Przykłady starszej wersji używające @solana/web3.js zostały
dołączone dla odniesienia.
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())
Jak utworzyć konto tokenowe
Utworzenie konta tokenowego wymaga dwóch instrukcji:
- Instrukcja
CreateAccountSystem Program tworzy nowe konto zwolnione z rent i przypisuje Token Program jako właściciela programu nowego konta. - Instrukcja
InitializeAccount,InitializeAccount2lubInitializeAccount3Token Program inicjalizuje nowe konto dla tokenu i właściciela.
Umieść instrukcję CreateAccount oraz instrukcję inicjalizacji konta
tokenowego w tej samej transakcji.
Podczas inicjalizacji konta tokenowego Token Program sprawdza, czy konto nie jest już zainicjalizowane oraz czy jest zwolnione z rent.
Poniższa sekcja pokazuje, jak utworzyć konto tokenowe poprzez bezpośrednie wywołanie instrukcji System Program i Token Program. W większości aplikacji użyj Associated Token Program. Bezpośrednich wywołań System Program i Token Program używaj wtedy, gdy masz konkretny powód, aby nie używać associated token account lub gdy musisz utworzyć niestandardowe konta tokenowe PDA, wykonując CPI do instrukcji System Program i Token Program z własnego programu Solana.
Odniesienie do źródła
| Element | Opis | Token Program | Token Extensions Program |
|---|---|---|---|
Account | Podstawowe pola konta tokenowego przechowywane w każdym koncie tokenowym. | Źródło | Źródło |
InitializeAccount | Instrukcja inicjalizacji konta tokenowego, która oczekuje właściciela i konta rent sysvar na liście kont. | Źródło | Źródło |
InitializeAccount2 | Instrukcja inicjalizacji konta tokenowego, która przekazuje właściciela w danych instrukcji zamiast na liście kont. | Źródło | Źródło |
InitializeAccount3 | Instrukcja inicjalizacji konta tokenowego, która przekazuje właściciela w danych instrukcji i nie wymaga konta rent sysvar. | Źródło | Źródło |
_process_initialize_account | Współdzielona logika procesora dla inicjalizacji konta tokenowego. | Źródło | Źródło |
process_initialize_account | Publiczny handler dla InitializeAccount. | Źródło | Źródło |
process_initialize_account2 | Publiczny handler dla InitializeAccount2. | Źródło | Źródło |
process_initialize_account3 | Publiczny handler dla InitializeAccount3. | Źródło | Źródło |
Typescript
Poniższe przykłady z Kit pokazują zalecane podejście przy użyciu
@solana/kit. Przykłady starszej wersji używające @solana/web3.js zostały
dołączone jako odniesienie.
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?