Skip to main content

Lesson 2: Getting started with SubQuery

SubQuery TeamAbout 3 min

Lesson 2: Getting started with SubQuery

It’s the high time we start coding. Let’s build our first SubQuery project with the help of SubQuery CLI!

In this lesson, we will:

  • Initialise the project through SubQuery CLI
  • Get familiar with the project structure
  • Index EVM events
  • Deploy the project locally with docker

1. Preparation

Environment and dependencies

Installation of the SubQuery CLI

Install SubQuery CLI globally on your terminal with NPM:

npm install -g @subql/cli

Important

Note that we don't encourage the use of yarn global for installing @subql/cli due to its poor dependency management. This may lead to multiple errors.

Then check the subql cli version by running the given below command:

subql -v

Run help to see available commands provided by SubQuery CLI:

subql help

2. Initialise the SubQuery Starter Project

Inside the directory in which you want to create the SubQuery project run the following command and follow all the steps chosing project name, GitHub repo addres, network family, rpc endpoint and more. Follow steps from the video.

Find out more about initialisation of SubQuery projects in our Quick Start Guide.

subql init project-name

Note

Remember that, everything in the project configuration can by changed afterwards as well.

Then you should see a folder with your project name has been created inside the directory, you can use this as the start point of your project. And the files should be identical as in the Directory Structure.

3. SubQuery Project Structure

If you want to change your project and go beyond the default content of any starter project built with SubQuery CLI, you will need to work on the following files:

  • The Manifest in project.yaml to configure your project.
  • The GraphQL Schema in schema.graphql to define shape of the data.
  • The Mapping functions in src/mappings/ directory to transform data coming from blockchain.

Find out more about Manifest and GraphQl Schema and Mapping functions in our documentation.

Note

In this first lesson there is no code alternation.

After the initialisation of the starter project your files should look like this:

Manifest

specVersion: 1.0.0
name: moonbeam-evm-starter
version: 0.0.1
runner:
  node:
    name: "@subql/node"
    version: ">=0.35.0"
  query:
    name: "@subql/query"
    version: ">=0.16.0"
description: Moonbeam / SubQuery Course — Building dApps with the help of SubQuery
repository: "https://github.com/subquery/tutorials-frontier-evm-starter"
schema:
  file: ./schema.graphql
network:
  chainId: "0x401a1f9dca3da46f5c4091016c8a2f26dcea05865116b286f60f668207d1474b"
  endpoint: "wss://moonriver.api.onfinality.io/public-ws"
  dictionary: "https://api.subquery.network/sq/subquery/moonriver-dictionary"
  chaintypes:
    file: ./dist/chaintypes.js
dataSources:
  - kind: substrate/FrontierEvm
    startBlock: 752073
    processor:
      file: ./node_modules/@subql/frontier-evm-processor/dist/bundle.js
      options:
        abi: erc20
        address: "0x6bd193ee6d2104f14f94e2ca6efefae561a4334b"
    assets:
      erc20:
        file: ./erc20.abi.json
    mapping:
      file: ./dist/index.js
      handlers:
        - handler: handleFrontierEvmEvent
          kind: substrate/FrontierEvmEvent
          filter:
            topics:
              - "Transfer(address indexed from,address indexed to,uint256 value)"
              - null
              - null
              - null
        - handler: handleFrontierEvmCall
          kind: substrate/FrontierEvmCall
          filter:
            function: "approve(address to,uint256 value)"

Schema GraphQl

type Transaction @entity {
  id: ID! # Transaction hash
  value: BigInt!
  to: String!
  from: String!
  contractAddress: String!
}

type Approval @entity {
  id: ID! # Transaction hash
  value: BigInt!
  owner: String!
  spender: String!
  contractAddress: String!
}

Mapping functions

import { Approval, Transaction } from "../types";
import {
  FrontierEvmEvent,
  FrontierEvmCall,
} from "@subql/frontier-evm-processor";
import { BigNumber } from "ethers";

// Setup types from ABI
type TransferEventArgs = [string, string, BigNumber] & {
  from: string;
  to: string;
  value: BigNumber;
};
type ApproveCallArgs = [string, BigNumber] & {
  _spender: string;
  _value: BigNumber;
};

export async function handleFrontierEvmEvent(
  event: FrontierEvmEvent<TransferEventArgs>,
): Promise<void> {
  const transaction = new Transaction(event.transactionHash);

  transaction.value = event.args.value.toBigInt();
  transaction.from = event.args.from;
  transaction.to = event.args.to;
  transaction.contractAddress = event.address;

  await transaction.save();
}

export async function handleFrontierEvmCall(
  event: FrontierEvmCall<ApproveCallArgs>,
): Promise<void> {
  const approval = new Approval(event.hash);

  approval.owner = event.from;
  approval.value = event.args._value.toBigInt();
  approval.spender = event.args._spender;
  approval.contractAddress = event.to;

  await approval.save();
}

4. Building the Project

Install dependencies

Under the project directory, install the node dependencies by running the following command:

yarn
yarn install

Generate Associated Typescript

Next, generate the associated typescript with the following command:

yarn
yarn codegen

Build the Project

Next, use following command to bundle the app into static files for production:

yarn
yarn build

5. Indexing and Query

Run Docker

Under the project directory run following command:

yarn start:docker

This will download packages from Docker, create a new Postgres database, and start an indexing an query service.

Note

When you are doing it for the first time, it may take some time to start.

Query this Project

Open your browser and head to http://localhost:3000. You should see a GraphQL playground is showing in the explorer and the schemas that ready to query. With this project can try to query with the following code to get a taste of how it works.

query {
  approvals(first: 5) {
    nodes {
      id
      value
      owner
      spender
    }
  }
  transactions(first: 5) {
    nodes {
      id
      value
      to: id
      from: id
    }
  }
}

Useful resources

Note

The project's code state after this lesson is hereopen in new window. The final code of this project can be found hereopen in new window.