Попытаюсь рассказать о своем опыте создания собственного сабграфа, чему я научился и что понял в момент создания.
Начнем с того, что я разработчик на Android OS, и до создания своего сабграфа был не сильно знаком с языком Solidity, на котором написаны смарт-контракты Ethereum, также языком TypeScript, на котором пишутся мепперы для сабграфов, и так же до этого не использовал GraphQl для создания сабграфов, поэтому заранее прошу прощения за неточности, если такие будут.
В этой статье не буду пересказывать официальную документацию про построение сабграфов, с которой вы можете ознакомиться здесь. Попытаюсь сосредоточиться на своем опыте, и что я узнал в процессе.
Для начала я стянул пример проекта, который уже содержит основные папки и файлы, как это сделать — описано здесь.
У меня была идея создать сабграф для проекта DODO DEX(exchange and liquidity provide), так как в процессе исследования уже созданных сабграфов для данного проекта, я обнаружил, что они создают отдельный сабграф для каждой пары, не используя все возможности, которые предоставляет граф-протокол.
Я решил попробовать слушать основные события из основного контракта фабрики и при создании новой пары добавлять новый контракт динамически используя шаблоны.
Теперь более подробно, я нашел все смарт-контракты для проекта DODO в их документации. Основной их контракт DODO Zoo, я зашел в Etherscan в секцию abi, скопировал его файл Zoo.json в папку abi, он понадобится нам в дальнейшем.
В смарт-контрактах нас в основном интересуют эвенты, которые данный контракт умеет отправлять, это основной источник информации для сабграфов. У данного контракта есть один основной event DODOBirth , код из смарт контракта:
который вызывается каждый раз после создания новой пары, и мы будем отслеживать его при создании сабграфа.
Дальше настраиваю манифест используя эти данные
Address — адрес контракта нашей фабрики
StartBlock — номер блока создания смарт контракта (обычно номер блока можно узнать по первой транзакции в этом контракте), можно не указывать, тогда синхронизация начнется с самого первого блока.
abi: abi этого контракта, которое мы скопировали в соответствующий файл.
abis: указываем имя и путь к нашему Zoo abi, также вы можете видеть три других abi, это вспомогательные abi, которые я добавил для того, чтобы извлекать информацию о токенах, в этой статье мы не будем это затрагивать.
eventHandlers: event, который мы отслеживаем из контракта, вместе с его параметрами, которые мы можем посмотреть в смарт-контракте и handler — обработчик для этого эвента, о котором я расскажу чуть позже, и путь к файлу, где мы прописали обработчик.
Далее в файле schema.graphql создал сущности DODOPair и Token (на скриншоте пример сущности токена)
Дальше создаю file main.ts в паке src, код которого выглядит приблизительно так:
Tут код пишем на TypeScript. Поскольку я работал с Java и другими объектно-ориентированными языками, мне было интуитивно понятно, иногда я подсматривал в туториалы, если вам будет не все непонятно, что тут происходит, советую посмотреть какие-то базовые уроки по TypeScript, думаю для построения сабграфа этого будет достаточно.
Ocновные момент, на который хотел бы обратить ваше внимание, сверху импортируем сущности, которые мы будем создавать (сущности из схемы) и получать (events) с сети Ethereum. Итак, я импортировал event DODOBirth, и он является входным параметром в нашу функцию-обработчик handleDododBirth, и наш метод будет отрабатывать каждый раз, когда когда в блокчейне Ethereum произойдет данный evet. Дальше у event мы можем получить параметры, делается это через event.params. В моем случае я могу получить адреса base токена и quote token, и на основе них создать объекты токена, которые я объявил в схеме. Так же обратите внимание на let mainStatistic = MainStatistic.load(FACTORY_ADDRESS). Мы пытаемся получить такую сущность по адресу, если она существует — мы ее получим, если же нет, то она будет null, мы делаем проверку, и если она null, то создаем новый объект, используя оператор new. На этом первая часть закончена.
Шаблоны
Как я говорил выше, контракт-фабрика создает другие контракты (контракты пар), которые, по сути, имеют одинаковый код, и, чтобы не прописывать каждый такой контракт в манифесте, мы можем использовать data source templates для динамически создаваемых контрактов.
Нам достаточно подгрузить abi одного из таких контрактов и создать в папке abi еще один файл (в моем случае это dodopair.ts) и вставить туда abi.
Дальше обновляем манифест
Добавляем шаблон, указываем abi для этого шаблона, файл, где мы объявили маппер для эвентов из данного контракта, путь к аби и, соответственно, эвенты (в моем сабграфе я обрабатываю еще и функции, но их может и не быть) и их функции-хендлеры (имя хендлера и функции в маппере должны совпадать, иначе мы получим ошибку на этапе компиляции).
Важный момент: при обработке DODOBirth в маппере для фабрики одной из строчек идет DODOPairTemplate.create(event.params.newBorn), эта строчка как раз берет адрес вновь созданного контракта и создает source на основе шаблона.
Также обратите внимание на то, что в данном шаблоне мы обрабатываем намного больше эвентов, чем фабрика, это потому, что эти контракты сложнее и имеют много эвентов, с которых можно получать полезные нам данные. С кодом контракта вы можете ознакомиться здесь.
В принципе, этого может быть достаточно для того, чтобы попробовать задеплоить свой сабграф, а потом постепенно добавлять больше сущностей и усложнять их структуру.
Думаю, этого достаточно в рамках этого материала, вышло, наверное, очень сумбурно и запутанно, извините — у меня небольшой опыт в написании статей и сабграфов. Если что, задавайте вопросы, буду рад ответить. Надеюсь, этот материал был кому-то полезен.
С полным кодом моего сабграфа вы можете ознакомиться по ссылке.