subgraphs > Developing > Creating > Advance Subgraph Features

Advance Subgraph Features

Reading time: 15 min

Обзор

Ссылка на этот раздел

Add and implement advanced subgraph features to enhanced your subgraph's built.

Starting from specVersion 0.0.4, subgraph features must be explicitly declared in the features section at the top level of the manifest file, using their camelCase name, as listed in the table below:

FeatureName
Non-fatal errorsnonFatalErrors
Full-text SearchfullTextSearch
Graftinggrafting

For instance, if a subgraph uses the Full-Text Search and the Non-fatal Errors features, the features field in the manifest should be:

specVersion: 0.0.4
description: Gravatar for Ethereum
features:
- fullTextSearch
- nonFatalErrors
dataSources: ...

Note that using a feature without declaring it will incur a validation error during subgraph deployment, but no errors will occur if a feature is declared but not used.

Тайм-серии и агрегации

Ссылка на этот раздел

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

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

type Data @entity(timeseries: true) {
id: Int8!
timestamp: Timestamp!
price: BigDecimal!
}
type Stats @aggregation(intervals: ["hour", "day"], source: "Data") {
id: Int8!
timestamp: Timestamp!
sum: BigDecimal! @aggregate(fn: "sum", arg: "price")
}

Определение тайм-серий и агрегаций

Ссылка на этот раздел

Timeseries entities are defined with @entity(timeseries: true) in schema.graphql. Every timeseries entity must have a unique ID of the int8 type, a timestamp of the Timestamp type, and include data that will be used for calculation by aggregation entities. These Timeseries entities can be saved in regular trigger handlers, and act as the “raw data” for the Aggregation entities.

Aggregation entities are defined with @aggregation in schema.graphql. Every aggregation entity defines the source from which it will gather data (which must be a Timeseries entity), sets the intervals (e.g., hour, day), and specifies the aggregation function it will use (e.g., sum, count, min, max, first, last). Aggregation entities are automatically calculated on the basis of the specified source at the end of the required interval.

Доступные интервалы агрегации

Ссылка на этот раздел
  • hour: sets the timeseries period every hour, on the hour.
  • day: sets the timeseries period every day, starting and ending at 00:00.

Доступные функции агрегации

Ссылка на этот раздел
  • sum: Total of all values.
  • count: Number of values.
  • min: Minimum value.
  • max: Maximum value.
  • first: First value in the period.
  • last: Last value in the period.

Пример запроса агрегации

Ссылка на этот раздел
{
stats(interval: "hour", where: { timestamp_gt: 1704085200 }) {
id
timestamp
sum
}
}

Примечание:

Чтобы использовать тайм-серии и агрегации, субграф должен иметь версию спецификации ≥1.1.0. Обратите внимание, что эта функция может претерпеть значительные изменения, которые могут повлиять на обратную совместимость.

Read more about Timeseries and Aggregations.

Неисправимые ошибки

Ссылка на этот раздел

Ошибки индексирования в уже синхронизированных субграфах по умолчанию приведут к сбою субграфа и прекращению синхронизации. В качестве альтернативы субграфы можно настроить на продолжение синхронизации при наличии ошибок, игнорируя изменения, внесенные обработчиком, который спровоцировал ошибку. Это дает авторам субграфов время на исправление своих субграфов, в то время как запросы к последнему блоку продолжают обрабатываться, хотя результаты могут быть противоречивыми из-за бага, вызвавшего ошибку. Обратите внимание на то, что некоторые ошибки всё равно всегда будут фатальны. Чтобы быть нефатальной, ошибка должна быть детерминированной.

Note: The Graph Network does not yet support non-fatal errors, and developers should not deploy subgraphs using that functionality to the network via the Studio.

Для включения нефатальных ошибок необходимо установить в манифесте субграфа следующий флаг функции:

specVersion: 0.0.4
description: Gravatar for Ethereum
features:
- nonFatalErrors
...

The query must also opt-in to querying data with potential inconsistencies through the subgraphError argument. It is also recommended to query _meta to check if the subgraph has skipped over errors, as in the example:

