Δημιουργία ενός Token Account

Τι είναι ένα Token Account;

Ένα token account αποθηκεύει το υπόλοιπό σας σε ένα συγκεκριμένο token. Κάθε token account συνδέεται με ακριβώς ένα mint και παρακολουθεί το υπόλοιπο των token σας και πρόσθετες λεπτομέρειες.

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>,
}

Σημειώστε ότι στον πηγαίο κώδικα, οι προγραμματιστές αποκαλούν ένα Token account ως τύπο Account. Τόσο το Token Program όσο και το Token Extension Program μοιράζονται την ίδια βασική υλοποίηση για το Token account.

Για να διατηρήσετε tokens, χρειάζεστε ένα token account για αυτό το συγκεκριμένο mint. Κάθε token account παρακολουθεί:

  1. Mint: Τον συγκεκριμένο τύπο token που διατηρεί
  2. Owner: Την αρχή που μπορεί να μεταφέρει tokens από αυτόν τον λογαριασμό

Παράδειγμα: USD Coin (USDC)

  • Διεύθυνση mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
  • Token account της Circle: 3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa
  • Αυτός ο λογαριασμός διατηρεί μόνο USDC tokens
  • Η Circle ελέγχει τα token μέσω της αρχής ιδιοκτήτη του token account: 7VHUFJHWu2CuExkJcJrzhQPJ2oygupTWkL2A2For4BmE

Μπορείτε να δείτε τις λεπτομέρειες αυτού του token account στον Solana Explorer.

Κατανόηση της ορολογίας "Owner"

Ο όρος "owner" μπορεί να χρησιμοποιηθεί σε δύο διαφορετικά πλαίσια:

  1. Token Account Owner: Η αρχή που μπορεί να μεταφέρει, να κάψει ή να αναθέσει tokens. Αυτό αποθηκεύεται στα δεδομένα του token account.

  2. Program Owner: Το πρόγραμμα που κατέχει τον λογαριασμό (πάντα το Token Program ή το Token Extension Program για token accounts).

Όταν εργάζεστε με token accounts, ο "owner" συνήθως αναφέρεται στην αρχή που μπορεί να μεταφέρει tokens, όχι στο πρόγραμμα που κατέχει τον λογαριασμό.

Τι είναι ένα Associated Token Account;

Ένα associated token account (ATA) είναι το προεπιλεγμένο token account για ένα πορτοφόλι για τη διατήρηση ενός συγκεκριμένου token. Χρησιμοποιεί μια ντετερμινιστική διεύθυνση (PDA) που δημιουργείται από το Associated Token Program.

Μόνο τα token accounts που δημιουργούνται από το Associated Token Program ονομάζονται "associated token accounts."

Τα ATAs χρησιμοποιούν ντετερμινιστικές διευθύνσεις, καθιστώντας εύκολη την εύρεση του token account οποιουδήποτε πορτοφολιού για ένα δεδομένο mint. Δείτε την υλοποίηση παραγωγής εδώ.

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
)
}

Για οποιονδήποτε συνδυασμό πορτοφολιού και token, υπάρχει ακριβώς μία διεύθυνση ATA. Αυτό εξαλείφει την ανάγκη χειροκίνητης παρακολούθησης των διευθύνσεων token account—μπορείτε πάντα να παράγετε τη σωστή διεύθυνση.

Πώς λειτουργεί το Associated Token Program

Το Associated Token Program είναι ένα βοηθητικό πρόγραμμα που:

  • Δημιουργεί token accounts σε προκαθορισμένες διευθύνσεις (PDAs)
  • Πραγματοποιεί Cross-Program Invocations (CPIs) στο Token Program
  • Δεν διατηρεί καμία κατάσταση από μόνο του

Τα token accounts που δημιουργούνται ανήκουν στο Token Program και χρησιμοποιούν την τυπική δομή Account.

Πώς να δημιουργήσετε ένα Token Account

Η δημιουργία ενός token account απαιτεί την εντολή InitializeAccount. Δείτε την υλοποίηση εδώ.

Χρειάζεστε δύο εντολές για να δημιουργήσετε ένα token account:

  1. System Program: Δημιουργία λογαριασμού με κατανεμημένο χώρο για ένα token account και μεταφορά ιδιοκτησίας στο Token Program
  2. Token Program: Αρχικοποίηση των δεδομένων του token account

Typescript

