Skip to main content

Graph Node

The Graph is a decentralized protocol designed for indexing and querying blockchain data, including Avalanche. It enables developers to efficiently access and organize blockchain data using a GraphQL API. A key component of this protocol is the Graph Node, which processes and indexes data from the blockchain based on predefined subgraph configurations. For detailed guidance on setting up and using a Graph Node, refer to the Graph Node Documentation.

note

For this tutorial, we will use the local inventory from the Ansible Avalanche Getting Started repository. Also ensure you have already created a Subnet and a blockchain with a smart contract to index; see Subnet Creation and Smart Contract Deployment for details.

tip

Always make sure you have the latest version of the collection installed. See Installation and Upgrade.

Prerequisites

Before starting, ensure you have the following:

Requirements

The ash.avalanche.graph_node role relies on Docker, and the playbook ash.avalanche.install_graph_node installs Docker via the geerlingguy.docker role.

Install the required role locally with:

ansible-galaxy install -r ansible_collections/ash/avalanche/requirements.yml

To create and deploy a subgraph, you will also need to install the graph-cli tool. You can do this using npm or yarn:

npm install -g @graphprotocol/graph-cli

or

yarn global add @graphprotocol/graph-cli

If you don't have Node.js installed, follow the instructions on the Node.js website to install it.

Configure Graph Node

The Graph Node will be installed as a Docker Compose service on the nodes of the graph_node Ansible group. In Ansible Avalanche Getting Started, it is the frontend node by default. You can change this by editing the hosts file.

Set the blockchain ID in graph_node.yml (the group_vars file associated with our hosts' group):

inventories/local/group_vars/graph_node.yml
graph_node_blockchain_id: 2dEmExGjJT6MouJRr1PqV4PSQEbScDAjKuPtT6pgqYR5xdUuac

The blockchain ID (2dEmExGjJT6MouJRr1PqV4PSQEbScDAjKuPtT6pgqYR5xdUuac in our case) should be the one created in the Subnet Creation tutorial.

Install Graph Node

Run the following command to install Docker and the Docker Compose service:

ansible-playbook ash.avalanche.install_graph_node -i inventories/local

Create a Graph Node Subgraph

A subgraph is a GraphQL schema that defines the data you want to index and how to query it. Follow these steps to create and deploy a subgraph:

Step 1: Set the GRAPH_NODE_IP Environment Variable

Since we are using a local network, set the GRAPH_NODE_IP environment variable to the IP address of the Graph Node (which is the same as the frontend node):

export GRAPH_NODE_IP=$(terraform -chdir=terraform/multipass output -raw frontend_ip)

Step 2: Create a Subgraph Directory

mkdir my-subgraph
cd my-subgraph

Step 3: Define the Subgraph Configuration

Create a subgraph.yaml file in the my-subgraph directory (don't forget to replace the address field with the address of your deployed smart contract):

subgraph.yaml
specVersion: 0.0.4
description: ASH Token on Subnet
repository: https://example.com
schema:
file: ./schema.graphql

dataSources:
- kind: ethereum/contract
name: ASHToken
network: subnet
source:
address: "0x11Fa7d827e18f28F4e3B2F4D4D8C5a62bCC8C5b3"
abi: ASHToken
startBlock: 0
mapping:
kind: ethereum/events
apiVersion: 0.0.7
language: wasm/assemblyscript
entities:
- Transfer
- Approval
abis:
- name: ASHToken
file: ./abis/ASH.json
eventHandlers:
- event: Transfer(indexed address,indexed address,uint256)
handler: handleTransfer
- event: Approval(indexed address,indexed address,uint256)
handler: handleApproval
file: ./src/mapping.ts
info
  • The network field should be set to subnet to indicate that you are indexing a Subnet.
  • The kind field should be set to ethereum/contract to indicate that you are indexing an Ethereum-compatible contract.
  • The name field should be set to a unique name for your subgraph. In this case, we are using ASHToken.
  • The description field should be a brief description of your subgraph.
  • The repository field should be a link to your subgraph's repository (optional).
  • The address field in the source section should be the address of your deployed smart contract. You can find this address in the output of the Smart Contract Deployment tutorial.
  • The startBlock field should be set to the block number where you want to start indexing. You can set it to 0 to index from the beginning.
  • The abi field should match the name of the ABI file you will create in the next step.
  • The entities field should include all the entities you want to index. In this case, we are indexing Transfer and Approval events.
  • The eventHandlers field should include all the events you want to handle. In this case, we are handling Transfer and Approval events.
  • The file field should point to the mapping file you will create in the next step.

Step 4: Define the GraphQL Schema

Create a schema.graphql file in the my-subgraph directory:

schema.graphql
type Transfer @entity(immutable: false) {
id: ID!
from: Bytes!
to: Bytes!
value: BigInt!
blockNumber: BigInt!
timestamp: BigInt!
}

type Approval @entity(immutable: false) {
id: ID!
owner: Bytes!
spender: Bytes!
value: BigInt!
blockNumber: BigInt!
timestamp: BigInt!
}

Step 5: Add ABI Files

The ABI (Application Binary Interface) is a JSON file that describes the smart contract's functions and events. It is used by the Graph Node to decode the data from the blockchain. Create an abis directory and add the ABI file for the smart contract you want to index.

Add the ABI content to ASH.json:

mkdir abis
ln ../ash_token/out/ASHToken.sol/ASHToken.json abis/ASH.json
note

In the example above, we are using the ASHToken contract from the Smart Contract Deployment tutorial. You can replace it with your own contract's ABI file.

Step 6: Add Mapping Functions

Create a src directory and add a mapping.ts file:

my-subgraph/src/mapping.ts
import {
Transfer as TransferEvent,
Approval as ApprovalEvent
} from "../generated/ASHToken/ASHToken"
import { Transfer, Approval } from "../generated/schema"

export function handleTransfer(event: TransferEvent): void {
let id = event.transaction.hash.concatI32(event.logIndex.toI32()).toHex()
let entity = new Transfer(id)
entity.from = event.params.from
entity.to = event.params.to
entity.value = event.params.value
entity.blockNumber = event.block.number
entity.timestamp = event.block.timestamp
entity.save()
}

export function handleApproval(event: ApprovalEvent): void {
let id = event.transaction.hash.concatI32(event.logIndex.toI32()).toHex()
let entity = new Approval(id)
entity.owner = event.params.owner
entity.spender = event.params.spender
entity.value = event.params.value
entity.blockNumber = event.block.number
entity.timestamp = event.block.timestamp
entity.save()
}

Step 7: Install Dependencies

Run the following command to install dependencies:

npm install --save-dev @graphprotocol/graph-cli @graphprotocol/graph-ts

Step 8: Generate TypeScript Types

This command generates the necessary TypeScript types based on your GraphQL schema and smart contract ABIs. These types are later used in the mapping file for strong typing.

graph codegen

Step 9: Build the Subgraph

Build your subgraph to ensure that all configurations, generated types, and mappings are correctly bundled.

graph build

Step 10: Create the Subgraph on the Graph Node

This command creates the subgraph on your Graph Node instance. Replace subgraph-name with your desired name.

graph create --node http://$GRAPH_NODE_IP:8020/ subgraph-name

Step 11: Deploy the Subgraph

Deploy your subgraph to the Graph Node using this command. This makes your subgraph available for indexing and querying. don't forget to replace subgraph-name with the name you used in the previous step.

graph deploy --node http://$GRAPH_NODE_IP:8020/ --ipfs http://$GRAPH_NODE_IP:5001/ subgraph-name -l v0.0.1

Step 12: Query the Subgraph

Use the GraphQL API at http://$GRAPH_NODE_IP:8000/subgraphs/name/subgraph-name to query your subgraph.

Example query:

{
transfers(first: 1) {
id
from
to
value
blockNumber
timestamp
}
}

output:

{
"data": {
"transfers": [
{
"id": "0xea6dc0b9e51e12753b069ec14256ba425ecdb2fd8bf1528acdd18e622bb444d700000000",
"from": "0x0000000000000000000000000000000000000000",
"to": "0x8db97c7cece249c2b98bdc0226cc4c2a57bf52fc",
"value": "200000000000000000000",
"blockNumber": "1",
"timestamp": "1745577540"
}
]
}
}