Running a subgraph locally
Tags: blockchain
Published: 2023-07-19 05:04:36

## Introduction

A subgraph extracts data from a blockchain, processing it and storing it so that it can be easily queried via GraphQL. For my use case, I'm currently using a subgraph to store title and markdown descriptions for proposals in a DAO governance application.

This post will provide instructions on how to setup a subgraph locally for development purposes. Most tutorials out there focus on creating a subgraph with testnet and mainnet but its advantageous to create subgraphs locally for easier debugging process.


## Required knowledge
1. Basic blockchain contract development
2. Docker
3. Nodejs

## Prerequisites

1. Docker
2. Node


## Contract deployment

1. Follow the steps here to create a new hardhat project. https://hardhat.org/tutorial/creating-a-new-hardhat-project
2. Delete Lock.sol
3. Add Greeting.sol and paste in the code below.

```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Greeting {
    string public greeting;

    event GreetingChanged(string newGreeting, address changedBy);

    constructor(string memory initialGreeting) {
        greeting = initialGreeting;
    }

    function setGreeting(string memory newGreeting) public {
        greeting = newGreeting;
        emit GreetingChanged(newGreeting, msg.sender);
    }

    function getGreeting() public view returns (string memory) {
        return greeting;
    }
}

```

The above code creates a basic CRU contract for a greeting text. Whenever the greeting changed, the event GreetingChanged is emitted. We will be capturing the event emitted everytime a new greeting is set and store this in our subgraph.


4. Replace the code in deploy.ts with the code below

```
import { ethers } from "hardhat";

async function main() {

  const Greeting= await ethers.getContractFactory("Greeting");
  const greeting = await Greeting.deploy("hello");

  await greeting.deployed();

  
  console.log(
    `deployed to ${greeting.address} with blockNumber ${greeting.deployTransaction.blockNumber}                                                     `
  );
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
``` 
At point of interest hre would be the `greeting.deployTransaction.blockNumber` which will tell subgraph at which blocknumber will is start indexing data.

5. Modify `hardhat.config.js` networks object and add
```
    localhost: {
      chainId: 1337,
      url:"http://0.0.0.0:8545"
    },
```
6. Run `npx hardhat node --hostname 0.0.0.0`. The `0.0.0.0` is important so that the subgraph that we deploy using docker will be able  to connect to our local hardhat node

7. Run `npx hardhat compile`
8. Run `npx hardhat run --network localhost scripts/deploy.ts`
9. Copy and save the address of the deployed contract and the blocknumber

## Subgraph Deployment
1. Clone this repo `git clone https://github.com/hashgraph/hedera-subgraph-example.git`
2. Replace docker-compose.yaml with the code below

```
version: '3'
services:
  graph-node:
    image: graphprotocol/graph-node
    ports:
      - '8000:8000'
      - '8001:8001'
      - '8020:8020'
      - '8030:8030'
      - '8040:8040'
    depends_on:
      - ipfs
      - postgres
    extra_hosts:
      - host.docker.internal:host-gateway
    environment:
      postgres_host: postgres
      postgres_user: graph-node
      postgres_pass: let-me-in
      postgres_db: graph-node
      ipfs: 'ipfs:5001'
      ethereum: 'localhost:http://host.docker.internal:8545'
      GRAPH_LOG: info
  ipfs:
    image: ipfs/go-ipfs:latest
    ports:
      - '5001:5001'
    volumes:
      - ./data/ipfs:/data/ipfs
  postgres:
    image: postgres
    ports:
      - '5432:5432'
    command:
      [
        "postgres",
        "-cshared_preload_libraries=pg_stat_statements"
      ]
    environment:
      POSTGRES_USER: graph-node
      POSTGRES_PASSWORD: let-me-in
      POSTGRES_DB: graph-node
      # FIXME: remove this env. var. which we shouldn't need. Introduced by
      # <https://github.com/graphprotocol/graph-node/pull/3511>, maybe as a
      # workaround for https://github.com/docker/for-mac/issues/6270?
      PGDATA: "/var/lib/postgresql/data"
      POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
```

Worth taking note here are the following:

