Subgraph development

In the previous chapters, you have learned step by step the creation, composition, and deployment of submap projects. After the subgraph is successfully deployed, the HyperGraph backend node will index data according to the subgraph. Based on the examples built in the previous section, this chapter explains the development of subgraphs in more detail.

./abis
./abis/Contract.json # The interface definition file of the contract is automatically obtained by the graph program according to the specified contract address and network. Only with abi can you know which events can be processed. This is the original data source definition
./schema.graphql # The sample data entity definition file, in which the data objects and attribute definitions to be operated are placed. Of course, it is not only the entity directly associated with the smart contract event object in the smart contract, but also some intermediate and result entities and query implementation
./subgraph.yaml # The data source, processing process, and method definition of the subgraph, including contract, starting block (not available by default), event, processing program, etc.
./yarn.lock yarn yarn # Tool's dependency update file
./package.json # Project dependencies and definitions used by npm and yarn
./src AssemblyScript # The directory is used to store the analytical logic code and defines the process of transforming data from an event into an entity that can be stored
./src/mapping.ts # Event processing program file, for each contract event, you can write the corresponding processing program here to perform statistics on the data

一、ABI file

ABI is the abbreviation of Application Binary Interface. It defines the interface of the smart contract in the JSON file format, including the definition of the name, parameters, return type, etc. of the open function of the smart contract. Generally, it will be generated with the deployment of smart contracts. The ABI file of a smart contract can be obtained in the following ways.

1、Use truffle compile

If it is a self-owned project, after compiling the contract, you can get a JSON file in the build/contracts directory with ABI in it

2、Generated by solc or solcjs

If there is contract code, you can use Solidity Compiler to obtain the contract ABI, using the JavaScript version of Compiler as an example.

Install solcjs:

npm install solc -g

Or install solc-select:

pip3 install solc-select

To install solc

Get the contract ABI:

Before obtaining the ABI, if it is a complex contract, you can run truffle-flattener to integrate the contract into one file

Execute:

solcjs xxx.sol --abi
solc xxx.sol --abi

You can get the ABI file.

3、Use Remix to compile and generate

If you compile in Remix, after the compilation is successful, you can see the JSON file generated after the smart contract is successfully compiled in the artifacts directory, and open the file. The first part is the ABI.

4、Pass a verified contract

For smart contracts that have been verified on the blockchain browser, you can directly use the smart contract to obtain the ABI. such as

https://hecoinfo.com/address/0x0bb480582ecae1d22bbaeaccfbb849b441450026#code

5、API access

If the ABI is obtained in the program, you can also use the API obtaining method provided by the block explorer.

For example, the scaffolding program in https://hecoinfo.com/apis#contracts graph-cli uses this method to automatically obtain the ABI based on the contract address.

Please use the correct version of ABI, or it may fail during data analysis.

二、Subgraph declaration file subgraph.yaml

subgraph.yaml is a file in YAML format. YAML is used to declare that the function of the subgraph is to facilitate reading and writing. The corresponding support library is complete and the data is not redundant.

In subgraph.yaml, it defines which contract data needs to be indexed in the current subgraph project, and which events need to be analyzed in the corresponding contract, and a mapping relationship is established to combine the events with the corresponding processing procedures Associate, store in Graph Node for storage, and provide query. Of course, for the remote realization of storage and query, the sub-graph developer does not need to care, but the sub-graph developer needs to describe the corresponding logical relationship in the sub-graph mapping program. According to the example in the previous section, the content of subgraph.yaml is as follows:

specVersion: 0.0.2
schema:
  file: ./schema.graphql
dataSources:
  - kind: ethereum/contract
    name: Contract
    network: mainnet
    source:
      address: "0x0bb480582ecae1d22bbaeaccfbb849b441450026"
      startBlock: 2095189
     abi: Contract
     mapping:
       kind: ethereum/events
       apiVersion: 0.0.4
       language: wasm/assemblyscript
      entities:
        - Packetstarted
        - PacketClaimed
        - Packetended
        - ClaimedTokens
        - ClaimedPacketTokens
        - OwnershipTransferred
      abis:
        - name: Contract
      file: ./abis/Contract.json
        eventHandlers:
          - event: Packetstarted(uint256,address)
            handler: handlePacketstarted
          - event: PacketClaimed(uint256,address,uint32,address)
            handler: handlePacketClaimed
          - event: Packetended(uint256,address)
            handler: handlePacketended
          - event: ClaimedTokens(address,address,uint256)
            handler: handleClaimedTokens
          - event: ClaimedPacketTokens(uint32,address,address,uint256)
            handler: handleClaimedPacketTokens
          - event: OwnershipTransferred(address,address)
            handler: handleOwnershipTransferred
        file: ./src/mapping.ts

Let's explain in more detail the composition of each entry in this file:

description: The description of the sub-picture item. If the sub-picture is associated with the sub-picture added in the console, this description will be saved and displayed on the detail page of the sub-picture.

repository: The git address where the submap is located. Here you can find the submap code, including the declaration file. It will also be displayed on the details page of the sub-picture.

dataSources.source: The smart contract address of the subgraph data source, and the abi definition corresponding to the smart contract. This contract address does not have to be written. If the contract address is not specified, it may be necessary to index all events of all addresses.

dataSources.source.startBlock: This data is also optional. This refers to the starting block for contract data analysis. It is generally recommended to use the block where the contract was created as the starting block for starting analysis.

dataSources.mapping.entities: In the sample file, the things in the contract are automatically analyzed and listed here, but in fact, the object entities in the index and query should be defined here. These entities can be stored in the database after the index, and the definition of the entities can also be set in the schema.graphql file.

dataSources.mapping.abis: One or more ABI files can be placed for subsequent interface and operation code generation. In order to facilitate the mapping code and logic, the call interaction between multiple contracts can be carried out.

dataSources.mapping.eventHandlers: event handlers, lists the smart contract events analyzed in the subgraph. In this example, the corresponding event handlers are defined in ./src/mapping.ts. This file can carry out these events Analyze, and then store the required content directly or after calculation into the entity object, and finally store it in the database for storage.

dataSources.mapping.callHandlers: This is the call list of the smart contract function that the subgraph wants to analyze. The handler is also defined in the mapping file to associate the input and output of the function with the entity to be stored in the database.

dataSources.mapping.blockHandlers: lists the block analysis processing procedures. When a block is added to the blockchain, these processing procedures will run to analyze the block data and save the analysis results according to the block analysis processing procedures. If there is no filtering mechanism, this block processing program will analyze each block. The filter can be provided by call. If the data source contract in a block contains a specified call, then the call filter will run the corresponding processing program.

The trigger sequence of data sources in a block is determined according to the transaction sequence in the block.

三、Schema file

GraphQL data entity definition

The data entities of the subgraph project are defined in the chema.graphql file. GraphQL is a language used for API queries. It provides a set of easy-to-understand and complete descriptions of the data in the API, so that the client can accurately obtain the data it needs. Therefore, it can be used to describe the data entity, and it can also be used to request the desired data and get the result.

The data entities in the subgraph are described and defined using GraphQL Interface Definition Language (IDL). If you are new to GraphQL, or want to learn more about the basics of GraphQL development, you can refer to the GraphQL API section.

Define entities

Just like the database tables of traditional database design and the object design of object-oriented compilation, the design of entities is also closely related to the business. Therefore, before defining the entities, you must have a sorting and correlation analysis of the business of the DApp and the data to be analyzed.

Since all queries are based on the data model entities defined in the subgraph, the index of the subgraph itself is also based on the entity. Therefore, the definition of entities is based on the needs of DApps, so when we define entities, we consider entities as objects that contain data, not just from the perspective of events and smart contract functions.

When various entity types are defined in schema.graphql, then HyperGraph will query an instance or collection of a certain entity and generate fields. When defining entities, each type of entity that should be an entity must be annotated with the @entity modifier.

Take two examples of entity definitions, and take the example items in the previous section as examples, one recommended and one not recommended:

First look at the event definition:

{
"anonymous": false,
"inputs": [
{ "indexed": false, "name": "total", "type": "uint256" },
{ "indexed": false, "name": "tokenAddress", "type": "address" }
],
"name": "Packetstarted",
"type": "event"
}

The Packetstarted event is recorded on the chain after the red envelope (airdrop plan) is successfully set. There are two parameters, one total records the number of tokens to be sent, and tokenAddress is the token sent.

A good practice is to define a Packet. In order to count the total amount of a certain Token issued, a PacketToken is also defined.

type Packet @entity {
id: ID!
count: BigInt!
total: BigInt! # uint256
tokenAddress: Bytes! # address
}
type PacketToken @entity {
id: ID!
total: BigInt! # uint256
}

The bad practice is to define the event as an entity intact, such as:

type Packetstarted @entity {
id: ID!
count: BigInt!
total: BigInt! # uint256
tokenAddress: Bytes! # address
}

According to actual business needs, instead of defining events or calls 1:1 as entities in shema.graphql.

Last updated