Локальная разработка и тестирование в devnet — отличные способы начать работу с платежами в Solana. Однако при переходе к развертыванию в Mainnet важно учитывать его особенности. Devnet прощает ошибки. Mainnet — нет. В этом руководстве рассматриваются ключевые различия, чтобы ваши пользователи получили максимально плавный опыт.
| Devnet | Mainnet |
|---|---|
| Бесплатные 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 serverconst 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 themlet 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:
- Получайте с помощью
confirmedcommitment - Сохраняйте
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 commitmentconst { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: "confirmed" }).send();// 2. Build and sign your transaction with the blockhash// ... (transaction building code)// 3. Send with production settingsconst signature = await rpc.sendTransaction(encodedTransaction, {encoding: "base64",maxRetries: 0n, // Handle retries yourselfskipPreflight: true, // Skip simulation for speed (use false during dev)preflightCommitment: "confirmed"}).send();// 4. Track expiration using lastValidBlockHeightconst { lastValidBlockHeight } = latestBlockhash;// Stop retrying when current block height exceeds lastValidBlockHeight
Используйте приоритетные комиссии
Каждая транзакция в Solana требует уплаты комиссии, которая оплачивается в SOL. Комиссия за транзакцию состоит из двух частей: базовой комиссии и приоритетной комиссии. Базовая комиссия вознаграждает валидаторов за обработку транзакции. Приоритетная комиссия — это необязательный платеж, который увеличивает вероятность того, что текущий лидер обработает вашу транзакцию. Представьте это как экспресс-доставку: вы платите больше за более быструю и надёжную доставку.
Как работают комиссии:
Total fee = Base fee (5,000 lamports per signature) + Priority feePriority 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 monitoringtry {await sendAndConfirmTransaction(signedTransaction, {commitment: "confirmed",// Optional: abort after 75 secondsabortSignal: AbortSignal.timeout(75_000)});} catch (e) {if (isSolanaError(e, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED)) {// Blockhash expired—rebuild transaction with fresh blockhash and retryrebuildAndRetryTransaction(); // implement your own logic for rebuilding and retrying the transaction}throw e;}
sendAndConfirmTransactionFactory из @solana/kit автоматически обрабатывает
опрос подтверждений и отслеживание высоты блока. Он выбрасывает
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, когда срок действия blockhash транзакции
истекает, сигнализируя о необходимости пересобрать транзакцию с новым blockhash.
Дополнительные ресурсы
- Руководство: подтверждение и истечение срока действия транзакций
- Helius: как провести транзакции в Solana
- QuickNode: стратегии оптимизации транзакций Solana
Уровни подтверждения транзакций
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 retryif (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 feesif (isSolanaError(error,SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE)) {return { message: "Not enough SOL for fees", retryable: false };}// Insufficient token balanceif (isSolanaError(error, SOLANA_ERROR__INSTRUCTION_ERROR__INSUFFICIENT_FUNDS)) {return { message: "Insufficient balance", retryable: false };}// Unknown errorconsole.error("Payment error:", error);return { message: "Payment failed—please retry", retryable: true };}
Распространённые коды ошибок:
| Код ошибки | Причина | Решение |
|---|---|---|
BLOCK_HEIGHT_EXCEEDED | Срок действия blockhash истёк | Пересоберите с новым blockhash |
BLOCKHASH_NOT_FOUND | Blockhash не найден | Пересоберите с новым blockhash |
INSUFFICIENT_FUNDS_FOR_FEE | Недостаточно SOL | Пополните аккаунт плательщика комиссии или используйте абстракцию комиссий |
INSUFFICIENT_FUNDS | Недостаточно токенов | Пользователю необходимо пополнить баланс |
ACCOUNT_NOT_FOUND | token 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-ключам — не раскрывайте их во фронтенд-коде, откуда они могут быть извлечены и использованы не по назначению. Используйте бэкенд-прокси или переменные окружения, которые не встраиваются в клиентский код.
- QuickNode: Лучшие практики безопасности эндпоинтов
- Helius: Защитите свои API-ключи Solana: лучшие практики безопасности
Мониторинг
Отслеживайте эти метрики в продакшене:
| Метрика | Зачем |
|---|---|
| Процент успешных транзакций | Раннее обнаружение проблем |
| Задержка подтверждения | Мониторинг здоровья сети |
| Расход на приоритетные комиссии | Управление затратами |
| Частота ошибок RPC | Здоровье провайдера |
Настройте оповещения для:
- Переводов выше порога из казначейства
- Всплесков частоты неудачных транзакций
- Необычных паттернов получателей
- Увеличения частоты ошибок RPC
Для мониторинга транзакций в реальном времени в масштабе см. наше руководство по индексированию.
Проверяйте адреса
Каждый токен и программа имеют ровно один корректный адрес в мейннете. Поддельные токены, имитирующие USDC или другие стейблкоины, встречаются часто — у них будет то же название и символ, но другой адрес эмиссии. Ваше приложение должно жёстко задавать или включать в белый список адреса эмиссии (в зависимости от ваших требований), никогда не принимайте их динамически из ненадёжных источников.
Конфигурация в зависимости от окружения: Devnet и Mainnet часто используют совершенно разные минты токенов. Настройте конфигурацию вашего приложения на загрузку правильных адресов для каждого окружения — не используйте жёстко заданные адреса mainnet и не забывайте менять их во время тестирования, или, что ещё хуже, не отправляйте адреса devnet в продакшен.
Некоторые распространённые минты стейблкоинов:
| Токен | Эмитент | Адрес минта |
|---|---|---|
| USDC | Circle | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| USDT | Tether | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB |
| PYUSD | PayPal | 2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo |
| USDG | Paxos | 2u1tszSeqZ3qBWF3uNGPFc8TzMk2tdiwknnRMWGWjGWH |
Адреса программ также имеют значение. Отправка инструкций не в ту программу приведёт к сбою — или, что ещё хуже, к безвозвратной потере средств. Адреса Token Program:
| Программа | Адрес |
|---|---|
| Token Program | TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA |
| Token-2022 Program | TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb |
Добавление в список разрешённых правильных адресов защищает от подделки токенов, однако не защищает от отправки на неправильный тип аккаунта. Адрес получателя может быть кошельком, 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?