Готовность к продакшену

Локальная разработка и тестирование в devnet — отличные способы начать работу с платежами в Solana. Однако при переходе к развертыванию в Mainnet важно учитывать его особенности. Devnet прощает ошибки. Mainnet — нет. В этом руководстве рассматриваются ключевые различия, чтобы ваши пользователи получили максимально плавный опыт.

DevnetMainnet
Бесплатные SOL из крановПолучите настоящие SOL для оплаты комиссий
Низкая конкуренция за блокиПриоритетные комиссии имеют значение
Транзакции проходят легкоВажно правильно настраивать транзакции
Публичный RPC подходитДля продакшена требуется выделенный RPC
Ключи и токены devnetИспользуйте другие ключи и токены — обновите конфиг

Инфраструктура RPC

Публичные endpoints (api.mainnet-beta.solana.com) имеют ограничения по количеству запросов и не гарантируют SLA. Они подходят для разработки, но не для продакшен-платежей — это как запускать платежный процессор через общий API без гарантии доступности.

Никогда не используйте публичный RPC в продакшене

Используйте частного провайдера RPC для надежного и быстрого доступа.

При выборе провайдера RPC обратите внимание на:

  • Надежность: SLA с гарантией доступности (99,9% и выше)
  • Задержки: Географическая близость к вашим пользователям
  • Функциональность: Возможности по обработке транзакций, индексация, API для приоритетных комиссий

Полный список провайдеров RPC смотрите в руководстве Поставщики инфраструктуры RPC.

Резервная конфигурация RPC

Как и любой сетевой сервис, провайдеры RPC могут сталкиваться с перебоями или снижением производительности. Чтобы ваше приложение было устойчивым, настройте его на работу с несколькими провайдерами RPC.

Solana Kit предоставляет библиотеку для кастомизации RPC-транспортов, что позволяет создавать собственный отказоустойчивый RPC-клиент. Вот пример того, как можно использовать её для создания отказоустойчивого RPC-клиента:

import { RpcTransport } from "@solana/rpc-spec";
import { RpcResponse } from "@solana/rpc-spec-types";
import { createHttpTransport } from "@solana/rpc-transport-http";
// Create a transport for each RPC server
const transports = [
createHttpTransport({ url: "https://mainnet-beta.my-server-1.com" }),
createHttpTransport({ url: "https://mainnet-beta.my-server-2.com" }),
createHttpTransport({ url: "https://mainnet-beta.my-server-3.com" })
];
// Create a wrapper transport that distributes requests to them
let nextTransport = 0;
async function roundRobinTransport<TResponse>(
...args: Parameters<RpcTransport>
): Promise<RpcResponse<TResponse>> {
const transport = transports[nextTransport];
nextTransport = (nextTransport + 1) % transports.length;
return await transport(...args);
}

Если вы не хотите создавать собственные инструменты маршрутизации, вы можете воспользоваться сторонним сервисом, например, Iron Forge, который возьмёт маршрутизацию на себя.

Завершение транзакции

В Devnet транзакции проходят довольно легко. В Mainnet вы конкурируете за место в блоке. Чтобы увеличить шансы включения вашей транзакции в блок, убедитесь, что вы правильно собрали транзакцию. Это значит:

  • добавить свежий blockhash перед отправкой транзакции
  • добавить инструкцию с приоритетной комиссией в транзакцию с конкурентной приоритетной комиссией
  • добавить инструкцию с лимитом вычислительных единиц в транзакцию с лимитом, основанным на оценке требуемых вычислительных единиц для транзакции

Кроме того, стоит рассмотреть другие инструменты, такие как Jito Bundles, чтобы повысить шансы включения вашей транзакции в блок. Давайте рассмотрим эти инструменты подробнее.

Конфигурация отправки транзакции

При отправке транзакций в Mainnet настройте эти параметры для оптимального попадания в блок:

Управление blockhash:

  • Получайте с помощью confirmed commitment
  • Сохраняйте lastValidBlockHeight, возвращённый getLatestBlockhash — это покажет, когда истекает срок действия вашей транзакции
  • Blockhash истекает примерно через 150 блоков (60–90 секунд)

Параметры отправки:

  • maxRetries: 0 — отключить автоматические повторные попытки RPC. Обрабатывайте повторные попытки самостоятельно, чтобы при необходимости обновлять blockhash.
  • skipPreflight: true — пропустить симуляцию перед отправкой. Используйте это, если вы уже проверили транзакцию и хотите минимальную задержку. Оставьте false во время разработки, чтобы раньше выявлять ошибки.
import { createSolanaRpc } from "@solana/kit";
const rpc = createSolanaRpc(process.env.RPC_URL!);
// 1. Get blockhash with confirmed commitment
const { value: latestBlockhash } = await rpc
.getLatestBlockhash({ commitment: "confirmed" })
.send();
// 2. Build and sign your transaction with the blockhash
// ... (transaction building code)
// 3. Send with production settings
const signature = await rpc
.sendTransaction(encodedTransaction, {
encoding: "base64",
maxRetries: 0n, // Handle retries yourself
skipPreflight: true, // Skip simulation for speed (use false during dev)
preflightCommitment: "confirmed"
})
.send();
// 4. Track expiration using lastValidBlockHeight
const { lastValidBlockHeight } = latestBlockhash;
// Stop retrying when current block height exceeds lastValidBlockHeight

Используйте приоритетные комиссии

Каждая транзакция в Solana требует уплаты комиссии, которая оплачивается в SOL. Комиссия за транзакцию состоит из двух частей: базовой комиссии и приоритетной комиссии. Базовая комиссия вознаграждает валидаторов за обработку транзакции. Приоритетная комиссия — это необязательный платеж, который увеличивает вероятность того, что текущий лидер обработает вашу транзакцию. Представьте это как экспресс-доставку: вы платите больше за более быструю и надёжную доставку.

Как работают комиссии:

Total fee = Base fee (5,000 lamports per signature) + Priority fee
Priority fee = Compute units x Price per unit (micro-lamports per compute unit)

Реальные затраты:

  • Простой перевод USDC: ~$0.001-0.005 при нормальных условиях
  • Во время перегрузки: ~$0.01-0.05
  • Пиковая перегрузка: может быть выше

Пример реализации:

Пакет @solana-program/compute-budget предоставляет вспомогательную функцию для простого обновления или добавления инструкции по цене вычислительных единиц (в микролампортах) в транзакцию.

