Раскройте возможности блокчейна TON: Пошаговое руководство по сбору данных с помощью dton.io

от автора

Для любой технической задачи на TON, вам необходимо использовать индексаторы. Индексаторы это сервисы, которые агрегируют внутри себя транзакции блокчейна, обогащают данные и позволяют получить эти данные в необходимом виде. 

Без использования таких сервисов, для каждого запроса информации, вам бы пришлось парсить кучу блоков блокчейна, чтобы вернуть данные. В данной статье, я покажу вам как делать GraphQL запросы в dton.io на блокчейне TON. Возьмем простую задачу и пройдем весь путь формирования запроса и параллельно рассмотрим основные возможности индексатора.

Что такое dton.io?

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

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

Большим плюсом dton.io, является обогащение данных, помимо нативных данных транзакций и блоков, dton.io собирает информацию из регистра данных смарт-контрактов, а также обогащает данными популярные стандарты токенов на TON, такие как NFT — стандарт не взаимозаменяемых токенов и Jetton — стандарт взаимозаменяемых токенов.

Обогащение данных позволяет уменьшить количество необходимых запросов.

Как происходит процесс формирования запроса

Верхнеуровневый алгоритм всегда следующий:

  1. понять как задача выглядит на уровне смарт-контрактов

  2. иттеративный сбор запроса + борьба с возникающими проблемами(вроде таймаута на очень крупных запросах), валидация правильности данных

В силу специфики архитектуры смарт-контрактов на TON, первая часть зачастую является самой сложной — одной логической операции, например поставка ликвидности в пул, может участвовать несколько контрактов и конечно же искать, где там среди 5 контрактов передается нужная вам информация получиться, только погружаясь в смарт-контракты.

Кстати запросы, которые мы будем рассматривать ниже, вы можете запутить тут — https://dton.io/graphql/

Формулируем задачу

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

Предпосылкой такого запроса может быть, желание найти, кому продать свою нфт. Но в данном туториале это просто удобная задача, через которую можно показать большую часть функционала dton.io.

Что это значит с точки зрения смарт-контрактов

Прежде чем прыгать с головой в поля доступные в индексаторе и думать, как нам агрегировать данные по сумме и количеству сделок, давайте поймем как вообще происходят продажи NFT в TON.

Почти любая сделка подразумевает некий контракт продажи. на который “перемещают NFT”, данный контракт реализует логику продажи, это может быть как просто продажа любому желающему заплатить сумму, так и сложная логика в виде аукциона. 

Соответственно с точки зрения данных продажей можно назвать транзакцию в которой меняется собственник NFT. Посмотреть примеры разных контрактов продаж можно тут — https://github.com/ton-blockchain/token-contract/blob/main/nft/nft-sale.fc. Это простейший контракт продажи написанный на языке FUNC.

Теперь попробуем сделать первый запрос.

Достанем последние транзакции с продажами nft

Итак, нас интересуют транзакции с передачей права собственности NFT, значит будем использовать представление транзакций:

      {         raw_transactions(         ) {         }       }

Теперь попробуем наполнить запрос, посмотреть все доступные поля можно в документации https://docs.dton.io/transactions-and-account-states .

Возьмем для примера коллекцию NFT Telegram Username, а также уже законченные сделки:

      {         raw_transactions(           parsed_seller_is_closed: 1           parsed_nft_collection_address_address: "80D78A35F955A14B679FAA887FF4CD5BFC0F43B4A4EEA2A7E6927F3701B273C2"         ) {           nft_address: address           col_address: parsed_nft_collection_address_address         }       }

Поля начинающиеся с parsed это обогащенные, а не нативные поля. Теперь, нам важно убрать транзакции в которые происходят не с NFT, для этого добавим поле  account_state_state_init_code_has_get_nft_data: 1 . Здесь может возникнуть вопрос что за get_nft_data, самым простым способ отличать разные типы смарт-контрактов, является их сигнатура, смарт-контракты определенных стандартов, должны содержать определенные функции и вызовы. Стандарт NFT в TON предполагает наличие GET метода get_nft_data.

      {         raw_transactions(           parsed_seller_is_closed: 1           account_state_state_init_code_has_get_nft_data: 1           workchain: 0           page: 0           page_size: 10           parsed_nft_collection_address_address: "80D78A35F955A14B679FAA887FF4CD5BFC0F43B4A4EEA2A7E6927F3701B273C2"         ) {           prev_owner: parsed_seller_nft_prev_owner_address_address           new_owner: parsed_seller_nft_new_owner_address_address           nft_address: address           col_address: parsed_nft_collection_address_address           price: parsed_seller_nft_price         }       } 

Также сразу добавим parsed значения цены  и предыдущего и нового владельца. Предлагаю выполнить запрос.

Проблема таймаут

После выполнения запроса мы увидим:

{   "data": {},   "errors": [     "Timeout exceeded, simplify your query (https://docs.dton.io/query-speed-optimization-timeout)"   ] }

Проблема заключается в том, что наш запрос, отрабатывает по всей базе индексатора. Возможные варианты решения проблемы описнны тут https://docs.dton.io/query-speed-optimization-timeout. Самым простым вариантом явлется добавления фильтра по времени. 

Для этого нам понадобятся фильтры.

Фильтруем запрос по времени

Фильтры в dton.io на поля можно добавлть с помощью нижнего подчеркивания. Фильтр на время генерации один из самых распространенных — он позволяет “не бегать” запросом по всей базе.