foos(first: 100, subgraphError: allow) {
id
}
_meta {
hasIndexingErrors
}

If the subgraph encounters an error, that query will return both the data and a graphql error with the message "indexing_error", as in this example response:

"data": {
"foos": [
{
"id": "0xdead"
}
],
"_meta": {
"hasIndexingErrors": true
}
},
"errors": [
{
"message": "indexing_error"
}
]

Источники файловых данных IPFS/Arweave

Ссылка на этот раздел

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

Это также закладывает основу для детерминированного индексирования данных вне сети, а также потенциального введения произвольных данных из HTTP-источников.

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

This is similar to the existing data source templates, which are used to dynamically create new chain-based data sources.

This replaces the existing ipfs.cat API

Руководство по обновлению

Ссылка на этот раздел

Update graph-ts and graph-cli

Ссылка на этот раздел

File data sources requires graph-ts >=0.29.0 and graph-cli >=0.33.1

Добавьте новый тип объекта, который будет обновляться при обнаружении файлов

Ссылка на этот раздел

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

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

Исходный объединенный объект:

type Token @entity {
id: ID!
tokenID: BigInt!
tokenURI: String!
externalURL: String!
ipfsURI: String!
image: String!
name: String!
description: String!
type: String!
updatedAtTimestamp: BigInt
owner: User!
}

Новый разделенный объект:

type Token @entity {
id: ID!
tokenID: BigInt!
tokenURI: String!
ipfsURI: TokenMetadata
updatedAtTimestamp: BigInt
owner: String!
}
type TokenMetadata @entity {
id: ID!
image: String!
externalURL: String!
name: String!
description: String!
}

Если между родительским объектом и результирующим объектом-источником данных существует связь1:1, то наиболее простым вариантом будет связать родительский объект с результирующим файловым объектом, используя в качестве поиска IPFS CID. Свяжитесь с нами в Discord, если у Вас возникли трудности с моделированием новых объектов на основе файлов!

You can use nested filters to filter parent entities on the basis of these nested entities.

Add a new templated data source with kind: file/ipfs or kind: file/arweave

Ссылка на этот раздел

Это источник данных, который будет создан при обнаружении интересующего файла.

templates:
- name: TokenMetadata
kind: file/ipfs
mapping:
apiVersion: 0.0.7
language: wasm/assemblyscript
file: ./src/mapping.ts
handler: handleMetadata
entities:
- TokenMetadata
abis:
- name: Token
file: ./abis/Token.json

Currently abis are required, though it is not possible to call contracts from within file data sources

The file data source must specifically mention all the entity types which it will interact with under entities. See limitations for more details.

Создание нового обработчика для обработки файлов

Ссылка на этот раздел

This handler should accept one Bytes parameter, which will be the contents of the file, when it is found, which can then be processed. This will often be a JSON file, which can be processed with graph-ts helpers (documentation).

The CID of the file as a readable string can be accessed via the dataSource as follows:

const cid = dataSource.stringParam()

Пример обработчика:

import { json, Bytes, dataSource } from '@graphprotocol/graph-ts'
import { TokenMetadata } from '../generated/schema'
export function handleMetadata(content: Bytes): void {
let tokenMetadata = new TokenMetadata(dataSource.stringParam())
const value = json.fromBytes(content).toObject()
if (value) {
const image = value.get('image')
const name = value.get('name')
const description = value.get('description')
const externalURL = value.get('external_url')
if (name && image && description && externalURL) {
tokenMetadata.name = name.toString()
tokenMetadata.image = image.toString()
tokenMetadata.externalURL = externalURL.toString()
tokenMetadata.description = description.toString()
}
tokenMetadata.save()
}
}

Создание файловых источников данных при необходимости

Ссылка на этот раздел

Теперь вы можете создавать файловые источники данных во время выполнения обработчиков на чейн-основе:

  • Import the template from the auto-generated templates
  • call TemplateName.create(cid: string) from within a mapping, where the cid is a valid content identifier for IPFS or Arweave

