Ищем мемкойны на Stonfi в TON или есть ли жизнь за Notcoin

Мемкойны на пару с нфт одни из моих самых нелюбимых трендов крипты, никакой технологичности, один маркетинг. Но взрывной рост в Q1 2024, привлекает внимание к нарративу мемкойнов. (По версии coingecko это самый прибыльный нарратив Q1 2024, но есть вопросы к методологии). Поэтому предлагаю попробовать, найти мемкойны на TON, попробовав собрать простенький дэшборд мемкойнов…

В статье я хочу разобрать, почему аналитические платформы мем койнов выглядят как live лента покупок этих самых мемкойнов. Пробежаться по новым API Stonfi, данные этой DEX теперь отображаются на Dexscreener, и под это появились отдельные открытые API, что сильно влияет на доступность данных (а это проблема в TON) И третье, посмотреть некоторые  свои статистические гипотезы относительно пулов. 

Что будем искать?

Если погуглить определение мемкойнов, то определения будут разница от пирамид нашего времени, до цифровой мем валюты, нового слова в сфере social tech.Поэтому в рамках этой статьи я предлагаю свое определение: Мемкойн это динамично растущий токен без изначального утилити, использующий механизмы Fomo для обеспечения роста. Данное определение дает мне возможность сформулировать гипотезу: Мемкойны это быстрорастущие малые проекты, а значит можно сначала отрезать крупные проекты по объему их пулов (Total Value Locked), а далее рассмотреть маленькие проекты по количеству свапов за последний, например, день.

Строим диаграмму TVL для пулов Stonfi — ищем порог за которым мемкойны

Начнем с TVL. У Stonfi есть две  API v1/pools — возвращающий текущее состояние пулов и v1/stats/pools — статистика по пулам за период. Воспользуемся v1/pools, нам нужен адрес пула, адреса токенов из которых состоит пул и lp_total_supply_usd — общее предложение lp токенов пула. Lp токены это автоматически генерируемые DEX и начисляемые провайдеру ликвидности за внесения активов в пул ликвидности. Эти токены представляют собой долю комиссий, заработанных пулом ликвидности. Соответственно их предложение в долларовом эквиваленте будет отражать TVL.

import requests
r = requests.get('https://api.ston.fi/v1/pools')
result = r.json()['pool_list']
# упомянуть что есть еще но грузит там долго


# возьмем нужные нам поля #замечу tvl храниться строкой - надо  менят ь
temp_pool_list = [{'pool_address': pool['address'], 'token0_address': pool['token0_address'], 'token1_address': pool['token1_address'], 'tvl': round(float(pool['lp_total_supply_usd']),2)} for pool in result]


# Отсортируем
sorted_pool_list = sorted(temp_pool_list, key=lambda d: d['tvl'],reverse=True)
sorted_pool_list[0:5]

Отсортировав получим (данные пока не обогащаю, чтобы не выглядело рекламой тех или иных токенов):

Адреса пулов, токенов и TVL

Адреса пулов, токенов и TVL

Визуально воспринимать информацию легче, поэтому построим график:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()

pool_addresses = [d['pool_address'][-4:] for d in sorted_pool_list]
tvl_values = [d['tvl'] for d in sorted_pool_list]


plt.bar(pool_addresses, tvl_values, color='skyblue',log=True)
plt.xlabel('Pool Address')
plt.ylabel('TVL')
plt.title('TVL for Pools')
plt.xticks(color='w')
plt.tight_layout()

Результат:

Логарифимческий масштаб!

Логарифимческий масштаб!

Из графика хорошо видно, что первые пару пулов перекрывают по объему все остальные, также видно, что бывают плато, и объем залоченных средств хорошо позволяет классифицировать токены по неким лигам, от мелочи до просто огромных пулов. 

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

threshold = 10000000

