Token account 생성하기

토큰 계정이란

토큰 계정은 솔라나에서 하나의 민트와 하나의 토큰 계정 소유자를 위한 토큰을 저장합니다.

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

여기서 *rsowner*는 token account에서 토큰을 전송, 소각 또는 위임할 수 있는 권한을 의미합니다. 계정의 프로그램 소유자는 여전히 Token Program 또는 Token Extensions Program입니다.

각 token account는 정확히 하나의 민트에 연결되어 있으며, 이는 token account가 token account의 mint 필드로 식별되는 토큰인 하나의 토큰 단위만 보유할 수 있음을 의미합니다.

Associated Token Account란

Associated token account(ATA)는 지갑과 민트를 위한 기본 토큰 계정입니다. Associated Token Program은 지갑 주소, 토큰 프로그램 주소 및 민트 주소로부터 ATA 주소를 도출합니다.

Associated Token Program에 의해 생성된 토큰 계정만 associated token account라고 불립니다.

Associated Token Program은 표준적이고 결정론적인 주소에서 토큰 계정을 생성하는 방법입니다. 결과 계정은 여전히 Associated Token Program이 아닌 Token Program 또는 Token Extensions Program이 소유한 token account입니다.

Associated Token Program은 address.rs에 표시된 대로 ATA 주소를 도출합니다:

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은 해당 주소에 표준 토큰 계정을 생성하며, 결과 계정은 여전히 Token Program 또는 Token Extensions Program에 정의된 Account 타입을 사용합니다.

연결된 토큰 계정 생성 방법

연결된 토큰 계정을 생성하려면 Associated Token Program의 Create 또는 CreateIdempotent 명령을 사용합니다. Associated Token Program은 ATA 주소를 파생하고, ATA 주소에 계정을 생성하며, Token Program 또는 Token Extensions Program이 소유한 토큰 계정으로 계정을 초기화합니다.

권장사항

대부분의 애플리케이션에서는 System Program과 Token Program 명령을 직접 호출하는 대신 Associated Token Program을 통해 토큰 계정을 생성하세요. 연결된 토큰 계정은 소유자, 토큰 프로그램 및 민트에서 파생된 결정론적 주소를 사용하므로, 지갑과 애플리케이션이 특정 민트의 기본 토큰 계정을 더 쉽게 찾을 수 있습니다.

소스 참조

항목설명소스
Create파생된 ATA 주소에 연결된 토큰 계정을 생성합니다.소스
CreateIdempotent연결된 토큰 계정을 생성하지만, 동일한 소유자와 민트에 대한 ATA가 이미 존재하는 경우에도 여전히 성공합니다.소스
process_create_associated_token_accountATA 주소를 파생하고 *rscreate_pda_account*와 선택한 토큰 프로그램에 대한 CPI를 사용하여 토큰 계정을 초기화합니다. 선택한 프로그램이 Token Extensions Program인 경우, ATA 프로세서는 불변 소유자 확장도 초기화합니다.소스
create_pda_accountSystem Program에 대한 CPI를 통해 PDA 계정을 생성하기 위해 *rsprocess_create_associated_token_account*에서 사용하는 헬퍼입니다.소스

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. System Program의 CreateAccount 명령어는 rent가 면제된 새 계정을 생성하고 Token Program을 새 계정의 프로그램 소유자로 할당합니다.
  2. Token Program의 InitializeAccount, InitializeAccount2 또는 InitializeAccount3 명령어는 민트와 소유자에 대해 새 계정을 초기화합니다.

CreateAccount 명령어와 토큰 계정 초기화 명령어를 동일한 트랜잭션에 포함시킵니다.

토큰 계정 초기화 중에 Token Program은 해당 계정이 이미 초기화되지 않았으며 rent가 면제되었는지 확인합니다.

아래 섹션에서는 System Program과 Token Program 명령어를 직접 호출하여 토큰 계정을 생성하는 방법을 보여줍니다. 대부분의 애플리케이션에서는 대신 Associated Token Program을 사용하세요. associated token account를 사용하지 않을 특별한 이유가 있거나, 자체 Solana 프로그램에서 System Program 및 Token Program 명령어에 대한 CPI를 수행하여 사용자 정의 PDA 토큰 계정을 생성해야 하는 경우 직접 System Program 및 Token Program 호출을 사용하세요.

소스 참조

항목설명Token ProgramToken Extensions Program
Account모든 토큰 계정에 저장되는 기본 토큰 계정 필드입니다.소스소스
InitializeAccount계정 목록에 소유자와 rent sysvar 계정을 요구하는 토큰 계정 초기화 명령어입니다.소스소스
InitializeAccount2계정 목록 대신 instruction data에 소유자를 전달하는 토큰 계정 초기화 명령어입니다.소스소스
InitializeAccount3instruction data에 소유자를 전달하고 rent sysvar 계정을 필요로 하지 않는 토큰 계정 초기화 명령어입니다.소스소스
_process_initialize_account토큰 계정 초기화를 위한 공유 프로세서 로직입니다.소스소스
process_initialize_account*rsInitializeAccount*를 위한 공개 핸들러입니다.소스소스
process_initialize_account2*rsInitializeAccount2*를 위한 공개 핸들러입니다.소스소스
process_initialize_account3*rsInitializeAccount3*를 위한 공개 핸들러입니다.소스소스

Typescript

아래 Kit 예제는 @solana/kit를 사용하는 권장 방식을 보여줍니다. 참고용으로 @solana/web3.js를 사용하는 레거시 예제도 포함되어 있습니다.

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 솔라나 재단.
모든 권리 보유.
연결하기