import { updateOrAppendSetComputeUnitPriceInstruction } from "@solana-program/compute-budget";
const tx = pipe(
createTransactionMessage({ version: 0 }),
(m) => setTransactionMessageFeePayerSigner(payer, m),
(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
(m) => appendTransactionMessageInstructions([myInstructions], m),
(m) => updateOrAppendSetComputeUnitPriceInstruction(1000n as MicroLamports, m)
);

Получение оценок комиссий: большинство RPC-провайдеров предлагают API для приоритетных комиссий:

Полную механику комиссий смотрите в разделе Комиссии за транзакции и в нашем гиде: Как добавить приоритетные комиссии в транзакцию.

Оптимизация вычислительных единиц

Вычисления в Solana — это по сути мера объёма работы, которую выполняет программа. Существует лимит на количество вычислений, которое может быть использовано в одной транзакции (в настоящее время 1,4 миллиона вычислительных единиц), а также лимит на количество вычислений на один аккаунт в одном блоке (сейчас 100 миллионов вычислительных единиц).

Когда вы отправляете транзакцию, необходимо оценить, сколько вычислительных единиц будет использовано, и установить соответствующий лимит — это по сути запрос на то, сколько общей мощности должно быть зарезервировано для вашей транзакции. На практике это означает, что правильная оценка необходимых вычислительных единиц критически важна для включения вашей транзакции в блок (и важна для управления приоритетными комиссиями).

В Solana JSON RPC API есть метод simulatetransaction, который можно использовать для оценки вычислительных единиц, необходимых для транзакции, включая оценку используемых вычислительных единиц. @solana/kit предоставляет вспомогательные функции, которые оценивают лимиты ресурсов транзакции и устанавливают их в сообщении за один шаг (используя метод simulatetransaction под капотом). Для транзакций версии 1 эти вспомогательные функции также оценивают лимит размера данных загружаемых аккаунтов.

import {
estimateResourceLimitsFactory,
estimateAndSetResourceLimitsFactory
} from "@solana/kit";
const estimateResourceLimits = estimateResourceLimitsFactory({ rpc });
const estimateAndSetResourceLimits = estimateAndSetResourceLimitsFactory(
estimateResourceLimits
);
const txWithResourceLimits = await estimateAndSetResourceLimits(tx);

Если вы собираете и отправляете транзакции с помощью плагин-клиента kit, этот шаг обычно не нужен — клиент автоматически добавляет инструкции по бюджету вычислений при отправке (.sendTransaction()). Описанный выше ручной способ применяется при непосредственной сборке транзакций с помощью @solana/kit.

В продакшн-среде, если вы повторяете один и тот же тип транзакции несколько раз, рекомендуется кэшировать оценку вычислительных ресурсов для данного типа транзакции, чтобы избежать накладных расходов на повторную оценку при каждом вызове.

Jito Bundles

Jito bundles — это инструмент для управления атомарным выполнением нескольких транзакций. Это достигается путём отправки нескольких транзакций в сеть Jito с чаевыми. Чаевые можно использовать для мотивации сети Jito включать ваши транзакции в блок.

Ресурсы:

Стратегии повторных попыток

Транзакции могут завершаться неудачно по многим причинам. В отличие от традиционных платёжных API, которые немедленно возвращают результат успеха или ошибки, блокчейн-транзакции требуют отслеживания подтверждения.

Ключевые концепции:

  • Истечение срока действия blockhash: транзакции действительны в течение ~150 блоков (~60–90 секунд)
  • Идемпотентность: одна и та же подписанная транзакция всегда создаёт одну и ту же подпись — повторная отправка безопасна
  • Экспоненциальная задержка: избегайте перегрузки сети частыми повторными попытками
import {
createSolanaRpc,
createSolanaRpcSubscriptions,
sendAndConfirmTransactionFactory,
isSolanaError,
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED
} from "@solana/kit";
const rpc = createSolanaRpc(process.env.RPC_URL!);
const rpcSubscriptions = createSolanaRpcSubscriptions(process.env.RPC_WSS_URL!);
const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({
rpc,
rpcSubscriptions
});
// Send with automatic confirmation tracking and block height monitoring
try {
await sendAndConfirmTransaction(signedTransaction, {
commitment: "confirmed",
// Optional: abort after 75 seconds
abortSignal: AbortSignal.timeout(75_000)
});
} catch (e) {
if (isSolanaError(e, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED)) {
// Blockhash expired—rebuild transaction with fresh blockhash and retry
rebuildAndRetryTransaction(); // implement your own logic for rebuilding and retrying the transaction
}
throw e;
}

sendAndConfirmTransactionFactory из @solana/kit автоматически обрабатывает опрос подтверждений и отслеживание высоты блока. Он выбрасывает SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, когда срок действия blockhash транзакции истекает, сигнализируя о необходимости пересобрать транзакцию с новым blockhash.

Дополнительные ресурсы

Уровни подтверждения транзакций

Solana предлагает три уровня подтверждения. В терминах традиционных финансов:

УровеньОпределение SolanaАналог в традиционных финансахСценарий использования
processedВ блоке, голосование ещё не завершеноАвторизация в ожиданииОбновления UI в реальном времени
confirmedПроголосовало супербольшинствоСредства зачисленыБольшинство платежей
finalizedЗафиксировано, необратимоСредства урегулированыКрупные суммы, комплаенс

Когда использовать каждый уровень:

  • Обновления UI: показывайте processed для мгновенной обратной связи («Платёж отправлен»)
  • Зачисление на аккаунт пользователя: дождитесь confirmed (безопасно для большинства транзакций)
  • Отправка физических товаров: дождитесь finalized
  • Крупные выводы средств: дождитесь finalized
  • Комплаенс/аудит: всегда фиксируйте статус finalized

Подробнее о проверке статуса транзакций см. в разделе Взаимодействие с Solana.

Обработка ошибок

Solana Kit предоставляет типизированные ошибки через isSolanaError(). Используйте конкретные коды ошибок вместо сопоставления строк:

import {
isSolanaError,
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED,
SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE,
SOLANA_ERROR__TRANSACTION_ERROR__BLOCKHASH_NOT_FOUND,
SOLANA_ERROR__INSTRUCTION_ERROR__INSUFFICIENT_FUNDS
} from "@solana/kit";
function handlePaymentError(error: unknown): {
message: string;
retryable: boolean;
} {
// Blockhash expired—rebuild and retry
if (
isSolanaError(error, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED) ||
isSolanaError(error, SOLANA_ERROR__TRANSACTION_ERROR__BLOCKHASH_NOT_FOUND)
) {
return { message: "Transaction expired—rebuilding", retryable: true };
}
// Insufficient SOL for fees
if (
isSolanaError(
error,
SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE
)
) {
return { message: "Not enough SOL for fees", retryable: false };
}
// Insufficient token balance
if (
isSolanaError(error, SOLANA_ERROR__INSTRUCTION_ERROR__INSUFFICIENT_FUNDS)
) {
return { message: "Insufficient balance", retryable: false };
}
// Unknown error
console.error("Payment error:", error);
return { message: "Payment failed—please retry", retryable: true };
}

Распространённые коды ошибок:

Код ошибкиПричинаРешение
BLOCK_HEIGHT_EXCEEDEDСрок действия blockhash истёкПересоберите с новым blockhash
BLOCKHASH_NOT_FOUNDBlockhash не найденПересоберите с новым blockhash
INSUFFICIENT_FUNDS_FOR_FEEНедостаточно SOLПополните аккаунт плательщика комиссии или используйте абстракцию комиссий
INSUFFICIENT_FUNDSНедостаточно токеновПользователю необходимо пополнить баланс
ACCOUNT_NOT_FOUNDtoken account отсутствуетСоздайте ATA в транзакции

Транзакции без комиссии

Пользователи хотят платить стейблкоинами, а не приобретать SOL для оплаты сетевых комиссий. Транзакции без комиссии решают эту проблему — аналогично тому, как пользователи Venmo не задумываются о комиссиях ACH. См. Абстракция комиссий для полного руководства по реализации.

Безопасность

Управление ключами

  • Никогда не раскрывайте приватные ключи в коде фронтенда. Используйте подписание на бэкенде, аппаратные кошельки, мультиподписные кошельки или сервисы управления ключами.
  • Разделяйте горячие и холодные кошельки. Горячий кошелёк — для операций, холодный — для казначейства.
  • Делайте резервные копии всех производственных ключей. Храните зашифрованные резервные копии в нескольких защищённых местах. Потеря ключа означает безвозвратную потерю доступа.
  • Используйте разные ключи для devnet и mainnet. Ключи для devnet не должны совпадать с ключами для mainnet. Используйте конфигурацию на основе окружения, чтобы гарантировать загрузку правильных ключей для каждой сети.

Инфраструктура подписания

Для подписания на производственном бэкенде используйте Keychain — единую библиотеку подписания, которая абстрагирует множество бэкендов управления ключами через единый интерфейс: Memory, Vault, Privy, Turnkey, AWS KMS, Fireblocks, GCP KMS, CDP, Para, Dfns, Crossmint, Openfort и Utila. Это позволяет вести разработку локально с ключами в памяти, а затем переключаться на производственные бэкенды без изменения кода приложения.

Не знаете, какой бэкенд подходит вам? См. Выбор бэкенда. Keychain доступен как для Rust, так и для TypeScript.

Безопасность RPC

Относитесь к RPC-эндпоинтам как к API-ключам — не раскрывайте их во фронтенд-коде, откуда они могут быть извлечены и использованы не по назначению. Используйте бэкенд-прокси или переменные окружения, которые не встраиваются в клиентский код.

Мониторинг

Отслеживайте эти метрики в продакшене:

МетрикаЗачем
Процент успешных транзакцийРаннее обнаружение проблем
Задержка подтвержденияМониторинг здоровья сети
Расход на приоритетные комиссииУправление затратами
Частота ошибок RPCЗдоровье провайдера

Настройте оповещения для:

  • Переводов выше порога из казначейства
  • Всплесков частоты неудачных транзакций
  • Необычных паттернов получателей
  • Увеличения частоты ошибок RPC

Для мониторинга транзакций в реальном времени в масштабе см. наше руководство по индексированию.

Проверяйте адреса

Каждый токен и программа имеют ровно один корректный адрес в мейннете. Поддельные токены, имитирующие USDC или другие стейблкоины, встречаются часто — у них будет то же название и символ, но другой адрес эмиссии. Ваше приложение должно жёстко задавать или включать в белый список адреса эмиссии (в зависимости от ваших требований), никогда не принимайте их динамически из ненадёжных источников.

Конфигурация в зависимости от окружения: Devnet и Mainnet часто используют совершенно разные минты токенов. Настройте конфигурацию вашего приложения на загрузку правильных адресов для каждого окружения — не используйте жёстко заданные адреса mainnet и не забывайте менять их во время тестирования, или, что ещё хуже, не отправляйте адреса devnet в продакшен.

Некоторые распространённые минты стейблкоинов:

ТокенЭмитентАдрес минта
USDCCircleEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
USDTTetherEs9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
PYUSDPayPal2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo
USDGPaxos2u1tszSeqZ3qBWF3uNGPFc8TzMk2tdiwknnRMWGWjGWH

Адреса программ также имеют значение. Отправка инструкций не в ту программу приведёт к сбою — или, что ещё хуже, к безвозвратной потере средств. Адреса Token Program:

ПрограммаАдрес
Token ProgramTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
Token-2022 ProgramTokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Добавление в список разрешённых правильных адресов защищает от подделки токенов, однако не защищает от отправки на неправильный тип аккаунта. Адрес получателя может быть кошельком, token account, минтом или программой, и каждый случай требует особой обработки. Нативный SOL, отправленный на что угодно, кроме кошелька, выходит из-под контроля отправителя — безвозвратно теряется при отправке на минт или программу, а при отправке на token account может быть возвращён только владельцем аккаунта — при этом транзакция не завершается ошибкой, которая предупредила бы пользователя.

Проверяйте получателей перед отправкой

Классифицируйте каждый адрес получателя перед подписанием перевода. См. Проверка адреса — полное дерево решений и справочный код для разграничения кошельков, token account, минтов и программ.

Контрольный список перед запуском

  • Получен SOL в основной сети для оплаты комиссий и rent
  • Настроен RPC для продакшена (не публичный эндпоинт)
  • Настроен резервный RPC-эндпоинт
  • Реализованы приоритетные комиссии с динамическим ценообразованием
  • Логика повторных попыток обрабатывает истечение срока действия blockhash
  • Уровень подтверждения соответствует сценарию использования
  • Все распространённые ошибки обрабатываются корректно
  • Настроен Gasless (если применимо)
  • Адреса токенов в основной сети проверены (не devnet-минты)
  • Валидация адреса получателя перед отправкой (кошелёк vs token account vs минт vs программа)
  • Все ключи надёжно зарезервированы
  • Управление ключами проверено (ключи не хранятся во фронтенде)
  • Мониторинг транзакций и оповещения активны
  • Проведено нагрузочное тестирование при ожидаемом объёме

Развёртывание программ

Если в рамках вашей платёжной инфраструктуры вы развёртываете собственную программу Solana, необходимо учесть ряд дополнительных аспектов.

Перед развёртыванием

  • Версия Solana CLI: Убедитесь, что используете последнюю версию Solana CLI.
  • keypair программы: Ваша программа будет иметь другой адрес в mainnet, отличный от devnet (если вы не используете тот же keypair). Обновите все ссылки в конфигурации вашего приложения. Храните keypair программы в надёжном месте (обратите внимание, что выполнение cargo clean, скорее всего, удалит keypair вашей программы).
  • Инициализация аккаунтов: Если ваша программа требует аккаунтов администратора, PDA или других аккаунтов состояния, убедитесь, что они созданы в mainnet до того, как пользователи начнут взаимодействовать с вашим приложением. То же самое касается любых Associated Token Accounts (ATAs), необходимых вашей программе.

Процесс развёртывания

  • Буферные аккаунты: Большие программы развёртываются через буферные аккаунты. Команда solana program deploy обрабатывает это автоматически, однако следует учитывать, что развёртывание не является атомарным — при прерывании может потребоваться восстановление или закрытие буферных аккаунтов. См. Deploying Programs.
  • Права на обновление: Решите, должна ли ваша программа поддерживать обновление после запуска. Для обеспечения неизменности отзовите права на обновление после развёртывания. Для гибкости надёжно защитите ключ прав на обновление.
  • Рента: Убедитесь, что в вашем кошельке для развёртывания достаточно SOL для покрытия минимальных требований освобождения от ренты для всех program accounts.
  • Верификация: Верифицируйте вашу программу, чтобы убедиться, что исполняемая программа, развёртываемая в сети Solana, соответствует исходному коду в вашем репозитории

Полное руководство по развёртыванию программ см. в Deploying Programs.

Is this page helpful?