import {
airdropFactory,
appendTransactionMessageInstructions,
createSolanaRpc,
createSolanaRpcSubscriptions,
createTransactionMessage,
generateKeyPairSigner,
getSignatureFromTransaction,
lamports,
pipe,
sendAndConfirmTransactionFactory,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
signTransactionMessageWithSigners
} from "@solana/kit";
import { getCreateAccountInstruction } from "@solana-program/system";
import {
getInitializeAccount2Instruction,
getInitializeMintInstruction,
getMintSize,
getTokenSize,
TOKEN_PROGRAM_ADDRESS
} from "@solana-program/token";
// Create Connection, local validator in this example
const rpc = createSolanaRpc("http://localhost:8899");
const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");
// Get latest blockhash to include in transaction
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// Generate keypairs for fee payer
const feePayer = await generateKeyPairSigner();
// Fund fee payer
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: feePayer.address,
lamports: lamports(1_000_000_000n),
commitment: "confirmed"
});
// Generate keypair to use as address of mint
const mint = await generateKeyPairSigner();
// Get default mint account size (in bytes), no extensions enabled
const space = BigInt(getMintSize());
// Get minimum balance for rent exemption
const rent = await rpc.getMinimumBalanceForRentExemption(space).send();
// Instruction to create new account for mint (token program)
// Invokes the system program
const createAccountInstruction = getCreateAccountInstruction({
payer: feePayer,
newAccount: mint,
lamports: rent,
space,
programAddress: TOKEN_PROGRAM_ADDRESS
});
// Instruction to initialize mint account data
// Invokes the token program
const initializeMintInstruction = getInitializeMintInstruction({
mint: mint.address,
decimals: 2,
mintAuthority: feePayer.address
});
const instructions = [createAccountInstruction, initializeMintInstruction];
// Create transaction message
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }), // Create transaction message
(tx) => setTransactionMessageFeePayerSigner(feePayer, tx), // Set fee payer
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), // Set transaction blockhash
(tx) => appendTransactionMessageInstructions(instructions, tx) // Append instructions
);
// Sign transaction message with required signers (fee payer and mint keypair)
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
// Send and confirm transaction
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransaction,
{ commitment: "confirmed" }
);
// Get transaction signature
const transactionSignature = getSignatureFromTransaction(signedTransaction);
console.log("Mint Address:", mint.address);
console.log("Transaction Signature:", transactionSignature);
// Generate keypair to use as address of token account
const tokenAccount = await generateKeyPairSigner();
// Get token account size (in bytes)
const tokenAccountSpace = BigInt(getTokenSize());
// Get minimum balance for rent exemption
const tokenAccountRent = await rpc
.getMinimumBalanceForRentExemption(tokenAccountSpace)
.send();
// Instruction to create new account for token account (token program)
// Invokes the system program
const createTokenAccountInstruction = getCreateAccountInstruction({
payer: feePayer,
newAccount: tokenAccount,
lamports: tokenAccountRent,
space: tokenAccountSpace,
programAddress: TOKEN_PROGRAM_ADDRESS
});
// Instruction to initialize token account data
// Invokes the token program
const initializeTokenAccountInstruction = getInitializeAccount2Instruction({
account: tokenAccount.address,
mint: mint.address,
owner: feePayer.address
});
const instructions2 = [
createTokenAccountInstruction,
initializeTokenAccountInstruction
];
// Create transaction message for token account creation
const tokenAccountMessage = pipe(
createTransactionMessage({ version: 0 }), // Create transaction message
(tx) => setTransactionMessageFeePayerSigner(feePayer, tx), // Set fee payer
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), // Set transaction blockhash
(tx) => appendTransactionMessageInstructions(instructions2, tx) // Append instructions
);
// Sign transaction message with required signers (fee payer and token account keypair)
const signedTokenAccountTx =
await signTransactionMessageWithSigners(tokenAccountMessage);
// Send and confirm transaction
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTokenAccountTx,
{ commitment: "confirmed" }
);
// Get transaction signature
const transactionSignature2 = getSignatureFromTransaction(signedTokenAccountTx);
console.log("Token Account Address:", tokenAccount.address);
console.log("Transaction Signature:", transactionSignature2);
Console
Click to execute the code.

Rust

