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

Локальная разработка и тестирование в 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 миллионов вычислительных единиц).

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

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

import {
estimateComputeUnitLimitFactory,
updateOrAppendSetComputeUnitLimitInstruction
} from "@solana-program/compute-budget";
const estimateComputeUnitLimit = estimateComputeUnitLimitFactory({ rpc });
const computeUnitLimit = await estimateComputeUnitLimit(tx);
const txWithComputeUnitLimit = updateOrAppendSetComputeUnitLimitInstruction(
computeUnitLimit,
tx
);

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

Jito Bundles

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

Ресурсы:

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

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

Ключевые понятия:

  • Истечение срока действия blockhash: Транзакции действительны примерно 150 блоков (60–90 секунд)
  • Идемпотентность: Одна и та же подписанная транзакция всегда даёт одну и ту же подпись — повторная отправка безопасна
  • Экспоненциальная задержка (backoff): Не перегружайте сеть частыми повторными попытками
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Пополните аккаунт плательщика комиссии или используйте fee abstraction
INSUFFICIENT_FUNDSНедостаточно токеновПользователю нужно больше баланса
ACCOUNT_NOT_FOUNDОтсутствует token accountСоздайте ATA в транзакции

Транзакции без газа

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

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

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

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

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

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

Мониторинг

В продакшене отслеживайте следующие метрики:

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

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

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

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

Проверка адресов

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

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

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

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

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

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

Чек-лист перед запуском

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

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

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

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

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

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

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

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

Is this page helpful?

Управляется

© 2026 Фонд Solana.
Все права защищены.
Подключиться