For IPFS, Graph Node supports v0 and v1 content identifiers, and content identifers with directories (e.g. bafyreighykzv2we26wfrbzkcdw37sbrby4upq7ae3aqobbq7i4er3tnxci/metadata.json).

For Arweave, as of version 0.33.0 Graph Node can fetch files stored on Arweave based on their transaction ID from an Arweave gateway (example file). Arweave supports transactions uploaded via Irys (previously Bundlr), and Graph Node can also fetch files based on Irys manifests.

Пример:

import { TokenMetadata as TokenMetadataTemplate } from '../generated/templates'
const ipfshash = 'QmaXzZhcYnsisuue5WRdQDH6FDvqkLQX1NckLqBYeYYEfm'
//Этот пример кода предназначен для сборщика субграфа Crypto. Приведенный выше хеш ipfs представляет собой каталог с метаданными токена для всех NFT криптоковена.
export function handleTransfer(event: TransferEvent): void {
let token = Token.load(event.params.tokenId.toString())
if (!token) {
token = new Token(event.params.tokenId.toString())
token.tokenID = event.params.tokenId
token.tokenURI = '/' + event.params.tokenId.toString() + '.json'
const tokenIpfsHash = ipfshash + token.tokenURI
//Это создает путь к метаданным для одного сборщика NFT Crypto. Он объединяет каталог с "/" + filename + ".json"
token.ipfsURI = tokenIpfsHash
TokenMetadataTemplate.create(tokenIpfsHash)
}
token.updatedAtTimestamp = event.block.timestamp
token.owner = event.params.to.toHexString()
token.save()
}

Это создаст новый источник данных файла, который будет опрашивать настроенную конечную точку IPFS или Arweave Graph Node, повторяя попытку, если она не найдена. Когда файл будет найден, будет выполнен обработчик источника данных файла.

This example is using the CID as the lookup between the parent Token entity and the resulting TokenMetadata entity.

Previously, this is the point at which a subgraph developer would have called ipfs.cat(CID) to fetch the file

Поздравляем, Вы используете файловые источники данных!

Развертывание субграфов

Ссылка на этот раздел

You can now build and deploy your subgraph to any Graph Node >=v0.30.0-rc.0.

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

  • Объекты, созданные с помощью файловых источников данных, неизменяемы и не могут быть обновлены
  • Обработчики файловых источников данных не могут получить доступ к объектам из других файловых источников данных
  • Объекты, связанные с источниками данных файлов, не могут быть доступны обработчикам на чейн-основе

Хотя это ограничение не должно вызывать проблем в большинстве случаев, для некоторых оно может вызвать сложности. Если у Вас возникли проблемы с моделированием Ваших файловых данных в субграфе, свяжитесь с нами через Discord!

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

Лучшие практики

Ссылка на этот раздел

Если Вы связываете метаданные NFT с соответствующими токенами, используйте хэш IPFS метаданных для ссылки на объект Metadata из объекта Token. Сохраните объект Metadata, используя хэш IPFS в качестве идентификатора.

You can use DataSource context when creating File Data Sources to pass extra information which will be available to the File Data Source handler.

If you have entities which are refreshed multiple times, create unique file-based entities using the IPFS hash & the entity ID, and reference them using a derived field in the chain-based entity.

Мы работаем над улучшением приведенной выше рекомендации, поэтому запросы возвращают только "самую последнюю" версию

Известные проблемы

Ссылка на этот раздел

File data sources currently require ABIs, even though ABIs are not used (issue). Workaround is to add any ABI.

Handlers for File Data Sources cannot be in files which import eth_call contract bindings, failing with "unknown import: ethereum::ethereum.call has not been defined" (issue). Workaround is to create file data source handlers in a dedicated file.

Crypto Coven Subgraph migration

GIP File Data Sources

Фильтры индексированных аргументов/фильтры тем

Ссылка на этот раздел

Requires: SpecVersion >= 1.2.0

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

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

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

Как работают фильтры тем

Ссылка на этот раздел