use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{
commitment_config::CommitmentConfig,
program_pack::Pack,
signature::{Keypair, Signer},
system_instruction::create_account,
transaction::Transaction,
};
use spl_token::{
id as token_program_id,
instruction::{initialize_account, initialize_mint},
state::{Account, Mint},
};
#[tokio::main]
async fn main() -> Result<()> {
// Create connection to local validator
let client = RpcClient::new_with_commitment(
String::from("http://localhost:8899"),
CommitmentConfig::confirmed(),
);
let latestBlockhash = client.get_latest_blockhash().await?;
// Generate a new keypair for the fee payer
let fee_payer = Keypair::new();
// Airdrop 1 SOL to fee payer
let airdrop_signature = client
.request_airdrop(&fee_payer.pubkey(), 1_000_000_000)
.await?;
client.confirm_transaction(&airdrop_signature).await?;
loop {
let confirmed = client.confirm_transaction(&airdrop_signature).await?;
if confirmed {
break;
}
}
// Generate keypair to use as address of mint
let mint = Keypair::new();
// Get default mint account size (in bytes), no extensions enabled
let mint_space = Mint::LEN;
let mint_rent = client
.get_minimum_balance_for_rent_exemption(mint_space)
.await?;
// Instruction to create new account for mint (token program)
let create_account_instruction = create_account(
&fee_payer.pubkey(), // payer
&mint.pubkey(), // new account (mint)
mint_rent, // lamports
mint_space as u64, // space
&token_program_id(), // program id
);
// Instruction to initialize mint account data
let initialize_mint_instruction = initialize_mint(
&token_program_id(),
&mint.pubkey(), // mint
&fee_payer.pubkey(), // mint authority
Some(&fee_payer.pubkey()), // freeze authority
2, // decimals
)?;
// Create transaction and add instructions
let transaction = Transaction::new_signed_with_payer(
&[create_account_instruction, initialize_mint_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer, &mint],
latestBlockhash,
);
// Send and confirm transaction
let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;
println!("Mint Address: {}", mint.pubkey());
println!("Transaction Signature: {}", transaction_signature);
// Generate keypair to use as address of token account
let token_account = Keypair::new();
// Get token account size (in bytes)
let token_account_space = Account::LEN;
let token_account_rent = client
.get_minimum_balance_for_rent_exemption(token_account_space)
.await?;
// Instruction to create new account for token account (token program)
let create_token_account_instruction = create_account(
&fee_payer.pubkey(), // payer
&token_account.pubkey(), // new account (token account)
token_account_rent, // lamports
token_account_space as u64, // space
&token_program_id(), // program id
);
// Instruction to initialize token account data
let initialize_token_account_instruction = initialize_account(
&token_program_id(),
&token_account.pubkey(), // account
&mint.pubkey(), // mint
&fee_payer.pubkey(), // owner
)?;
// Create transaction and add instructions
let transaction = Transaction::new_signed_with_payer(
&[
create_token_account_instruction,
initialize_token_account_instruction,
],
Some(&fee_payer.pubkey()),
&[&fee_payer, &token_account],
latestBlockhash,
);
// Send and confirm transaction
let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;
println!("Token Account Address: {}", token_account.pubkey());
println!("Transaction Signature: {}", transaction_signature);
Ok(())
}
Console
Click to execute the code.

Πώς να δημιουργήσετε ένα Associated Token Account

Η δημιουργία ενός Associated Token Account (ATA) απαιτεί την εντολή Create. Δείτε την υλοποίηση εδώ.

Αυτή η μοναδική εντολή διαχειρίζεται αυτόματα τη δημιουργία του token account και την αρχικοποίησή του μέσω Cross Program Invocations (CPI).

Typescript

import {
airdropFactory,
appendTransactionMessageInstructions,
createSolanaRpc,
createSolanaRpcSubscriptions,
createTransactionMessage,
generateKeyPairSigner,
getSignatureFromTransaction,
lamports,
pipe,
sendAndConfirmTransactionFactory,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
signTransactionMessageWithSigners
} from "@solana/kit";
import { getCreateAccountInstruction } from "@solana-program/system";
import {
getCreateAssociatedTokenInstructionAsync,
getInitializeMintInstruction,
getMintSize,
TOKEN_PROGRAM_ADDRESS,
findAssociatedTokenPda
} from "@solana-program/token";
// Create Connection, local validator in this example
const rpc = createSolanaRpc("http://localhost:8899");
const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");
// Generate keypairs for fee payer
const feePayer = await generateKeyPairSigner();
// Fund fee payer
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: feePayer.address,
lamports: lamports(1_000_000_000n),
commitment: "confirmed"
});
// Generate keypair to use as address of mint
const mint = await generateKeyPairSigner();
// Get default mint account size (in bytes), no extensions enabled
const space = BigInt(getMintSize());
// Get minimum balance for rent exemption
const rent = await rpc.getMinimumBalanceForRentExemption(space).send();
// Get latest blockhash to include in transaction
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// Instruction to create new account for mint (token program)
// Invokes the system program
const createAccountInstruction = getCreateAccountInstruction({
payer: feePayer,
newAccount: mint,
lamports: rent,
space,
programAddress: TOKEN_PROGRAM_ADDRESS
});
// Instruction to initialize mint account data
// Invokes the token program
const initializeMintInstruction = getInitializeMintInstruction({
mint: mint.address,
decimals: 2,
mintAuthority: feePayer.address
});
const instructions = [createAccountInstruction, initializeMintInstruction];
// Create transaction message
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(feePayer, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions(instructions, tx)
);
// Sign transaction message with all required signers
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
// Send and confirm transaction
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransaction,
{ commitment: "confirmed" }
);
// Get transaction signature
const transactionSignature = getSignatureFromTransaction(signedTransaction);
console.log("Mint Address:", mint.address.toString());
console.log("Transaction Signature:", transactionSignature);
// Use findAssociatedTokenPda to derive the ATA address
const [associatedTokenAddress] = await findAssociatedTokenPda({
mint: mint.address,
owner: feePayer.address,
tokenProgram: TOKEN_PROGRAM_ADDRESS
});
console.log(
"Associated Token Account Address:",
associatedTokenAddress.toString()
);
// Get a fresh blockhash for the second transaction
const { value: latestBlockhash2 } = await rpc.getLatestBlockhash().send();
// Create instruction to create the associated token account
const createAtaInstruction = await getCreateAssociatedTokenInstructionAsync({
payer: feePayer,
mint: mint.address,
owner: feePayer.address
});
// Create transaction message
const transactionMessage2 = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(feePayer, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash2, tx),
(tx) => appendTransactionMessageInstructions([createAtaInstruction], tx)
);
// Sign transaction message with all required signers
const signedTransaction2 =
await signTransactionMessageWithSigners(transactionMessage2);
// Send and confirm transaction
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransaction2,
{ commitment: "confirmed" }
);
// Get transaction signature
const transactionSignature2 = getSignatureFromTransaction(signedTransaction2);
console.log("Transaction Signature:", transactionSignature2);
Console
Click to execute the code.