big_pools = sum(item['tvl'] for item in sorted_pool_list if item['tvl']>=threshold)
small_pools = sum(item['tvl'] for item in sorted_pool_list if item['tvl']=threshold])
small_pools_count = len([item['tvl'] for item in sorted_pool_list if item['tvl']

Получим следующую диаграмму:

737aef9e51587b41f30328284bef0318.png

На блокчейне Solana есть лаунчапад pump fun, в нем заложено ограничение на 69к долларов, т.е. вы можете запустить свой токен, но как только он вырастет выше 69к долларов он «уйдет» на крупную биржу, попробуем такой порог:

31aeae9984d4b223a5253765d74fd816.png

Но и здесь не без нюансов, пулы могут содержать в себе пары любых жеттонов (стандарт токенов на ТОН) или ТОН. Но в большинстве своем это пулы:

Notcoin можно назвать самым крупным мемкойном TON«а, поэтому наш простой дэшборд стоит начать с доминации Notcoin. Проверим диаграммой:

Notcoin = 'EQAvlWFDxGF2lXm67y4yzC17wYKD9A0guwPkMs1gOsM__NOT'


not_pools = sum(item['tvl'] for item in sorted_pool_list if item['token0_address']==Notcoin or item['token1_address']==Notcoin )
notnot_pools = sum(item['tvl'] for item in sorted_pool_list if item['token0_address']!=Notcoin or item['token1_address']!=Notcoin )


not_count = len([item['tvl'] for item in sorted_pool_list if item['token0_address']==Notcoin or item['token1_address']==Notcoin])
notnot_count = len([item['tvl'] for item in sorted_pool_list if item['token0_address']!=Notcoin or item['token1_address']!=Notcoin])


# посчитать кол-во и вывести в название

labels = 'Notcoin pools', 'Other pools count'
sizes = [not_pools, notnot_pools]

fig, ax = plt.subplots()

ax.pie(sizes, labels=labels)
ax.set_title("Notcoin pools count:{}, Other pools count {} ".format(not_count,notnot_count))

Итог:

Сравните с предыдущей диаграммой и вы поймете масштаб Ноткойна в рамках TON

Сравните с предыдущей диаграммой и вы поймете масштаб Ноткойна в рамках TON

Как можно видеть по сравнению с другими токенами Notcoin огромен и его доминацию стоит учитывать при обзоре рынка мемкойнов TON.

Проблема ликвидности

Окей, допустим вы выбрали какой-то порог TVL, и посмотрели доминацию Notcoin, но дальше рассматривать TVL бесполезно. Малые пулы страдают проблемой ликвидности — в них просто мало свапов. И заблокированная в них ликвидность никак не позволяет их сравнить. 

Именно поэтому, аналитические платформы мемкойнов часто выглядит как live feed. Приложения сканируют блоки на предмет свапов в малых пулах. Это позволяет за минуту примерно увидеть какой мемкойн сейчас растет. Чтобы было понятнее как это выглядит, я собрал подобное для пулов Stonfi: https://tonhotshot.fun/

  1. Сначала сканируются пулы и находится самый крупный пул до 69к TVL — царь горы

  2. Далее каждый блок сканируется на транзакции в Stonfi в пулах до 69к.

Причем данные беруться из общедоступных API Stonfi о которых я упоминал ранее. Попробуем построить свой дэшборд на этих данных.

Ранжируем мемкойны на Stonfi

Данные о свапах мы будем брать из нового API созданного под Dexscreener, это API событий (export/dexscreener/v1/events). Данное API отдает все события DEX Stonfi между двумя блоками. Пускай для нашего дэшборда мы выберем период в день, где же нам взять текущий блок от которого оттолкнуться? Здесь два варианта:

Первый вариант, воспользоваться соседней ручкой export/dexscreener/v1/latest-block, она вернет последний блок обработанный бэкендом биржи, которая индексирует блокчейн и позволяет нам получить данные в агрегированном виде. Плюс данного подхода, что мы получим последний обработанный индексатором блок, минус то, что ручка отрабатывает в среднем 10 секунд и это не всегда удобно.

  ltb = requests.get('https://api.ston.fi/v1/screener/latest-block')
  lastest_block = ltb.json()['block']['blockNumber']

Второй вариант, взять прост последний блок из блокчейна, да последний блок в блокчейне не равен последнему блоку обработанному индексатором биржи, но зато это быстро, один из вариантов как это сделать

  # RPS 1 sec
  ltb = requests.get('https://toncenter.com/api/v3/masterchainInfo')
  return ltb.json()['last']['seqno']

Допустим мы возьмем все события, как убрать крупные блоки? Точно также как мы делали вначале — достать все пулы и убрать те, что нам не нужны:

def get_block_list(sorted_pool_list,threshold):
  Notcoin = 'EQAvlWFDxGF2lXm67y4yzC17wYKD9A0guwPkMs1gOsM__NOT'
  return [item['pool_address'] for item in sorted_pool_list if item['tvl']>=threshold or item['token0_address'] == Notcoin or item['token1_address'] == Notcoin]

Также сразу уберем пулы с ноткойном, так как мы ищем новые мемкойны.

После сбора событий, сразу посчитаем количество используя Counter:

def count_swap_events(sorted_pool_list,threshold):
  blocker = get_block_list(sorted_pool_list,threshold)


  ltb = requests.get('https://api.ston.fi/v1/screener/latest-block')
  lastest_block = ltb.json()['block']['blockNumber']


  start_block=lastest_block - int(86400/5) # TON "обновляет" блоки каждые 5 секунд в дне 86400 секунд
  payload = {'fromBlock': start_block, 'toBlock': lastest_block}
  r = requests.get('https://api.ston.fi/export/dexscreener/v1/events', params=payload)


  count_arr=[]
  for item in r.json()['events']:
    if(item['eventType']=='swap'):
      if(item["pairId"] not in blocker):
        count_arr.append(item)


  c = Counter()
  for event in count_arr:
    c[event["pairId"]] += 1

Чтобы зацепить еще пару ручек, обогатим наши данные используя только адрес пула, для этого с помощью /export/dexscreener/v1/pair/ достанем адреса токенов и с помощью /v1/assets/ достанем названия жетонов или TON.

def pool_pair(pool_addr):
    p = requests.get('https://api.ston.fi/export/dexscreener/v1/pair/{}'.format(pool_addr))
    try:
      pair=p.json()['pool']
      return jetton_name(pair['asset0Id']) +"-"+ jetton_name(pair['asset1Id'])
    except:
      return pool_addr

Здесь отмечу, что это просто туториал и весь код пишется максимально примитивно, чтобы вы могли прочитать его по диагонали. Обогатим наш Counter и отсортируем его:

def count_swap_events(sorted_pool_list,threshold):
  blocker = get_block_list(sorted_pool_list,threshold)


  ltb = requests.get('https://api.ston.fi/v1/screener/latest-block')
  lastest_block = ltb.json()['block']['blockNumber']


  start_block=lastest_block - int(86400/5) # TON "обновляет" блоки каждые 5 секунд в дне 86400 секунд
  payload = {'fromBlock': start_block, 'toBlock': lastest_block}
  r = requests.get('https://api.ston.fi/export/dexscreener/v1/events', params=payload)


  count_arr=[]
  for item in r.json()['events']:
    if(item['eventType']=='swap'):
      if(item["pairId"] not in blocker):
        count_arr.append(item)


  c = Counter()
  for event in count_arr:
    c[event["pairId"]] += 1


  enriched_arr=[]


  for pool in sorted(list(c.items()), key=lambda d: d[1],reverse=True)[0:30]:
    enriched_arr.append({"pool": pool_pair(pool[0]),'24h_swaps':pool[1]})


  return enriched_arr

Получим примерно следующее:

На момент статьи это уже сильно устаревшие данные, чтобы не было рекламой

На момент статьи это уже сильно устаревшие данные, чтобы не было рекламой

Конечно, можно много что улучшить, но предлагаю читателю самому дособирать дэшборд, все API  Stonfi можно глянуть здесь — https://api.ston.fi/swagger-ui/ 

Заключение

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

https://github.com/romanovichim/TonFunClessons_ru

Новые туториалы и дата аналитику я кидаю сюда

© Habrahabr.ru