Как стать автором
Обновить

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

Уровень сложностиПростой
Время на прочтение7 мин
Количество просмотров413

Для любой технической задачи на 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

Теги:
Хабы:
0
Комментарии0

Публикации

Истории

Работа

Ближайшие события

2 – 18 декабря
Yandex DataLens Festival 2024
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань