Разработка SPL-токена (Solana)
EVM-разработчики, приходящие на Solana, обычно ожидают аналог ERC-20 с похожими концепциями. Реальность сильно отличается. В Solana нет концепции "контракта токена" как такового — есть Token Program (системная программа), которая управляет всеми токенами, и Mint account — аккаунт с метаданными конкретного токена. Логика настройки и кастомизации токена реализуется не через наследование контракта, а через комбинацию extension'ов Token-2022 и отдельных программ (например, Transfer Hook).
Token Program vs. Token-2022
На Solana существует два стандарта:
spl-token (Token Program) — оригинальный стандарт 2020 года. Простой, проверенный временем, поддерживается всеми кошельками и DEX. Возможности: mint, burn, transfer, freeze, multisig authority.
Token-2022 (Token Extensions Program) — новый стандарт с расширяемой архитектурой. Рекомендован для новых проектов. Ключевые расширения:
| Extension | Назначение |
|---|---|
TransferFee |
Комиссия на каждый transfer (аналог reflection/tax token) |
TransferHook |
Вызов кастомной программы при каждом transfer |
ConfidentialTransfer |
Приватные балансы через ZK proofs (ElGamal шифрование) |
PermanentDelegate |
Адрес с постоянным правом сжигать/переводить токены |
MintCloseAuthority |
Возможность закрыть Mint account (вернуть rent) |
NonTransferable |
Soulbound токены — нельзя передать |
InterestBearingMint |
Начисление процентов on-chain |
MetadataPointer + TokenMetadata |
Метаданные прямо в Mint account |
Создание Mint account
С помощью @solana/spl-token (TypeScript SDK):
import {
createMint,
createAssociatedTokenAccount,
mintTo,
TOKEN_2022_PROGRAM_ID,
ExtensionType,
getMintLen,
createInitializeMintInstruction,
createInitializeTransferFeeConfigInstruction,
} from "@solana/spl-token";
import { Connection, Keypair, SystemProgram, Transaction } from "@solana/web3.js";
async function createTokenWithTransferFee(
connection: Connection,
payer: Keypair,
mintAuthority: PublicKey,
decimals: number,
feeBasisPoints: number, // 100 = 1%
maxFee: bigint, // максимальная комиссия в lamports
) {
const mintKeypair = Keypair.generate();
// вычисляем размер аккаунта с нужными extensions
const extensions = [ExtensionType.TransferFeeConfig];
const mintLen = getMintLen(extensions);
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen);
const transaction = new Transaction().add(
// создаём аккаунт нужного размера
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mintKeypair.publicKey,
space: mintLen,
lamports,
programId: TOKEN_2022_PROGRAM_ID,
}),
// инициализируем TransferFee extension ДО инициализации mint
createInitializeTransferFeeConfigInstruction(
mintKeypair.publicKey,
payer.publicKey, // transferFeeConfigAuthority
payer.publicKey, // withdrawWithheldAuthority
feeBasisPoints,
maxFee,
TOKEN_2022_PROGRAM_ID,
),
// инициализируем mint
createInitializeMintInstruction(
mintKeypair.publicKey,
decimals,
mintAuthority,
null, // freeze authority — null если не нужна
TOKEN_2022_PROGRAM_ID,
),
);
await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair]);
return mintKeypair.publicKey;
}
Критически важный момент: extensions инициализируются до InitializeMint. Это контрнтуитивно для EVM-разработчиков, но так устроена архитектура Token-2022 — сначала настраиваем расширения, потом финализируем mint.
Associated Token Accounts
В Solana каждый пользователь должен иметь отдельный Token Account для каждого токена. Associated Token Account (ATA) — детерминированный адрес, производный от кошелька пользователя и адреса mint. ATA создаётся один раз, адрес всегда предсказуем:
import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction } from "@solana/spl-token";
const ata = await getAssociatedTokenAddress(
mintAddress,
walletAddress,
false, // allowOwnerOffCurve — обычно false
TOKEN_2022_PROGRAM_ID,
);
// создаём ATA если не существует (idempotent)
const createATAIx = createAssociatedTokenAccountInstruction(
payer.publicKey, // кто платит rent
ata,
walletAddress, // владелец
mintAddress,
TOKEN_2022_PROGRAM_ID,
);
Rent за Token Account (~0.002 SOL) платит создающая сторона — это важный UX-момент. Многие проекты берут на себя создание ATA для пользователей при первой операции (airdrop, покупка), включая стоимость rent в свою operational expenses.
Transfer Hook: кастомная логика при переводах
Transfer Hook — наиболее мощный extension, позволяющий выполнять произвольную логику при каждом transfer. Это Solana-аналог ERC-20 хуков или transfer callbacks.
Кастомная программа (Transfer Hook program) вызывается автоматически Token-2022 при каждом transfer. В программе можно реализовать: whitelist/blacklist проверку, royalty для токенов, блокировку переводов в locked period, integration с protocol state.
// Transfer Hook program (Anchor framework)
use anchor_lang::prelude::*;
use spl_transfer_hook_interface::instruction::ExecuteInstruction;
#[program]
pub mod transfer_hook {
use super::*;
// эта функция вызывается Token-2022 при каждом transfer
pub fn transfer_hook(ctx: Context<TransferHook>, amount: u64) -> Result<()> {
let sender = &ctx.accounts.source_token;
let receiver = &ctx.accounts.destination_token;
// проверяем whitelist
let config = &ctx.accounts.hook_config;
require!(
config.whitelist.contains(&receiver.owner),
TransferError::ReceiverNotWhitelisted
);
// логируем transfer для off-chain анализа
emit!(TransferEvent {
from: sender.owner,
to: receiver.owner,
amount,
timestamp: Clock::get()?.unix_timestamp,
});
Ok(())
}
}
Transfer Hook требует что все additional accounts, нужные хуку, были переданы в транзакцию заранее. Это усложняет клиентский код — нужно знать какие accounts нужны хуку перед отправкой транзакции. SDK предоставляет addExtraAccountMetasForExecute для автоматического разрешения этих accounts.
Метаданные токена
Метаданные (name, symbol, image, описание) хранятся двумя способами:
Metaplex Metadata Program — legacy подход, стандарт для NFT и большинства fungible токенов сегодня. Отдельный аккаунт с метаданными, привязанный к mint. Широкая поддержка кошельками и маркетплейсами.
Token Metadata Extension (Token-2022) — новый подход, метаданные прямо в Mint account. Проще архитектурно, не требует Metaplex. Поддержка кошельками растёт, но пока менее универсальна чем Metaplex.
Для production fungible токена (DeFi, governance) в 2024–2025 году рекомендуется Metaplex — максимальная совместимость с экосистемой (кошельки, DEX, агрегаторы цен).
Работа с Anchor framework
Большинство Solana-программ пишется на Rust с Anchor framework. Anchor генерирует IDL (Interface Definition Language) — аналог ABI в EVM — что упрощает клиентскую интеграцию:
anchor build # компиляция + генерация IDL
anchor test # тесты на localnet
anchor deploy # деплой на devnet/mainnet
Для токена без кастомной программы Anchor не нужен — достаточно @solana/spl-token SDK. Anchor необходим если строится dApp поверх токена с кастомной бизнес-логикой (стейкинг, вестинг, governance).







