Описание подграфа

Claudi#6022
5 min readNov 13, 2020

Подграф определяет, какие данные The Graph будет индексировать из Ethereum и как он будет их хранить.

Материал для перевода взят из статьи.

Три ключевых компонента подграфа для лучшего понимания на высоком уровне:

  • Манифест подграфа
  • Схема GraphQL
  • Написание mappings

Манифест Подграфа

Манифест подграфа subgraph.yaml определяет смарт-контракты, на которые индексируются ваши подграфы, на какие события из этих контрактов следует обращать внимание и как сопоставлять данные событий с сущностями, которые узел The Graph хранит и позволяет запрашивать. Полную спецификацию для манифестов подграфов можно найти здесь.

Для примера подграф, subgraph.yaml является:

specVersion: 0.0.1
description: Gravatar for Ethereum
repository: https://github.com/graphprotocol/example-subgraph
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum/contract
name: Gravity
network: mainnet
source:
address: '0x2E645469f354BB4F5c8a05B3b30A929361cf77eC'
abi: Gravity
startBlock: 6175244
mapping:
kind: ethereum/events
apiVersion: 0.0.1
language: wasm/assemblyscript
entities:
- Gravatar
abis:
- name: Gravity
file: ./abis/Gravity.json
eventHandlers:
- event: NewGravatar(uint256,address,string,string)
handler: handleNewGravatar
- event: UpdatedGravatar(uint256,address,string,string)
handler: handleUpdatedGravatar
callHandlers:
- function: createGravatar(string,string)
handler: handleCreateGravatar
blockHandlers:
- function: handleBlock
- function: handleBlockWithCall
filter:
kind: call
file: ./src/mapping.ts

Важными записями манифеста являются:

  • description: удобочитаемое описание того, что это за подграф. Описание отображается в Graph Explorer, когда подграф развертывается в размещенной службе.
  • repository: URL-адрес репозитория, в котором можно найти манифест подграфа. Это также отображается в Graph Explorer.
  • dataSources.source: address смарт-контракта, источники подграфа и abi смарт-контракта для использования. address не является обязательным; его отсутствие позволяет индексировать совпадающие события из всех контрактов.
  • dataSources.source.startBlock: необязательный номер блока, с которого источник данных начинает индексацию. В большинстве случаев мы предлагаем использовать блок, в котором был создан контракт.
  • dataSources.mapping.entities: сущности, которые источник данных записывает в хранилище. Схема для каждой сущности определяется в файлеschema.graphql.
  • dataSources.mapping.abis: один или несколько именованных файлов ABI для исходного контракта, а также любых других смарт-контрактов, с которыми вы взаимодействуете из сопоставлений.
  • dataSources.mapping.eventHandlers: перечисляет события смарт-контрактов, на которые реагирует этот подграф, и обработчики в файле mapping-./src/mapping.ts в примере, которые преобразуют эти события в entities in the store.
  • dataSources.mapping.callHandlers: перечисляет функции смарт-контракта, на которые реагирует этот подграф, и обработчики в сопоставлении, которые преобразуют входные и выходные данные в вызовы функций в entities in the store.
  • dataSources.mapping.blockHandlers: перечисляет блоки, на которые этот подграф реагирует, и обработчики в сопоставлении, которые запускаются при добавлении блока в цепочку. Без фильтра обработчик блока будет запускаться каждый блок. Необязательный фильтр может быть следующих видов: call. Фильтр call запустит обработчик, если блок содержит хотя бы один вызов контракта источника данных.

Один подграф может индексировать данные из нескольких смарт-контрактов. Добавьте запись для каждого контракта, из которого необходимо индексировать данные, в массив dataSources.

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

  1. Триггеры событий и вызовов сначала упорядочиваются по индексу транзакции в блоке.
  2. Триггеры событий и вызовов, содержащиеся в одной транзакции, упорядочиваются по соглашению: сначала триггеры событий, затем триггеры вызова, причем каждый тип соответствует порядку, который они определены в манифесте.
  3. Блочные триггеры запускаются после событий и триггеров вызова в том порядке, в котором они определены в манифесте.

Эти правила могут быть изменены.

Получение ABIs

Файл (ы) ABI должен соответствовать вашему контракту (ам). Есть несколько способов получить файлы ABI:

  • Если вы создаете свой собственный проект, у вас, вероятно, будет доступ к вашим самым последним ABI.
  • Если вы создаете подграф для общедоступного проекта, вы можете загрузить этот проект на свой компьютер и получить ABI с помощью truffle compile или использования solc для компиляции.
  • Вы также можете найти ABI на Etherscan, но это не всегда надежно, поскольку загруженный туда ABI может быть устаревшим. Убедитесь, что у вас правильный ABI, иначе выполнение вашего подграфа не удастся.

Схема GraphQL

Схема для вашего подграфа находится в файле schema.graphql. Схемы GraphQL определяются с использованием языка определения интерфейса GraphQL. Если вы никогда не писали схему GraphQL, рекомендуется ознакомиться с этим учебником по системе типов GraphQL. Справочную документацию по схемам GraphQL можно найти в разделе GraphQL API.

