Создание токен-аккаунта

Что такое токен-аккаунт

Токен-аккаунт хранит токены для одного минта и одного владельца токен-аккаунта на Solana.

Token Account Type
/// Account data.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Account {
/// The mint associated with this account
pub 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 delegate
pub delegate: COption<Pubkey>,
/// The account's state
pub 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 delegated
pub delegated_amount: u64,
/// Optional authority to close the account.
pub close_authority: COption<Pubkey>,
}

Здесь owner означает орган, который может переводить, сжигать или делегировать токены из токен-аккаунта. Владельцем программы аккаунта по-прежнему остается Token Program или Token Extensions Program.

Каждый токен-аккаунт привязан ровно к одному минту, что означает, что токен-аккаунт может хранить единицы только одного токена — токена, идентифицируемого полем mint токен-аккаунта.

Что такое associated token account

Associated token account (ATA) — это токен-аккаунт по умолчанию для кошелька и минта. Associated Token Program выводит адрес ATA из адреса кошелька, адреса токен-программы и адреса минта.

Только токен-аккаунты, созданные Associated Token Program, называются associated token account.

Associated Token Program — это способ создания токен-аккаунта по стандартному детерминированному адресу. Полученный аккаунт по-прежнему остается токен-аккаунтом, принадлежащим Token Program или Token Extensions Program, а не Associated Token Program.

Associated Token Program выводит адрес ATA, как показано в address.rs:

Associated Token Account Address Derivation
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
)
}

Для любой комбинации кошелька, токен-программы и минта существует ровно один адрес ATA. Associated Token Program создает стандартный токен-аккаунт по этому адресу, и полученный аккаунт по-прежнему использует тип Account, определенный Token Program или Token Extensions Program.

Как создать ассоциированный токен аккаунт

Создание ассоциированного токен аккаунта использует инструкцию Create или CreateIdempotent программы Associated Token Program. Associated Token Program выводит адрес ATA, создает аккаунт по адресу ATA и инициализирует аккаунт как токен аккаунт, принадлежащий Token Program или Token Extensions Program.

Рекомендуется

Для большинства приложений создавайте токен аккаунты через Associated Token Program вместо их создания напрямую через инструкции System Program и Token Program. Ассоциированный токен аккаунт использует детерминированный адрес, выведенный из владельца, токен программы и минта, что упрощает кошелькам и приложениям поиск дефолтного токен аккаунта для данного минта.

Справочник по исходному коду

ЭлементОписаниеИсходный код
CreateСоздает ассоциированный токен аккаунт по выведенному адресу ATA.Исходный код
CreateIdempotentСоздает ассоциированный токен аккаунт, но все равно завершается успешно, если этот ATA уже существует для того же владельца и минта.Исходный код
process_create_associated_token_accountВыводит адрес ATA и использует create_pda_account плюс CPI к выбранной токен программе для инициализации токен аккаунта. Когда выбранной программой является Token Extensions Program, процессор ATA также инициализирует расширение неизменяемого владельца.Исходный код
create_pda_accountВспомогательная функция, используемая process_create_associated_token_account для создания PDA аккаунта через CPI в System Program.Исходный код

Typescript

Примеры Kit ниже демонстрируют рекомендуемый подход с использованием @solana/kit. Устаревшие примеры с использованием @solana/web3.js включены для справки.

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);
Console
Click to execute the code.

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);
Console
Click to execute the code.

Rust

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(())
}
Console
Click to execute the code.

Python

Python
#!/usr/bin/env python3
import asyncio
import json
from solana.rpc.async_api import AsyncClient
from solders.keypair import Keypair
from solders.message import Message
from solders.pubkey import Pubkey
from solders.system_program import create_account, CreateAccountParams
from solders.transaction import Transaction
from spl.token.async_client import AsyncToken
from spl.token.instructions import (
create_associated_token_account,
get_associated_token_address,
initialize_mint,
InitializeMintParams,
)
from spl.token.constants import MINT_LEN, TOKEN_PROGRAM_ID
async 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 value
for 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())
Console
Click to execute the code.

Как создать токен-аккаунт

Создание токен-аккаунта требует двух инструкций:

  1. Инструкция CreateAccount System Program создаёт новый аккаунт, освобождённый от rent, и назначает Token Program владельцем программы нового аккаунта.
  2. Инструкция Token Program InitializeAccount, InitializeAccount2 или InitializeAccount3 инициализирует новый аккаунт для минта и владельца.

Включите инструкцию CreateAccount и инструкцию инициализации токен-аккаунта в одной транзакции.

Во время инициализации токен-аккаунта Token Program проверяет, что аккаунт ещё не инициализирован и освобождён от rent.

Раздел ниже показывает, как создать токен-аккаунт путём прямого вызова инструкций System Program и Token Program. Для большинства приложений используйте Associated Token Program. Используйте прямые вызовы System Program и Token Program, когда у вас есть конкретная причина не использовать associated token account или когда вам нужно создать пользовательские PDA токен-аккаунты, выполняя CPI к инструкциям System Program и Token Program из вашей собственной программы Solana.

Справочник по исходному коду

ЭлементОписаниеToken ProgramToken Extensions Program
AccountБазовые поля токен-аккаунта, хранящиеся в каждом токен-аккаунте.Исходный кодИсходный код
InitializeAccountИнструкция инициализации токен-аккаунта, которая ожидает владельца и аккаунт rent sysvar в списке аккаунтов.Исходный кодИсходный код
InitializeAccount2Инструкция инициализации токен-аккаунта, которая передаёт владельца в данных инструкции вместо списка аккаунтов.Исходный кодИсходный код
InitializeAccount3Инструкция инициализации токен-аккаунта, которая передаёт владельца в данных инструкции и не требует аккаунта rent sysvar.Исходный кодИсходный код
_process_initialize_accountОбщая логика процессора для инициализации токен-аккаунта.Исходный кодИсходный код
process_initialize_accountПубличный обработчик для InitializeAccount.Исходный кодИсходный код
process_initialize_account2Публичный обработчик для InitializeAccount2.Исходный кодИсходный код
process_initialize_account3Публичный обработчик для InitializeAccount3.Исходный кодИсходный код

Typescript

Примеры ниже демонстрируют рекомендуемый подход с использованием Kit. Устаревшие примеры с использованием @solana/kit включены для справки.

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);
Console
Click to execute the code.

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);
Console
Click to execute the code.

Rust

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(())
}
Console
Click to execute the code.

Python

Python
#!/usr/bin/env python3
import asyncio
import json
from solana.rpc.async_api import AsyncClient
from solders.keypair import Keypair
from solders.message import Message
from solders.pubkey import Pubkey
from solders.system_program import create_account, CreateAccountParams
from solders.transaction import Transaction
from spl.token.async_client import AsyncToken
from spl.token.instructions import (
initialize_account,
InitializeAccountParams,
initialize_mint,
InitializeMintParams,
)
from spl.token.constants import ACCOUNT_LEN, MINT_LEN, TOKEN_PROGRAM_ID
async 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)).value
create_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 value
for 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())
Console
Click to execute the code.

Is this page helpful?

Управляется

© 2026 Solana Foundation.
Все права защищены.
Связаться с нами