Сузим нашу задачу и будем искать только за последний месяц:

      {         raw_transactions(           parsed_seller_is_closed: 1           account_state_state_init_code_has_get_nft_data: 1           workchain: 0           page: 0           page_size: 10           gen_utime__gt: "2024-12-04 00:00:00"           parsed_nft_collection_address_address: "80D78A35F955A14B679FAA887FF4CD5BFC0F43B4A4EEA2A7E6927F3701B273C2"         ) {           prev_owner: parsed_seller_nft_prev_owner_address_address           new_owner: parsed_seller_nft_new_owner_address_address           nft_address: address           col_address: parsed_nft_collection_address_address           price: parsed_seller_nft_price         }       } 

Список доступных фильтров здесь — https://docs.dton.io/filters

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

Попробуем запустить запрос, получим примерно следующее:

{   "data": {     "raw_transactions": [       {         "prev_owner": "CF01D60CC8924D3C34C54A5787B9175BCD8D45D9ADA91371F93318DF2B76EBDB",         "new_owner": "69B8D2CA45C14F056FED73236A6A0CB7AB5BA2B1A0F2E1AD784D84F7B4454D81",         "nft_address": "8C08EBCDDEDB79E6FF3AA1B4F0A65388D16FE2F1D86BFBA46E25833A24A659F4",         "col_address": "80D78A35F955A14B679FAA887FF4CD5BFC0F43B4A4EEA2A7E6927F3701B273C2",         "price": 4746195291       },       {         "prev_owner": "50FC81C8CFEA8780CEF8B636D3643ED9F7E5ADD3D4E8045510FD19AF2EAC337D",         "new_owner": "0E4A5829EEBC85FB049DD63B5CCF801D3DC411780ABEC22F8F8918B66B852337",         "nft_address": "E20696CD521BF88D729E06827587D2908E0ED94ED5910A79D26069C4F9F261AB",         "col_address": "80D78A35F955A14B679FAA887FF4CD5BFC0F43B4A4EEA2A7E6927F3701B273C2",         "price": 8479134769       } …   ]   },   "errors": [] } 

Убираем отмывочную торговлю

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

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

      {         raw_transactions(           parsed_seller_is_closed: 1           account_state_state_init_code_has_get_nft_data: 1           workchain: 0           page: 0           page_size: 10           gen_utime__gt: "2024-12-04 00:00:00"           not__: {parsed_seller_nft_prev_owner_address_address: new_owner}           parsed_nft_collection_address_address: "80D78A35F955A14B679FAA887FF4CD5BFC0F43B4A4EEA2A7E6927F3701B273C2"         ) {           prev_owner: parsed_seller_nft_prev_owner_address_address           new_owner: parsed_seller_nft_new_owner_address_address           nft_address: address           col_address: parsed_nft_collection_address_address           price: parsed_seller_nft_price         }       } 

Удобные фичи — преобразуем адреса

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

В нашем случае получиться примерно вот так:

      {         raw_transactions(           parsed_seller_is_closed: 1           account_state_state_init_code_has_get_nft_data: 1           workchain: 0           page: 0           page_size: 10           gen_utime__gt: "2024-12-04 00:00:00"           not__: {parsed_seller_nft_prev_owner_address_address: new_owner}           order_by: "parsed_seller_nft_price",           order_desc: true,         parsed_nft_collection_address_address: "80D78A35F955A14B679FAA887FF4CD5BFC0F43B4A4EEA2A7E6927F3701B273C2"         ) {           prev_owner: parsed_seller_nft_prev_owner_address_address__friendly           new_owner: parsed_seller_nft_new_owner_address_address__friendly           nft_address: address__friendly           col_address: parsed_nft_collection_address_address__friendly           price: parsed_seller_nft_price         }       } 

Если вам интересно почитать про разные представления адрессов, в TON есть отдельный стандарт :https://github.com/ton-blockchain/TEPs/blob/master/text/0002-address.md

Агрегация данных

После того как мы собрали простой запрос, посмотрели, что в ответе на запрос мы получаем те транзакции, которые ожидали, можно перейти к сборке агрегированного запроса.

Для агрегации можно использовать endpoint’ы groupTransactions и groupAccountStates для выполнения операций агрегации. Эти endpoint’ы поддерживают различные функции агрегации и позволяют группировать, сортировать и фильтровать результаты.

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

Поля, по которым мы фильтровали запрос кладем в filter. остальные же поля заполняем в соответствии с логикой агрегации:

{   groupTransactions(     by: ["parsed_seller_nft_new_owner_address_address__friendly"],     aggregations: [       {field: "parsed_seller_nft_price", operation: "sum"},       {operation: "count"}     ],     order_by: "parsed_seller_nft_price__sum",     order_desc: true,     page: 0,     page_size: 10,     filter_by: {or__: [       {not__: {         parsed_seller_nft_prev_owner_address_address: parsed_seller_nft_new_owner_address_address       }},       {and__: {           parsed_seller_is_closed: 1           account_state_state_init_code_has_get_nft_data: 1           workchain: 0           gen_utime__gt: "2024-12-04 00:00:00"           parsed_nft_collection_address_address: "80D78A35F955A14B679FAA887FF4CD5BFC0F43B4A4EEA2A7E6927F3701B273C2"       }}]}   ) {     trader_new_address:parsed_seller_nft_new_owner_address_address__friendly     deal_count: count     deal_sum: parsed_seller_nft_price__sum   } } 

С помощью by агрегируем по адресу нового владельца, в остальных полях прописываем логику подсчета по цене. Для репрезентативности посчитаем и сумму и количество сделок. Подробнее об агрегации здесь — https://docs.dton.io/aggregations.

Отмечу, что для выполения крупных агрегаций, необходим платный план dton.io

Заключение

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

https://github.com/romanovichim/TonFunClessons_ru

Новые туториалы и дата аналитику я кидаю сюда: https://t.me/ton_learn


ссылка на оригинал статьи https://habr.com/ru/articles/866744/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *