Apa Itu Akun Token
Akun token menyimpan token untuk satu mint dan satu pemilik akun token di 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>,}
Di sini, owner berarti otoritas yang dapat mentransfer, membakar, atau
mendelegasikan token dari akun token. Pemilik program akun tetap Token Program
atau Token Extensions Program.
Setiap akun token terikat pada satu mint saja, yang berarti akun token hanya
dapat menampung unit dari satu token, yaitu token yang diidentifikasi oleh field
mint pada akun token.
Apa Itu Akun Token Terkait
Akun token terkait (ATA) adalah akun token default untuk sebuah dompet dan mint. Associated Token Program menurunkan alamat ATA dari alamat dompet, alamat token program, dan alamat mint.
Hanya akun token yang dibuat oleh Associated Token Program yang disebut sebagai akun token terkait.
Associated Token Program adalah cara untuk membuat akun token pada alamat yang standar dan deterministik. Akun yang dihasilkan tetap merupakan akun token yang dimiliki oleh Token Program atau Token Extensions Program, bukan oleh Associated Token Program.
Associated Token Program menurunkan alamat ATA seperti yang ditunjukkan dalam 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)}
Untuk setiap kombinasi dompet, token program, dan mint, hanya ada satu alamat
ATA. Associated Token Program membuat akun token standar pada alamat tersebut,
dan akun yang dihasilkan tetap menggunakan tipe Account yang didefinisikan
oleh Token Program atau Token Extensions Program.
Cara Membuat Associated Token Account
Membuat associated token account menggunakan instruksi Create atau
CreateIdempotent dari Associated Token Program. Associated Token Program
menurunkan alamat ATA, membuat akun pada alamat ATA tersebut, dan
menginisialisasi akun sebagai token account yang dimiliki oleh Token Program
atau Token Extension Program.
Direkomendasikan
Untuk sebagian besar aplikasi, buat token account melalui Associated Token Program alih-alih membuatnya dengan langsung memanggil instruksi System Program dan Token Program. Associated token account menggunakan alamat deterministik yang diturunkan dari pemilik, token program, dan mint, yang membuat token account default untuk mint tertentu lebih mudah ditemukan oleh wallet dan aplikasi.
Referensi Sumber
| Item | Deskripsi | Sumber |
|---|---|---|
Create | Membuat associated token account pada alamat ATA yang diturunkan. | Sumber |
CreateIdempotent | Membuat associated token account, tetapi tetap berhasil jika ATA tersebut sudah ada untuk pemilik dan mint yang sama. | Sumber |
process_create_associated_token_account | Menurunkan alamat ATA dan menggunakan create_pda_account ditambah CPI ke token program yang dipilih untuk menginisialisasi token account. Ketika program yang dipilih adalah Token Extension Program, processor ATA juga menginisialisasi ekstensi immutable owner. | Sumber |
create_pda_account | Helper yang digunakan oleh process_create_associated_token_account untuk membuat akun PDA melalui CPI ke System Program. | Sumber |
Typescript
Contoh Kit di bawah ini menunjukkan pendekatan yang direkomendasikan
menggunakan @solana/kit. Contoh legacy menggunakan @solana/web3.js
disertakan sebagai referensi.
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())
Cara Membuat Token Account
Membuat token account memerlukan dua instruksi:
- Instruksi
CreateAccountdari System Program membuat akun baru yang bebas rent dan menetapkan Token Program sebagai pemilik program dari akun baru tersebut. - Instruksi
InitializeAccount,InitializeAccount2, atauInitializeAccount3dari Token Program menginisialisasi akun baru untuk mint dan owner.
Sertakan instruksi CreateAccount dan instruksi inisialisasi token account
dalam transaksi yang sama.
Selama inisialisasi token account, Token Program memeriksa bahwa akun belum diinisialisasi dan bebas rent.
Bagian di bawah ini menunjukkan cara membuat token account dengan memanggil langsung instruksi System Program dan Token Program. Untuk sebagian besar aplikasi, gunakan Associated Token Program sebagai gantinya. Gunakan panggilan langsung System Program dan Token Program ketika Anda memiliki alasan khusus untuk tidak menggunakan associated token account atau ketika Anda perlu membuat token account PDA khusus dengan melakukan CPI ke instruksi System Program dan Token Program dari program Solana Anda sendiri.
Referensi Sumber
| Item | Deskripsi | Token Program | Token Extensions Program |
|---|---|---|---|
Account | Field dasar token account yang disimpan di setiap token account. | Sumber | Sumber |
InitializeAccount | Instruksi inisialisasi token account yang mengharapkan owner dan akun rent sysvar dalam daftar akunnya. | Sumber | Sumber |
InitializeAccount2 | Instruksi inisialisasi token account yang meneruskan owner dalam instruction data alih-alih daftar akun. | Sumber | Sumber |
InitializeAccount3 | Instruksi inisialisasi token account yang meneruskan owner dalam instruction data dan tidak memerlukan akun rent sysvar. | Sumber | Sumber |
_process_initialize_account | Logika prosesor bersama untuk inisialisasi token account. | Sumber | Sumber |
process_initialize_account | Handler publik untuk InitializeAccount. | Sumber | Sumber |
process_initialize_account2 | Handler publik untuk InitializeAccount2. | Sumber | Sumber |
process_initialize_account3 | Handler publik untuk InitializeAccount3. | Sumber | Sumber |
Typescript
Contoh Kit di bawah ini menunjukkan pendekatan yang direkomendasikan
menggunakan @solana/kit. Contoh lama menggunakan @solana/web3.js disertakan
sebagai referensi.
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?