Skip to main content
Если вы разрабатываете приложения, которые должны реагировать на изменения ончейн, опрос конечных точек RPC для получения обновлений аккаунтов будет неэффективным и медленным. Подписки на аккаунты решают эту проблему, передавая обновления об изменениях состояния аккаунтов в реальном времени непосредственно вашему приложению. В этом руководстве рассматривается всё, что нужно знать о подписках на аккаунты: что это такое, как они работают и как оптимизировать их для вашего конкретного сценария использования.

Контекст модели аккаунтов

Пропустите этот раздел, если вы знакомы с аккаунтами Solana и их структурой.
Solana использует модель на основе аккаунтов, в которой каждый фрагмент данных хранится в аккаунте — контейнере, содержащем данные и метаданные. У каждого аккаунта есть:
  • Данные: фактические байты, в которых хранится состояние программы, балансы токенов или другая информация
  • Владелец: программа, которая управляет этим аккаунтом и может изменять его данные
  • Лампорты: баланс SOL аккаунта для освобождения от арендной платы
  • Исполняемость: содержит ли этот аккаунт программный код
Программы не имеют состояния — они не хранят данные внутри себя. Вместо этого они создают отдельные аккаунты и управляют ими для хранения своего состояния. При взаимодействии с программой вы передаёте ей аккаунты, из которых она должна читать или в которые должна записывать данные. Благодаря такой архитектуре подписки на аккаунты особенно эффективны: вы можете отслеживать изменения конкретных аккаунтов, всех аккаунтов, принадлежащих программе, или аккаунтов, соответствующих определённым критериям.

Базовая подписка на аккаунты

Начнём с простого примера подписки на изменения в аккаунтах токенов. Этот скрипт будет уведомлять вас при каждом изменении баланса токенов:
Запустив эту базовую подписку, вы увидите в консоли поток обновлений аккаунтов в реальном времени:
Что только что произошло? Наша подписка сработала идеально! Мы попросили Laserstream уведомлять нас об изменениях аккаунтов токенов, и сервис передал обновление об аккаунте BKMHWYLAX4un3HUbR7a3u9jPmzCiLNa4mSj1RiX11eWF. У этого аккаунта есть:
  • 2 039 280 лампортов (баланс около 0,002 SOL — это сумма, необходимая для освобождения этого аккаунта токена от арендной платы)
  • Программа-владелец TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA (это программа SPL Token)
  • Подпись транзакции 5C9Hr5nG2j8eQz6inxPmfyjbYdmXddzUDyR1iQgEnjYQ3RNvuP4Zzc8t1enLNy7Rk8KNCtQPEQztENYWxkt9GaVD, указывающая, какая именно транзакция вызвала изменение этого аккаунта
  • Слот 352366983, указывающий, когда это обновление произошло в блокчейне
  • Поле данных, содержащее 165 байт данных аккаунта в кодировке base58

Принцип фильтрации аккаунтов с помощью datasize

Поле данных имеет ключевое значение: оно содержит фактическую структуру аккаунта токена. Используем это понимание для интеллектуальной фильтрации аккаунтов.

Зачем использовать фильтрацию по datasize?

Чтобы понять, зачем нужна фильтрация, сначала разберёмся, что представляют собой аккаунты токенов. Для каждого токена в кошельке существует отдельный аккаунт ончейн. Если в вашем кошельке хранятся 3 разных токена (USDC, BONK и SOL), у вас фактически есть 1 аккаунт кошелька (основной аккаунт SOL) и 3 аккаунта токенов (по одному для каждого типа токена). Размер каждого аккаунта токена составляет ровно 165 байт. В нём хранится информация о том, какой токен он содержит (адрес минта), кому он принадлежит (адрес вашего кошелька) и сколько в нём токенов (количество). Программе Token Program принадлежат миллионы аккаунтов в Solana, но не все они являются «аккаунтами токенов», в которых хранятся балансы пользователей. Вот что происходит с фильтрацией и без неё: Без фильтрации — поток данных:
Этот код подписывается на ВСЕ аккаунты, принадлежащие Token Program, включая:
  • Аккаунты токенов (165 байт) — пользовательские балансы: миллионы аккаунтов
  • Аккаунты минтов (82 байта) — определения токенов: сотни тысяч аккаунтов
  • Аккаунты с мультиподписью (355 байт) — управление общими кошельками: десятки тысяч аккаунтов
  • Аккаунты Associated Token Program (различные размеры) — миллионы аккаунтов
Результат: ваше приложение постоянно получает миллионы обновлений аккаунтов, большинство из которых вам не нужны.
С интеллектуальной фильтрацией — хирургическая точность:
Этот фильтр оставляет только аккаунты размером 165 байт, то есть именно аккаунты балансов пользовательских токенов — то, что нужно для отслеживания переводов токенов, изменений баланса и обновлений портфеля. Разница:
  • Без фильтрации: миллионы обновлений аккаунтов (создание минтов, изменения мультиподписей и т. д.)
  • С фильтрацией по datasize: только изменения балансов токенов
Это значительно снижает объём лишних данных и позволяет сосредоточиться только на аккаунтах, которые действительно представляют пользовательские токены.

Откуда берутся 165 байт?

В этом нет никакой магии: это следует из структуры аккаунта программы SPL Token. В исходном коде видно, что структура Account определяет ровно 165 байт:
Этот фиксированный размер позволяет точно фильтровать стандартные аккаунты токенов и исключать:
  • Аккаунты минтов (82 байта)
  • Аккаунты с мультиподписью (355 байт)
  • Аккаунты программы связанных аккаунтов токенов
  • Другие связанные с токенами аккаунты иного размера