Когда смарт-контракт генерирует событие, любые аргументы, помеченные как индексированные, могут использоваться в манифесте субграфа в качестве фильтров. Это позволяет субграфу выборочно прослушивать события, соответствующие этим индексированным аргументам.

  • The event's first indexed argument corresponds to topic1, the second to topic2, and so on, up to topic3, since the Ethereum Virtual Machine (EVM) allows up to three indexed arguments per event.
// Идентификатор лицензии SPDX: MIT
pragma solidity ^0.8.0;
contract Token {
// Объявление события с индексируемыми параметрами для адресов
event Transfer(address indexed from, address indexed to, uint256 value);
// Функция для имитации передачи токенов
function transfer(address to, uint256 value) public {
// Генерация события Transfer с указанием from, to и value
emit Transfer(msg.sender, to, value);
}
}

В этом примере:

  • The Transfer event is used to log transactions of tokens between addresses.
  • The from and to parameters are indexed, allowing event listeners to filter and monitor transfers involving specific addresses.
  • The transfer function is a simple representation of a token transfer action, emitting the Transfer event whenever it is called.

Конфигурация в субграфах

Ссылка на этот раздел

Фильтры тем определяются непосредственно в конфигурации обработчика событий в манифесте субграфа. Вот как они настроены:

eventHandlers:
- event: SomeEvent(indexed uint256, indexed address, indexed uint256)
handler: handleSomeEvent
topic1: ['0xValue1', '0xValue2']
topic2: ['0xAddress1', '0xAddress2']
topic3: ['0xValue3']

В этой настройке:

  • topic1 corresponds to the first indexed argument of the event, topic2 to the second, and topic3 to the third.
  • Каждая тема может иметь одно или несколько значений, и событие обрабатывается только в том случае, если оно соответствует одному из значений в каждой указанной теме.

Логика фильтра

Ссылка на этот раздел
  • В рамках одной темы: логика действует как условие OR. Событие будет обработано, если оно соответствует любому из перечисленных значений в данной теме.
  • Между разными темами: логика функционирует как условие AND. Событие должно удовлетворять всем указанным условиям в разных темах, чтобы вызвать соответствующий обработчик.

Пример 1. Отслеживание прямых переводов с адреса A на адрес B

Ссылка на этот раздел
eventHandlers:
- event: Transfer(indexed address,indexed address,uint256)
handler: handleDirectedTransfer
topic1: ['0xAddressA'] # Sender Address
topic2: ['0xAddressB'] # Receiver Address

В данной конфигурации:

  • topic1 is configured to filter Transfer events where 0xAddressA is the sender.
  • topic2 is configured to filter Transfer events where 0xAddressB is the receiver.
  • The subgraph will only index transactions that occur directly from 0xAddressA to 0xAddressB.

Пример 2. Отслеживание транзакций в любом направлении между двумя и более адресами

Ссылка на этот раздел
eventHandlers:
- event: Transfer(indexed address,indexed address,uint256)
handler: handleTransferToOrFrom
topic1: ['0xAddressA', '0xAddressB', '0xAddressC'] # Адрес отправителя
topic2: ['0xAddressB', '0xAddressC'] # Адрес получателя

В данной конфигурации:

  • topic1 is configured to filter Transfer events where 0xAddressA, 0xAddressB, 0xAddressC is the sender.
  • topic2 is configured to filter Transfer events where 0xAddressB and 0xAddressC is the receiver.
  • Субграф будет индексировать транзакции, происходящие в любом направлении между несколькими адресами, что позволит осуществлять комплексный мониторинг взаимодействий с участием всех адресов.

Декларированный eth_call

Ссылка на этот раздел

Примечание: Это экспериментальная функция, которая пока недоступна в стабильной версии Graph Node. Вы можете использовать её только в Subgraph Studio или на своей локальной ноде.

Declarative eth_calls are a valuable subgraph feature that allows eth_calls to be executed ahead of time, enabling graph-node to execute them in parallel.

Эта функция выполняет следующие действия:

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

Ключевые понятия

Ссылка на этот раздел
  • Declarative eth_calls: Ethereum calls that are defined to be executed in parallel rather than sequentially.
  • Параллельное выполнение: Вместо того, чтобы ждать завершения одного вызова перед началом следующего, можно инициировать несколько вызовов одновременно.
  • Эффективность использования времени: Общее время, затраченное на все вызовы, изменяется от суммы времени отдельных вызовов (последовательные) до времени, затраченного на самый продолжительный вызов (параллельные).

