Что может быть пустым
Все описанные поля присутствуют всегда — форма события не меняется вместе с данными. Меняется другое: несёт ли поле значение. Несколько таких случаев стоит знать заранее, до того как вы напишете под них код.
Как выглядит пустота
| Тип поля | Когда значения нет |
|---|---|
Адрес (pool, lpMint, counterparty, концы трансфера) | null |
Список (fees, transfers, liquidity, tokens, route.hops) | [] |
Сумма или объект (priorityFee, creatorBuy) | null |
reserves | null, и это значит кое-что — см. ниже |
Одна проверка на поле, и всегда одна и та же. Единственное исключение — reserves: там null это утверждение, а не отсутствие. Он говорит, что транзакция не позволила достоверно определить глубину пула в этот момент, а не что пул пуст.
Поля, которых часто нет
pool отдают не все площадки. Если сделку нельзя привязать к аккаунту пула, поле приходит как null, а остальное событие от этого не страдает.
valueUsd требует, чтобы одна нога была в SOL или в стейблкоине. У свопа токен-к-токену такой ноги нет, поэтому поле null — а пороговые фильтры такие события пропускают, вместо того чтобы молча скрыть целый класс сделок. Это сделано намеренно, см. minUsdAmount.
blockTime равен null, пока сеть не сообщила время блока. Мы отдаём событие сразу, а не держим его в ожидании.
from и to в переводе токена: у минта нет источника, у burn нет назначения — для этих двух видов один конец приходит как null. То же касается counterparty в переводах кошелька.
Площадки без имени
В protocol приходит слаг площадки, на которой прошла сделка. Если программа, через которую она прошла, не входит в наш реестр, событие всё равно приходит целиком — тип сделки, кошелёк, обе ноги, суммы — и неизвестным остаётся только имя площадки: protocol: "unknown". За этим обычно стоят самописные программы, которые участники рынка используют под себя, и площадки с совсем небольшим объёмом.
Это стоит учесть в двух местах. Не проверяйте protocol по списку известных значений — набор открытый и пополняется. И фильтр platforms такие сделки отсекает: если по токену нужен весь поток, фильтр лучше не задавать.
Списки трансферов отфильтрованы, а не сырые
transfers[] у сделки — это выжимка: движения, которые стоят сами по себе, вроде чаевых, выплат и минта. Строки, которые лишь повторяют то, что событие уже несёт — ноги самого свопа, комиссии, — отброшены, а это большая часть леджера транзакции.
Поэтому transfers[] намеренно не сходится с транзакцией, которую вы увидите в эксплорере. Когда нужен полный леджер, для этого есть includeRaw.
Две детали про сами строки:
fromиto— это кошельки-владельцы, а не токен-аккаунты. У нативного SOL токен-аккаунта нет вообще, там кошелёк — единственный адрес.memoбывает только внутри этих строк и никогда на самом событии. Memo принадлежит строке леджера, и привязать его к действию, которое описывает событие, нечем — а угадывать мы не станем.
Суммы
Любая сумма приходит парой строк, и обе — строки не случайно:
{ "mint": "So111...", "amountRaw": "14841398823", "amount": "14.841398823" }amountRaw — целое в минимальных единицах токена, amount — с учётом decimals. Держите их строками или разбирайте десятичным типом произвольной точности: double не представляет точно любую сырую сумму токена.