- The node connects to `localhost:http://host.docker.internal:8545` which is our locally dpeloyed hardhat node
- We are using the latest ipfs docker image because of some compatibility issues with Mac M1
3. Run `docker-compose up` to startup your local Graph Node server. Subgraphs deployed in our Grap Node would have its own GraphQL endpoint. You should see multiple calls made by the Graph Node server in your hardhat node logs similar to below



![Hardhat Logs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jdflngjbddwmxw060w9w.png)





4. Copy the Greeting ABI from '/artifacts/contracts/Greeting.sol/Greeting.json` in our Hardhat project
5. Create a new file called Greeting.json inside the abis folder of our subgraph project
6. Replace the code in mapping.ts with the code in this
[gist](https://gist.github.com/iandjx/8319e0dc4645427749779749a93f8792)


Dont worry of the import errors for now. We will generate the schema and other related code later on
7. Replace the code in `schema.graphql` with the code from this [gist](https://gist.github.com/iandjx/d3fdd88f9af133123fbc4c6be3fe262d)

This would mean that we will store Greeting objects in our subgraph.
8. Update the subgraph.template.yaml with the code from this [gist](https://gist.github.com/iandjx/f2a56ba0e9bccf20554818386391660b)

* replace `source: address` and `starbBock` with the address and blocknumber that we copied earlier after deploying the Greeting contract

This file will tell the Graph Node server the following:

- Deploy our subgraph in the local node. 
- This subgraph will subscribe to events coming from the stated address in `source: address`
- Whenever the event `GreetingChanged(string,address) is emitted. Pass the event object to the handler `handleGreetingChanged` which we created earlier
9.Update `testnet.json` with the same address and startblock we defined earlier
```
{
    "startBlock": 1,
    "Greeting": "0x5FbDB2315678afecb367f032d93F642f64180aa3"
}
```
10. Run `npm i`
11. Run `npm run compile`
12. Run `npm run create-local`
13. Run `npm run deploy-local`. You should see something like this once you have deployed successfully
```
Build completed: Qmerhoqakv6BuiWyhSjSpQ7a5dVK92QUZVxtgnSFUXsA8s

Deployed to http://localhost:8000/subgraphs/name/Greeter/graphql

Subgraph endpoints:
Queries (HTTP):     http://localhost:8000/subgraphs/name/Greeter
```

## Testing the subgraph
1. Create a new node project
2. Run `npm i ethers`
3. Create an `index.js` file and copy paste the code below
```
const ethers = require("ethers");

const provider = new ethers.JsonRpcProvider(
  "http://127.0.0.1:8545"
);

const signer = new ethers.Wallet("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", provider);

const ABI = [
    {
      "inputs": [
        {
          "internalType": "string",
          "name": "initialGreeting",
          "type": "string"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "constructor"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "string",
          "name": "newGreeting",
          "type": "string"
        },
        {
          "indexed": false,
          "internalType": "address",
          "name": "changedBy",
          "type": "address"
        }
      ],
      "name": "GreetingChanged",
      "type": "event"
    },
    {
      "inputs": [],
      "name": "getGreeting",
      "outputs": [
        {
          "internalType": "string",
          "name": "",
          "type": "string"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "greeting",
      "outputs": [
        {
          "internalType": "string",
          "name": "",
          "type": "string"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "string",
          "name": "newGreeting",
          "type": "string"
        }
      ],
      "name": "setGreeting",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }
  ];

const Greeting = new ethers.Contract(
  "0x5FbDB2315678afecb367f032d93F642f64180aa3",
 ABI,
  signer
);

const updateGreeting= async () => {

const tx = await Greeting.setGreeting("hello world")
const receipt = await tx.wait()  
  console.log(receipt.logs)

  
}

updateGreeting()
```

- Replace the wallet private key with one of the private keys from the hardhat node.
- Replace the Greeting contract address with the deployed address of our Greeting contract
4. Run `node index.js`
5. Once successful, go to `http://localhost:8000/subgraphs/name/Greeter`
6. Run this query
```
query MyQuery {
  greetings {
    id
    changedBy
    newGreeting
  }
}
```
7. You should be able to see one greeting object displayed as the query result


![GQL Query Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72mz0pgi62kllzj594fz.png)


Congratulations you have now deployed and tested a subgraph locally.