Rust

use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{
commitment_config::CommitmentConfig,
program_pack::Pack,
signature::{Keypair, Signer},
system_instruction::create_account,
transaction::Transaction,
};
use spl_associated_token_account::{
get_associated_token_address_with_program_id, instruction::create_associated_token_account,
};
use spl_token::{id as token_program_id, instruction::initialize_mint, state::Mint};
#[tokio::main]
async fn main() -> Result<()> {
// Create connection to local validator
let client = RpcClient::new_with_commitment(
String::from("http://localhost:8899"),
CommitmentConfig::confirmed(),
);
let latestBlockhash = client.get_latest_blockhash().await?;
// Generate a new keypair for the fee payer
let fee_payer = Keypair::new();
// Airdrop 1 SOL to fee payer
let airdrop_signature = client
.request_airdrop(&fee_payer.pubkey(), 1_000_000_000)
.await?;
client.confirm_transaction(&airdrop_signature).await?;
loop {
let confirmed = client.confirm_transaction(&airdrop_signature).await?;
if confirmed {
break;
}
}
// Generate keypair to use as address of mint
let mint = Keypair::new();
// Get default mint account size (in bytes), no extensions enabled
let mint_space = Mint::LEN;
let mint_rent = client
.get_minimum_balance_for_rent_exemption(mint_space)
.await?;
// Instruction to create new account for mint (token program)
let create_account_instruction = create_account(
&fee_payer.pubkey(), // payer
&mint.pubkey(), // new account (mint)
mint_rent, // lamports
mint_space as u64, // space
&token_program_id(), // program id
);
// Instruction to initialize mint account data
let initialize_mint_instruction = initialize_mint(
&token_program_id(),
&mint.pubkey(), // mint
&fee_payer.pubkey(), // mint authority
Some(&fee_payer.pubkey()), // freeze authority
2, // decimals
)?;
// Create transaction and add instructions
let transaction = Transaction::new_signed_with_payer(
&[create_account_instruction, initialize_mint_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer, &mint],
latestBlockhash,
);
// Send and confirm transaction
let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;
println!("Mint Address: {}", mint.pubkey());
println!("Transaction Signature: {}", transaction_signature);
// Get the latest blockhash for the next transaction
let latestBlockhash = client.get_latest_blockhash().await?;
// Derive the associated token account address for fee_payer
let associated_token_account = get_associated_token_address_with_program_id(
&fee_payer.pubkey(), // owner
&mint.pubkey(), // mint
&token_program_id(), // program_id
);
// Instruction to create associated token account
let create_ata_instruction = create_associated_token_account(
&fee_payer.pubkey(), // funding address
&fee_payer.pubkey(), // wallet address (owner)
&mint.pubkey(), // mint address
&token_program_id(), // program id
);
// Create transaction for associated token account creation
let transaction = Transaction::new_signed_with_payer(
&[create_ata_instruction],
Some(&fee_payer.pubkey()),
&[&fee_payer],
latestBlockhash,
);
// Send and confirm transaction
let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;
println!(
"Associated Token Account Address: {}",
associated_token_account.to_string()
);
println!("Transaction Signature: {}", transaction_signature);
Ok(())
}
Console
Click to execute the code.

Is this page helpful?

Πίνακας Περιεχομένων

Επεξεργασία Σελίδας