Scenario without Declarative eth_calls

Ссылка на этот раздел

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

Традиционно эти вызовы могут выполняться последовательно:

  1. Вызов 1 (Транзакции): Занимает 3 секунды
  2. Вызов 2 (Баланс): Занимает 2 секунды
  3. Вызов 3 (Холдинг токенов): Занимает 4 секунды

Общее затраченное время = 3 + 2 + 4 = 9 секунд

Scenario with Declarative eth_calls

Ссылка на этот раздел

С помощью этой функции Вы можете объявить, что эти вызовы будут выполняться параллельно:

  1. Вызов 1 (Транзакции): Занимает 3 секунды
  2. Вызов 2 (Баланс): Занимает 2 секунды
  3. Вызов 3 (Холдинг токенов): Занимает 4 секунды

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

Общее затраченное время = макс. (3, 2, 4) = 4 секунды

Как это работает

Ссылка на этот раздел
  1. Декларативное определение: В манифесте субграфа Вы декларируете вызовы Ethereum таким образом, чтобы указать, что они могут выполняться параллельно.
  2. Механизм параллельного выполнения: Механизм выполнения The Graph Node распознает эти объявления и выполняет вызовы одновременно.
  3. Агрегация результатов: После завершения всех вызовов результаты агрегируются и используются субграфом для дальнейшей обработки.

Пример конфигурации в манифесте субграфа

Ссылка на этот раздел

Declared eth_calls can access the event.address of the underlying event as well as all the event.params.

Subgraph.yaml using event.address:

eventHandlers:
event: Swap(indexed address,indexed address,int256,int256,uint160,uint128,int24)
handler: handleSwap
calls:
global0X128: Pool[event.address].feeGrowthGlobal0X128()
global1X128: Pool[event.address].feeGrowthGlobal1X128()

Подробности для приведенного выше примера:

  • global0X128 is the declared eth_call.
  • The text (global0X128) is the label for this eth_call which is used when logging errors.
  • The text (Pool[event.address].feeGrowthGlobal0X128()) is the actual eth_call that will be executed, which is in the form of Contract[address].function(arguments)
  • The address and arguments can be replaced with variables that will be available when the handler is executed.

Subgraph.yaml using event.params

calls:
- ERC20DecimalsToken0: ERC20[event.params.token0].decimals()

Графтинг (перенос) на существующие субграфы

Ссылка на этот раздел

Note: it is not recommended to use grafting when initially upgrading to The Graph Network. Learn more here.

When a subgraph is first deployed, it starts indexing events at the genesis block of the corresponding chain (or at the startBlock defined with each data source) In some circumstances; it is beneficial to reuse the data from an existing subgraph and start indexing at a much later block. This mode of indexing is called Grafting. Grafting is, for example, useful during development to get past simple errors in the mappings quickly or to temporarily get an existing subgraph working again after it has failed.

A subgraph is grafted onto a base subgraph when the subgraph manifest in subgraph.yaml contains a graft block at the top-level:

description: ...
graft:
base: Qm... # Subgraph ID of base subgraph
block: 7345624 # Block number

When a subgraph whose manifest contains a graft block is deployed, Graph Node will copy the data of the base subgraph up to and including the given block and then continue indexing the new subgraph from that block on. The base subgraph must exist on the target Graph Node instance and must have indexed up to at least the given block. Because of this restriction, grafting should only be used during development or during an emergency to speed up producing an equivalent non-grafted subgraph.

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

Графтовый субграф может использовать схему GraphQL, которая не идентична схеме базового субграфа, а просто совместима с ней. Она сама по себе должна быть допустимой схемой субграфа, но может отличаться от схемы базового субграфа следующими способами:

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

Feature Management: grafting must be declared under features in the subgraph manifest.

Редактировать страницу

Предыдущий
Writing AssemblyScript Mappings
Следующий
AssemblyScript API
Редактировать страницу