Чтобы рассчитывать размеры аккаунтов в других программах, ознакомьтесь со справочником Anchor по пространству. В нём указано, сколько места занимают разные типы данных (Pubkey = 32 байта, u64 = 8 байт и т. д.).

Декодирование структуры аккаунта

Теперь, когда мы понимаем, почему установили фильтр на 165 байт, декодируем содержимое аккаунта из нашего примера:
165 байт распределяются следующим образом:
  • Байты 0–31: адрес минта (какой токен хранится в этом аккаунте)
  • Байты 32–63: адрес владельца (кому принадлежит этот аккаунт токена)
  • Байты 64–71: количество токенов (сколько токенов находится в аккаунте)
  • Байты 72–164: дополнительные метаданные (делегат, состояние, право закрытия и т. д.)
Этот структурированный подход обеспечивает хирургическую точность: мы получаем обновления только для стандартных аккаунтов токенов, без лишних данных от аккаунтов других типов.

Объединение фильтров: datasize + memcmp для максимальной точности

Теперь, когда мы знаем, что адрес минта находится в байтах 0–31, можно ещё больше сузить выборку. Допустим, мы хотим отслеживать только аккаунты токена USDC. Можно объединить фильтр datasize с фильтром memcmp, чтобы выбрать точный адрес минта:
Стратегия последовательной фильтрации:
  1. Фильтр по владельцу: «Покажи аккаунты, принадлежащие Token Program» (миллионы аккаунтов)
  2. Фильтр по datasize: «Но только стандартные аккаунты токенов размером 165 байт» (сотни тысяч)
  3. Фильтр по memcmp: «И только те, в которых хранится USDC» (тысячи)
Такой переход от общих критериев к конкретным — ключ к эффективному мониторингу аккаунтов. Каждый фильтр сужает набор результатов, поэтому вы получаете только те обновления, которые вам нужны. Важно: все фильтры используют логику AND — чтобы обновление аккаунта было передано, должны выполняться все условия.

Чтение обновлений аккаунтов USDC: кто, сколько и где?

Теперь посмотрим, что именно содержат эти отфильтрованные обновления. Создадим монитор специально для USDC, который при изменении аккаунта токена отвечает на ключевые вопросы:
  • Кому принадлежит этот аккаунт токена?
  • Сколько USDC теперь в нём содержится?
  • Где произошло изменение (какой именно аккаунт изменился)?
  • Когда произошло это изменение?
  • Какая транзакция вызвала изменение?
Необработанные обновления аккаунтов содержат двоичные данные, которые нужно декодировать. Поскольку Solana использует кодировку base58 для адресов и подписей, мы применяем функцию bs58.encode(), чтобы преобразовать двоичные объекты Buffer в читаемые строки.
Запустив этот монитор USDC, вы увидите понятный структурированный вывод следующего вида:
Каждый блок представляет аккаунт USDC, состояние которого изменилось. В первом аккаунте теперь хранится 1 500 USDC, а второй аккаунт был опустошён до 0 USDC. Сразу после каждой транзакции вы получаете текущий баланс, а также сведения о том, какой именно аккаунт изменился и когда это произошло. Подписки на аккаунты показывают конечный результат изменений каждого аккаунта, а не сведения о транзакции. Если вам нужен полный контекст транзакции (кто кому отправил средства, комиссии и т. д.), необходимо получить полную транзакцию по указанной подписи.

Полный справочник по фильтрации

Помимо уже использованных базовых фильтров owner, datasize и memcmp, подписки на аккаунты поддерживают дополнительные параметры фильтрации, позволяющие ещё больше сузить результаты:

Фильтрация конкретных аккаунтов

Отслеживайте конкретные аккаунты по их открытым ключам:
Этот подход хорошо подходит, если вы точно знаете, какие аккаунты важны для вашего приложения, например казначейские аккаунты приложения или конкретные пользовательские аккаунты. Для очень больших наборов аккаунтов явные списки pubkey становятся затратными: по 32 байта на каждый аккаунт в запросе подписки. Если аккаунтов больше ~10 000, используйте сжатый фильтр кукушки (~3–4 байта на аккаунт), чтобы отслеживать сотни тысяч аккаунтов в одном потоке. Доступно в SDK для Rust и JavaScript.

Комбинированные стратегии фильтрации

Главное преимущество заключается в объединении нескольких типов фильтров. Вот удобная концептуальная модель:
  1. Охватите широкую область с помощью owner — «Покажи все аккаунты, которыми управляет эта программа»
  2. Отфильтруйте по структуре с помощью datasize — «Но только аккаунты этого конкретного типа»
  3. Выберите конкретные данные с помощью memcmp — «И только те, которые содержат эту конкретную информацию»
  4. Отслеживайте известные аккаунты с помощью account — «Или отслеживай только интересующие меня конкретные аккаунты»
Например, так можно отслеживать аккаунты USDC с крупными балансами:
Главное заключается в том, что каждый фильтр уменьшает объём получаемых обновлений. Без фильтрации вы можете получать чрезмерное количество обновлений аккаунтов. Благодаря интеллектуальной фильтрации вы получаете только обновления, важные для вашего конкретного сценария использования.

Общая картина

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

Применение этого шаблона к другим программам

Рассмотренный подход применим к любой программе Solana. Вот общий шаблон:
  1. Изучите структуру аккаунта — проверьте исходный код или документацию программы
  2. Начните с фильтрации по владельцу — выберите программу, которая управляет аккаунтами
  3. Примените структурные фильтры — используйте размер аккаунта, шаблоны данных или другие характеристики, чтобы выбрать аккаунты конкретных типов
  4. Добавьте целевые фильтры — сосредоточьтесь на конкретных аккаунтах, состояниях или значениях данных, важных для вашего приложения