15 minutos
Manifest do Subgraph
Visão geral
The Subgraph manifest, subgraph.yaml
, defines the smart contracts & network your Subgraph will index, the events from these contracts to pay attention to, and how to map event data to entities that Graph Node stores and allows to query.
The Subgraph definition consists of the following files:
-
subgraph.yaml
: Contains the Subgraph manifest -
schema.graphql
: A GraphQL schema defining the data stored for your Subgraph and how to query it via GraphQL -
mapping.ts
: Código de Mapeamentos do AssemblyScript que traduz dados de eventos para entidades definidas no seu schema (por exemplo,mapping.ts
neste guia)
Capacidades do Subgraph
A single Subgraph can:
-
Indexar dados de vários contratos inteligentes (mas não de múltiplas redes).
-
Indexar dados de arquivos IPFS usando Fontes de Dados de Arquivo.
-
Adicionar uma entrada para cada contrato que precisa ser indexado para o arranjo
dataSources
.
The full specification for Subgraph manifests can be found here.
For the example Subgraph listed above, subgraph.yaml
is:
1specVersion: 1.3.02description: Gravatar for Ethereum3repository: https://github.com/graphprotocol/graph-tooling4schema:5 file: ./schema.graphql6indexerHints:7 prune: auto8dataSources:9 - kind: ethereum/contract10 name: Gravity11 network: mainnet12 source:13 address: '0x2E645469f354BB4F5c8a05B3b30A929361cf77eC'14 abi: Gravity15 startBlock: 617524416 endBlock: 717524517 context:18 foo:19 type: Bool20 data: true21 bar:22 type: String23 data: 'bar'24 mapping:25 kind: ethereum/events26 apiVersion: 0.0.927 language: wasm/assemblyscript28 entities:29 - Gravatar30 abis:31 - name: Gravity32 file: ./abis/Gravity.json33 eventHandlers:34 - event: NewGravatar(uint256,address,string,string)35 handler: handleNewGravatar36 - event: UpdatedGravatar(uint256,address,string,string)37 handler: handleUpdatedGravatar38 callHandlers:39 - function: createGravatar(string,string)40 handler: handleCreateGravatar41 blockHandlers:42 - handler: handleBlock43 - handler: handleBlockWithCall44 filter:45 kind: call46 file: ./src/mapping.ts
Entradas do Subgraph
Important Note: Be sure you populate your Subgraph manifest with all handlers and entities.
As entradas importantes para atualizar para o manifest são:
-
specVersion
: a semver version that identifies the supported manifest structure and functionality for the Subgraph. The latest version is1.3.0
. See specVersion releases section to see more details on features & releases. -
description
: a human-readable description of what the Subgraph is. This description is displayed in Graph Explorer when the Subgraph is deployed to Subgraph Studio. -
repository
: the URL of the repository where the Subgraph manifest can be found. This is also displayed in Graph Explorer. -
features
: é uma lista de todos os nomes de função usados. -
indexerHints.prune
: Defines the retention of historical block data for a Subgraph. See prune in indexerHints section. -
dataSources.source
: the address of the smart contract the Subgraph sources, and the ABI of the smart contract to use. The address is optional; omitting it allows to index matching events from all contracts. -
dataSources.source.startBlock
: o número opcional do bloco de onde a fonte de dados começa a indexar. Em muitos casos, sugerimos usar o bloco em que o contrato foi criado. -
dataSources.source.endBlock
: O número opcional do bloco onde a fonte de dados pára de indexar, inclusive aquele bloco. Versão de spec mínima exigida:0.0.9
. -
dataSources.context
: key-value pairs that can be used within Subgraph mappings. Supports various data types likeBool
,String
,Int
,Int8
,BigDecimal
,Bytes
,List
, andBigInt
. Each variable needs to specify itstype
anddata
. These context variables are then accessible in the mapping files, offering more configurable options for Subgraph development. -
dataSources.mapping.entities
: as entidades que a fonte de dados escreve ao armazenamento. O schema para cada entidade é definido no arquivo schema.graphql. -
dataSources.mapping.abis
: um ou mais arquivos de ABI nomeados para o contrato de origem, além de quaisquer outros contratos inteligentes com os quais interage de dentro dos mapeamentos. -
dataSources.mapping.eventHandlers
: lists the smart contract events this Subgraph reacts to and the handlers in the mapping—./src/mapping.ts in the example—that transform these events into entities in the store. -
dataSources.mapping.callHandlers
: lists the smart contract functions this Subgraph reacts to and handlers in the mapping that transform the inputs and outputs to function calls into entities in the store. -
dataSources.mapping.blockHandlers
: lists the blocks this Subgraph reacts to and handlers in the mapping to run when a block is appended to the chain. Without a filter, the block handler will be run every block. An optional call-filter can be provided by adding afilter
field withkind: call
to the handler. This will only run the handler if the block contains at least one call to the data source contract.
A single Subgraph can index data from multiple smart contracts. Add an entry for each contract from which data needs to be indexed to the dataSources
array.
Handlers de Eventos
Event handlers in a Subgraph react to specific events emitted by smart contracts on the blockchain and trigger handlers defined in the Subgraph’s manifest. This enables Subgraphs to process and store event data according to defined logic.
Como Definir um Handler de Evento
An event handler is declared within a data source in the Subgraph’s YAML configuration. It specifies which events to listen for and the corresponding function to execute when those events are detected.
1dataSources:2 - kind: ethereum/contract3 name: Gravity4 network: dev5 source:6 address: '0x731a10897d267e19b34503ad902d0a29173ba4b1'7 abi: Gravity8 mapping:9 kind: ethereum/events10 apiVersion: 0.0.911 language: wasm/assemblyscript12 entities:13 - Gravatar14 - Transaction15 abis:16 - name: Gravity17 file: ./abis/Gravity.json18 eventHandlers:19 - event: Approval(address,address,uint256)20 handler: handleApproval21 - event: Transfer(address,address,uint256)22 handler: handleTransfer23 topic1: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', '0xc8dA6BF26964aF9D7eEd9e03E53415D37aA96325'] # Optional topic filter which filters only events with the specified topic.
Handlers de chamada
While events provide an effective way to collect relevant changes to the state of a contract, many contracts avoid generating logs to optimize gas costs. In these cases, a Subgraph can subscribe to calls made to the data source contract. This is achieved by defining call handlers referencing the function signature and the mapping handler that will process calls to this function. To process these calls, the mapping handler will receive an ethereum.Call
as an argument with the typed inputs to and outputs from the call. Calls made at any depth in a transaction’s call chain will trigger the mapping, allowing activity with the data source contract through proxy contracts to be captured.
Handlers de chamadas só serão ativados em um de dois casos: quando a função especificada é chamada por uma conta que não for do próprio contrato, ou quando ela é marcada como externa no Solidity e chamada como parte de outra função no mesmo contrato.
Note: Call handlers currently depend on the Parity tracing API. Certain networks, such as BNB chain and Arbitrum, does not support this API. If a Subgraph indexing one of these networks contain one or more call handlers, it will not start syncing. Subgraph developers should instead use event handlers. These are far more performant than call handlers, and are supported on every evm network.
Como Definir um Handler de Chamada
Para definir um handler de chamada no seu manifest, apenas adicione um arranjo callHandlers
sob a fonte de dados para a qual quer se inscrever.
1dataSources:2 - kind: ethereum/contract3 name: Gravity4 network: mainnet5 source:6 address: '0x731a10897d267e19b34503ad902d0a29173ba4b1'7 abi: Gravity8 mapping:9 kind: ethereum/events10 apiVersion: 0.0.911 language: wasm/assemblyscript12 entities:13 - Gravatar14 - Transaction15 abis:16 - name: Gravity17 file: ./abis/Gravity.json18 callHandlers:19 - function: createGravatar(string,string)20 handler: handleCreateGravatar
O function
é a assinatura de função normalizada para filtrar chamadas. A propriedade handler
é o nome da função no mapeamento que quer executar quando a função-alvo é chamada no contrato da fonte de dados.
Função de Mapeamento
Each call handler takes a single parameter that has a type corresponding to the name of the called function. In the example Subgraph above, the mapping contains a handler for when the createGravatar
function is called and receives a CreateGravatarCall
parameter as an argument:
1import { CreateGravatarCall } from '../generated/Gravity/Gravity'2import { Transaction } from '../generated/schema'34export function handleCreateGravatar(call: CreateGravatarCall): void {5 let id = call.transaction.hash6 let transaction = new Transaction(id)7 transaction.displayName = call.inputs._displayName8 transaction.imageUrl = call.inputs._imageUrl9 transaction.save()10}
A função handleCreateGravatar
toma um novo CreateGravatarCall
que é uma subclasse do ethereum.Call, fornecido pelo @graphprotocol/graph-ts, que inclui as entradas e saídas digitadas da chamada. O tipo CreateGravatarCall
é gerado ao executar o graph codegen
.
Handlers de Blocos
In addition to subscribing to contract events or function calls, a Subgraph may want to update its data as new blocks are appended to the chain. To achieve this a Subgraph can run a function after every block or after blocks that match a pre-defined filter.
Filtros Apoiados
Filtro Call
1filter:2 kind: call
O handler definido será chamado uma vez para cada bloco, que contém uma chamada ao contrato (fonte de dados) sob o qual o handler está definido.
Note: The call
filter currently depend on the Parity tracing API. Certain networks, such as BNB chain and Arbitrum, does not support this API. If a Subgraph indexing one of these networks contain one or more block handlers with a call
filter, it will not start syncing.
A ausência de um filtro para um handler de blocos garantirá que o handler seja chamado a todos os blocos. Uma fonte de dados só pode conter um handler de bloco para cada tipo de filtro.
1dataSources:2 - kind: ethereum/contract3 name: Gravity4 network: dev5 source:6 address: '0x731a10897d267e19b34503ad902d0a29173ba4b1'7 abi: Gravity8 mapping:9 kind: ethereum/events10 apiVersion: 0.0.911 language: wasm/assemblyscript12 entities:13 - Gravatar14 - Transaction15 abis:16 - name: Gravity17 file: ./abis/Gravity.json18 blockHandlers:19 - handler: handleBlock20 - handler: handleBlockWithCallToContract21 filter:22 kind: call
Filtro Polling
Requer specVersion
>= 0.0.8
**Nota: Filtros de polling só estão disponíveis nas dataSources de kind: ethereum
.
1blockHandlers:2 - handler: handleBlock3 filter:4 kind: polling5 every: 10
The defined handler will be called once for every n
blocks, where n
is the value provided in the every
field. This configuration allows the Subgraph to perform specific operations at regular block intervals.
Filtro Once
Requer specVersion
>= 0.0.8
Observação: Filtros de once só estão disponíveis nas dataSources de kind: ethereum
.
1blockHandlers:2 - handler: handleOnce3 filter:4 kind: once
The defined handler with the once filter will be called only once before all other handlers run. This configuration allows the Subgraph to use the handler as an initialization handler, performing specific tasks at the start of indexing.
1export function handleOnce(block: ethereum.Block): void {2 let data = new InitialData(Bytes.fromUTF8('initial'))3 data.data = 'Setup data here'4 data.save()5}
Função de Mapeamento
The mapping function will receive an ethereum.Block
as its only argument. Like mapping functions for events, this function can access existing Subgraph entities in the store, call smart contracts and create or update entities.
1import { ethereum } from '@graphprotocol/graph-ts'23export function handleBlock(block: ethereum.Block): void {4 let id = block.hash5 let entity = new Block(id)6 entity.save()7}
Eventos Anónimos
Caso precise processar eventos anónimos no Solidity, isto é possível ao fornecer o topic 0 do evento, como no seguinte exemplo:
1eventHandlers:2 - event: LogNote(bytes4,address,bytes32,bytes32,uint256,bytes)3 topic0: '0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31'4 handler: handleGive
Um evento só será ativado quando a assinatura e o topic 0 corresponderem. topic0
é igual ao hash da assinatura do evento.
Recibos de Transação em Handlers de Eventos
A partir do specVersion
0.0.5
e apiVersion
0.0.7
, os handlers de eventos podem acessar o recibo para a transação que os emitiu.
To do so, event handlers must be declared in the Subgraph manifest with the new receipt: true
key, which is optional and defaults to false.
1eventHandlers:2 - event: NewGravatar(uint256,address,string,string)3 handler: handleNewGravatar4 receipt: true
Dentro da função do handler, o recibo pode ser acessado no campo Event.receipt
. Quando a chave receipt
é configurada em false
, ou omitida no manifest, um valor null
será retornado em vez disto.
Ordem de Handlers de Gatilhos
Os gatilhos para uma fonte de dados dentro de um bloco são ordenados com o seguinte processo:
- Gatilhos de evento e chamada são, primeiro, ordenados por índice de transação no bloco.
- Gatilhos de evento e chamada dentro da mesma transação são ordenados a usar uma convenção: primeiro, gatilhos de evento, e depois, de chamada, cada tipo a respeitar a ordem em que são definidos no manifest.
- Gatilhos de blocos são executados após gatilhos de evento e chamada, na ordem em que são definidos no manifest.
Estas regras de organização estão sujeitas à mudança.
Observe: Quando novas fontes de dados dinâmicas forem criadas, os handlers definidos para fontes de dados dinâmicas só começarão o processamento após todos os handlers existentes forem processados, e repetirão a mesma sequência quando ativados.
Modelos de Fontes de Dados
Um padrão comum em contratos inteligentes compatíveis com EVMs é o uso de contratos de registro ou fábrica. Nisto, um contrato cria, gesta ou refere a um número arbitrário de outros contratos, cada um com o seu próprio estado e eventos.
Os endereços destes subcontratos podem ou não ser conhecidos imediatamente, e muitos destes contratos podem ser criados e/ou adicionados ao longo do tempo. É por isto que, em muitos casos, é impossível definir uma única fonte de dados ou um número fixo de fontes de dados, e é necessária uma abordagem mais dinâmica: modelos de fontes de dados.
Fonte de Dados para o Contrato Principal
Primeiro, defina uma fonte de dados regular para o contrato principal. Abaixo está um exemplo simplificado de fonte de dados para o contrato de fábrica de trocas do Uniswap. Preste atenção ao handler de evento NewExchange(address,address)
: é emitido quando um novo contrato de troca é criado on-chain pelo contrato de fábrica.
1dataSources:2 - kind: ethereum/contract3 name: Factory4 network: mainnet5 source:6 address: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95'7 abi: Factory8 mapping:9 kind: ethereum/events10 apiVersion: 0.0.911 language: wasm/assemblyscript12 file: ./src/mappings/factory.ts13 entities:14 - Directory15 abis:16 - name: Factory17 file: ./abis/factory.json18 eventHandlers:19 - event: NewExchange(address,address)20 handler: handleNewExchange
Modelos de Fontes de Dados para Contratos Criados Dinamicamente
Depois, adicione modelos de fontes de dados ao manifest. Estes são idênticos a fontes de dados regulares, mas não têm um endereço de contrato predefinido sob source
. Tipicamente, é possível definir um modelo para cada tipo de subcontrato administrado ou referenciado pelo contrato parente.
1dataSources:2 - kind: ethereum/contract3 name: Factory4 # ... other source fields for the main contract ...5templates:6 - name: Exchange7 kind: ethereum/contract8 network: mainnet9 source:10 abi: Exchange11 mapping:12 kind: ethereum/events13 apiVersion: 0.0.914 language: wasm/assemblyscript15 file: ./src/mappings/exchange.ts16 entities:17 - Exchange18 abis:19 - name: Exchange20 file: ./abis/exchange.json21 eventHandlers:22 - event: TokenPurchase(address,uint256,uint256)23 handler: handleTokenPurchase24 - event: EthPurchase(address,uint256,uint256)25 handler: handleEthPurchase26 - event: AddLiquidity(address,uint256,uint256)27 handler: handleAddLiquidity28 - event: RemoveLiquidity(address,uint256,uint256)29 handler: handleRemoveLiquidity
Como Instanciar um Modelo de Fontes de Dados
Na etapa final, atualize o seu mapeamento de contratos para criar uma instância dinâmica de fontes de dados de um dos modelos. Neste exemplo, mudarias o mapeamento do contrato principal para importar o modelo Exchange
e chamar o método Exchange.create(address)
nele, para começar a indexar o novo contrato de troca.
1import { Exchange } from '../generated/templates'23export function handleNewExchange(event: NewExchange): void {4 // Comece a indexar a troca; `event.params.exchange` é o5 // endereço do novo contrato de troca6 Exchange.create(event.params.exchange)7}
Observação: Uma nova fonte de dados só processará as chamadas e eventos para o bloco onde ele foi criado e todos os blocos a seguir. Porém, não serão processados dados históricos, por ex., dados contidos em blocos anteriores.
Se blocos anteriores conterem dados relevantes à nova fonte, é melhor indexá-los ao ler o estado atual do contrato e criar entidades que representem aquele estado na hora que a nova fonte de dados for criada.
Contextos de Fontes de Dados
Contextos de fontes de dados permitem passar configurações extras ao instanciar um modelo. No nosso exemplo, vamos dizer que há trocas associadas com um par de trading particular, incluído no evento NewExchange
. Essa informação pode ser passada na fonte de dados instanciada, como:
1import { Exchange } from '../generated/templates'23export function handleNewExchange(event: NewExchange): void {4 let context = new DataSourceContext()5 context.setString('tradingPair', event.params.tradingPair)6 Exchange.createWithContext(event.params.exchange, context)7}
Dentro de um mapeamento do modelo Exchange
, dá para acessar o contexto:
1import { dataSource } from '@graphprotocol/graph-ts'23let context = dataSource.context()4let tradingPair = context.getString('tradingPair')
Há setters e getters como setString
e getString
para todos os tipos de valores.
Blocos Iniciais
The startBlock
is an optional setting that allows you to define from which block in the chain the data source will start indexing. Setting the start block allows the data source to skip potentially millions of blocks that are irrelevant. Typically, a Subgraph developer will set startBlock
to the block in which the smart contract of the data source was created.
1dataSources:2 - kind: ethereum/contract3 name: ExampleSource4 network: mainnet5 source:6 address: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95'7 abi: ExampleContract8 startBlock: 66279179 mapping:10 kind: ethereum/events11 apiVersion: 0.0.912 language: wasm/assemblyscript13 file: ./src/mappings/factory.ts14 entities:15 - User16 abis:17 - name: ExampleContract18 file: ./abis/ExampleContract.json19 eventHandlers:20 - event: NewEvent(address,address)21 handler: handleNewEvent
Observe: O bloco de criação do contrato pode ser buscado rapidamente no Etherscan:
- Procure pelo contrato ao inserir o seu endereço na barra de busca.
- Clique no hash da transação de criação na seção
Contract Creator
. - Carregue a página dos detalhes da transação, onde encontrará o bloco inicial para aquele contrato.
IndexerHints
The indexerHints
setting in a Subgraph’s manifest provides directives for indexers on processing and managing a Subgraph. It influences operational decisions across data handling, indexing strategies, and optimizations. Presently, it features the prune
option for managing historical data retention or pruning.
Este recurso está disponível a partir da specVersion: 1.0.0
Prune
indexerHints.prune
: Defines the retention of historical block data for a Subgraph. Options include:
"never"
: Nenhum pruning de dados históricos; retém o histórico completo."auto"
: Retém o histórico mínimo necessário determinado pelo Indexador e otimiza o desempenho das queries.- Um número específico: Determina um limite personalizado no número de blocos históricos a guardar.
1indexerHints:2 prune: auto
The term “history” in this context of Subgraphs is about storing data that reflects the old states of mutable entities.
O histórico, desde um bloco especificado, é necessário para:
- Time travel queries, which enable querying the past states of these entities at specific blocks throughout the Subgraph’s history
- Using the Subgraph as a graft base in another Subgraph, at that block
- Rewinding the Subgraph back to that block
Se os dados históricos desde aquele bloco tiverem passado por pruning, as capacidades acima não estarão disponíveis.
Vale usar o "auto"
, por maximizar o desempenho de queries e ser suficiente para a maioria dos utilizadores que não exigem acesso a dados extensos no histórico.
For Subgraphs leveraging time travel queries, it’s advisable to either set a specific number of blocks for historical data retention or use prune: never
to keep all historical entity states. Below are examples of how to configure both options in your Subgraph’s settings:
Para reter uma quantidade específica de dados históricos:
1indexerHints:2 prune: 1000 # Substitua 1000 pelo número de blocos que deseja reter
Para preservar o histórico completo dos estados da entidade:
1indexerHints:2 prune: never
SpecVersion Releases
Versão | Notas de atualização |
---|---|
1.3.0 | Added support for Subgraph Composition |
1.2.0 | Added support for Indexed Argument Filtering & declared eth_call |
1.1.0 | Supports Timeseries & Aggregations. Added support for type Int8 for id . |
1.0.0 | Supports indexerHints feature to prune Subgraphs |
0.0.9 | Supports endBlock feature |
0.0.8 | Added support for polling Block Handlers and Initialisation Handlers. |
0.0.7 | Added support for File Data Sources. |
0.0.6 | Supports fast Proof of Indexing calculation variant. |
0.0.5 | Added support for event handlers having access to transaction receipts. |
0.0.4 | Added support for managing subgraph features. |