Qué es una Cuenta de Tokens
Una token account almacena tokens para un mint y un propietario de cuenta de tokens en 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>,}
Aquí, owner significa la autoridad que puede transferir, quemar o delegar
tokens desde la token account. El propietario del program account sigue siendo
el Token Program o Token Extensions Program.
Cada token account está vinculada exactamente a un mint, lo que significa que la
token account solo puede contener unidades de un único token, el token
identificado por el campo mint de la token account.
Qué es una Cuenta de Tokens Asociada
Una associated token account (ATA) es la token account predeterminada para una billetera y un mint. El Associated Token Program deriva la dirección de ATA a partir de la dirección de la billetera, la dirección del programa de tokens y la dirección del mint.
Solo las cuentas de tokens creadas por el Associated Token Program se denominan associated token accounts.
El Associated Token Program es una forma de crear una token account en una dirección estándar y determinista. La cuenta resultante sigue siendo una token account propiedad del Token Program o Token Extensions Program, no del Associated Token Program.
El Associated Token Program deriva la dirección de ATA como se muestra en 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)}
Para cualquier combinación de billetera, programa de tokens y mint, existe
exactamente una dirección de ATA. El Associated Token Program crea una token
account estándar en esa dirección, y la cuenta resultante sigue usando el tipo
Account definido por el Token Program o Token Extensions Program.
Cómo Crear una Cuenta de Token Asociada
La creación de una cuenta de token asociada utiliza la instrucción Create
o CreateIdempotent del Programa de Token Asociado. El Programa de Token
Asociado deriva la dirección ATA, crea la cuenta en la dirección ATA e
inicializa la cuenta como una cuenta de token propiedad del Token Program o
Token Extension Program.
Recomendado
Para la mayoría de las aplicaciones, cree cuentas de token a través del Programa de Token Asociado en lugar de crearlas llamando directamente a las instrucciones del Programa del Sistema y del Programa de Token. Una cuenta de token asociada utiliza una dirección determinista derivada del propietario, el programa de token y el mint, lo que facilita que las billeteras y aplicaciones encuentren la cuenta de token predeterminada para un mint determinado.
Referencia de Código Fuente
| Elemento | Descripción | Fuente |
|---|---|---|
Create | Crea una cuenta de token asociada en la dirección ATA derivada. | Fuente |
CreateIdempotent | Crea la cuenta de token asociada, pero aún así tiene éxito si esa ATA ya existe para el mismo propietario y mint. | Fuente |
process_create_associated_token_account | Deriva la dirección ATA y utiliza create_pda_account más CPIs al programa de token seleccionado para inicializar la cuenta de token. Cuando el programa seleccionado es el Token Extension Program, el procesador ATA también inicializa la extensión de propietario inmutable. | Fuente |
create_pda_account | Función auxiliar utilizada por process_create_associated_token_account para crear la cuenta PDA mediante CPI al Programa del Sistema. | Fuente |
Typescript
Los ejemplos Kit a continuación muestran el enfoque recomendado usando
@solana/kit. Los ejemplos heredados usando @solana/web3.js se incluyen como
referencia.
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())
Cómo crear una cuenta de token
Crear una cuenta de token requiere dos instrucciones:
- La instrucción
CreateAccountdel System Program crea una nueva cuenta exenta de rent y asigna el Token Program como propietario del programa de la nueva cuenta. - La instrucción
InitializeAccount,InitializeAccount2oInitializeAccount3del Token Program inicializa la nueva cuenta para una casa de acuñación y propietario.
Incluye la instrucción CreateAccount y la instrucción de inicialización de
la cuenta de token en la misma transacción.
Durante la inicialización de la cuenta de token, el Token Program verifica que la cuenta no esté ya inicializada y esté exenta de rent.
La sección siguiente muestra cómo crear una cuenta de token invocando directamente las instrucciones del System Program y del Token Program. Para la mayoría de las aplicaciones, utiliza el Associated Token Program en su lugar. Utiliza las llamadas directas al System Program y Token Program cuando tengas una razón específica para no usar una associated token account o cuando necesites crear cuentas de token PDA personalizadas realizando CPIs a las instrucciones del System Program y Token Program desde tu propio programa Solana.
Referencia del código fuente
| Elemento | Descripción | Token Program | Token Extensions Program |
|---|---|---|---|
Account | Los campos base de la cuenta de token almacenados en cada cuenta de token. | Fuente | Fuente |
InitializeAccount | Una instrucción de inicialización de cuenta de token que espera el propietario y la cuenta sysvar de rent en su lista de cuentas. | Fuente | Fuente |
InitializeAccount2 | Una instrucción de inicialización de cuenta de token que pasa el propietario en instruction data en lugar de en la lista de cuentas. | Fuente | Fuente |
InitializeAccount3 | Una instrucción de inicialización de cuenta de token que pasa el propietario en instruction data y no requiere la cuenta sysvar de rent. | Fuente | Fuente |
_process_initialize_account | Lógica del procesador compartida para la inicialización de cuentas de token. | Fuente | Fuente |
process_initialize_account | Manejador público para InitializeAccount. | Fuente | Fuente |
process_initialize_account2 | Manejador público para InitializeAccount2. | Fuente | Fuente |
process_initialize_account3 | Manejador público para InitializeAccount3. | Fuente | Fuente |
Typescript
Los siguientes ejemplos Kit muestran el enfoque recomendado usando
@solana/kit. Los ejemplos heredados usando @solana/web3.js se incluyen como
referencia.
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?