Определение сущностей

Перед определением сущностей важно сделать шаг назад и подумать о том, как ваши данные структурированы и связаны. Все запросы будут выполняться против модели данных, определенной в схеме подграфа, и сущностей, проиндексированных подграфом. Из-за этого хорошо определить схему подграфа таким образом, чтобы она соответствовала потребностям вашего dApp. Может быть полезно представить сущности как «объекты, содержащие данные», а не как события или функции.

С помощью The Graph вы просто определяете типы сущностей в schema.graphql, а Graph Node будет генерировать поля верхнего уровня для запроса отдельных экземпляров и коллекций этого типа сущности.

Хороший пример

Сущность Gravatar ниже структурирована вокруг объекта Gravatar и является хорошим примером того, как можно определить сущность.

type Gravatar @entity {
id: ID!
owner: Bytes
displayName: String
imageUrl: String
accepted: Boolean
}

Плохой пример

type GravatarAccepted @entity {
id: ID!
owner: Bytes
displayName: String
imageUrl: String
}

type GravatarDeclined @entity {
id: ID!
owner: Bytes
displayName: String
imageUrl: String
}

Написание Mappings

Mappings преобразуют данные Ethereum, которые ваши mappings получают, в сущности, определенные в вашей схеме. Mappings записываются в подмножестве TypeScript, называемом AssemblyScript, который может быть скомпилирован в WASM (WebAssembly). AssemblyScript строже обычного TypeScript, но обеспечивает знакомый синтаксис.

Для каждого обработчика событий, определенного в subgraph.yaml в разделе mapping.eventHandlers, создайте экспортируемую функцию с тем же именем. Каждый обработчик должен принимать один параметр с именем event с типом, соответствующим имени обрабатываемого события.

В примере подграфа src/mapping.ts содержит обработчики событий NewGravatar и UpdatedGravatar:

import { NewGravatar, UpdatedGravatar } from '../generated/Gravity/Gravity'
import { Gravatar } from '../generated/schema'

export function handleNewGravatar(event: NewGravatar): void {
let gravatar = new Gravatar(event.params.id.toHex())
gravatar.owner = event.params.owner
gravatar.displayName = event.params.displayName
gravatar.imageUrl = event.params.imageUrl
gravatar.save()
}

export function handleUpdatedGravatar(event: UpdatedGravatar): void {
let id = event.params.id.toHex()
let gravatar = Gravatar.load(id)
if (gravatar == null) {
gravatar = new Gravatar(id)
}
gravatar.owner = event.params.owner
gravatar.displayName = event.params.displayName
gravatar.imageUrl = event.params.imageUrl
gravatar.save()
}

Первый обработчик принимает событие NewGravatar и создает новую сущность Gravatar-new Gravatar(event.params.id.toHex()), заполняя поля сущности с использованием соответствующих параметров события. Этот экземпляр сущности представлен переменной gravatar со значением id event.params.id.toHex().

Второй обработчик пытается загрузить существующий Gravatar из хранилища Graph Node. Если его еще нет, он создается по запросу. Затем объект обновляется в соответствии с новыми параметрами события, прежде чем он будет сохранен обратно в хранилище с помощью gravatar.save().

Рекомендуемые идентификаторы для создания новых объектов

У каждой сущности должен быть id, уникальный среди всех сущностей одного типа. Значение idобъекта устанавливается при создании объекта. Ниже приведены некоторые рекомендуемые значения id, которые следует учитывать при создании новых сущностей. ПРИМЕЧАНИЕ. Значение id должно быть string.

  • event.params.id.toHex()
  • event.transaction.from.toHex()
  • event.transaction.hash.toHex() + "-" + event.logIndex.toString()

Мы предоставляем библиотеку Graph Typescript Library, которая содержит утилиты для взаимодействия с хранилищем Graph Node и удобства для работы с данными и объектами смарт-контрактов. Вы можете использовать эту библиотеку в своих сопоставлениях, импортировав @graphprotocol/graph-ts в mapping.ts.

О проекте The Graph

Это протокол индексации для организации и эффективного доступа к данным блокчейнов и сетей хранения. С января 2019 года The Graph использует размещенный сервис с более чем 2300 подграфами, развернутыми для приложений Web3 и DeFi, построенных на Ethereum и IPFS, таких как Synthetix, Uniswap, Aave, Balancer, Gnosis, Aragon и других.

Присоединяйтесь к нашему сообществу, в нашем канале Discord , присоединяйтесь к нашему чату Telegram или подписывайтесь на нас в Twitter! В экосистеме The Graph растет сообщество разработчиков, работающих над построением децентрализованного будущего. Наши разработчики всегда готовы пообщаться с вами.

Testnet website: https://thegraph.com/testnet

Blog: https://thegraph.com/blog/

Linkedin: https://www.linkedin.com/company/thegraph/

Everest: https://everest.link

--

--