Module 2: SubQuery Basics
Module 2: SubQuery Basics
Lesson 1: The Manifest File
Documentation reference
Lesson 2: The Schema File
Documentation reference
Lesson 3: The Mappings File - Block Handler
Documentation reference
Lesson 4: The Mappings File - Event Handler
Documentation reference
Lesson 5: The Mappings File - Call Handler
Documentation reference
Exercises
Pre-requisites
- Completion of Module 1
Account Balances
In this exercise, we will take the starter project and focus on using an event handler to extract the balance of each account.
High level steps
- Initialise the starter project
- Update your mappings, manifest file and graphql schema file by removing all the default code except for the handleEvent function.
- Generate, build and deploy your code
- Deploy your code in Docker
- Query for address balances in the playground
Detailed steps
Step 1: Initialise your project
The first step in creating a SubQuery project is to create a project with the following command:
~/Code/subQuery$ subql init
Project name [subql-starter]: account-balance
? Select a network Polkadot
? Select a template project subql-starter Starter project
for subquery
Cloning project... done
RPC endpoint: [wss://polkadot.api.onfinality.io/public-ws]:
Git repository [https://github.com/subquery/subql-starter]:
Fetching network genesis hash... done
Author [Ian He & Jay Ji]:
Description [This project can be use as a starting po...]:
Version [0.0.4]:
License [MIT]:
Preparing project... done
account-balance is ready
Step 2: Update the graphql schema
The default schema.graphql file contains 5 fields. Rename field2 to account and field3 to balance. Rename the entity to Account.
Extra: Whenever you update the manifest file, don’t forget to update the reference to field1 in the mappings file appropriately and to regenerate the code via yarn codegen.
The schema file should look like this:
type Account @entity {
id: ID! #id is a required field
account: String #This is a Polkadot address
balance: BigInt # This is the amount of DOT
}
Step 3: Update the manifest file (aka project.yaml)
The initialisation command also pre-creates a sample manifest file and defines 3 handlers. Because we are only focusing on Events, let’s remove handleBlock and handleCall from the mappings file. The manifest file should look like this:
specVersion: 0.2.0
name: account-balance
version: 0.0.4
description: >-
This project can be use as a starting point for developing your SubQuery
project
repository: 'https://github.com/subquery/subql-starter'
schema:
file: ./schema.graphql
network:
endpoint: 'wss://polkadot.api.onfinality.io/public-ws'
genesisHash: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3'
dataSources:
- kind: substrate/Runtime
startBlock: 1
mapping:
file: ./dist/index.js
handlers:
- handler: handleEvent
kind: substrate/EventHandler
filter:
module: balances
method: Deposit
Step 4: Update the mappings file
The initialisation command pre-creates a sample mappings file with 3 functions, handleBlock, handleEvent and handleCall. Again, as we are only focusing on handleEvent, let’s delete the remaining functions.
We also need to make a few other changes. Because the Account entity (formally called the StarterEntity), was instantiated in the handleBlock function and we no longer have this, we need to instantiate this within our handleEvent function. We also need to update the argument we pass to the constructor.
let record = new Account(event.extrinsic.block.block.header.hash.toString());
The mappingHandler.ts file should look like this:
import {SubstrateEvent} from "@subql/types";
import {Account} from "../types";
import {Balance} from "@polkadot/types/interfaces";
export async function handleEvent(event: SubstrateEvent): Promise<void> {
const {event: {data: [account, balance]}} = event;
//Create a new Account entity with ID using block hash
let record = new Account(event.extrinsic.block.block.header.hash.toString());
// Assign the Polkadot address to the account field
record.account = account.toString();
// Assign the balance to the balance field "type cast as Balance"
record.balance = (balance as Balance).toBigInt();
await record.save();
}
Step 5: Install the dependencies
Install the node dependencies by running the following commands:
yarn install
OR
npm install
Step 6: Generate the associated typescript
Next, we will generate the associated typescript with the following command:
yarn codegen
OR
npm run-script codegen
Step 7: Build the project
The next step is to build the project with the following command:
yarn build
OR
npm run-script build
This bundles the app into static files for production.
Step 8: Start the Docker container
Run the docker command to pull the images and to start the container.
docker-compose pull && docker-compose up
Step 9: Run a query
Once the docker container is up and running, which could take a few minutes, open up your browser and navigate to www.localhost:3000.
This will open up a “playground” where you can create your query. Copy the example below.
query {
accounts(first:10 orderBy:BALANCE_DESC){
nodes{
account
balance
}
}
}
This should return something similar to the following:
{
"data": {
"accounts": {
"nodes": [
{
"account": "13wY4rD88C3Xzd4brFMPkAMEMC3dSuAR2NC6PZ5BEsZ5t6rJ",
"balance": "162804160"
},
{
"account": "146YJHyD5cjFN77HrfKhxUFbU8WjApwk9ncGD6NbxE66vhMS",
"balance": "130775360"
},
{
"account": "146YJHyD5cjFN77HrfKhxUFbU8WjApwk9ncGD6NbxE66vhMS",
"balance": "130644160"
},
{
"account": "146YJHyD5cjFN77HrfKhxUFbU8WjApwk9ncGD6NbxE66vhMS",
"balance": "117559360"
},
{
"account": "12H7nsDUrJUSCQQJrTKAFfyCWSactiSdjoVUixqcd9CZHTGt",
"balance": "117359360"
},
{
"account": "146YJHyD5cjFN77HrfKhxUFbU8WjApwk9ncGD6NbxE66vhMS",
"balance": "108648000"
},
{
"account": "13wY4rD88C3Xzd4brFMPkAMEMC3dSuAR2NC6PZ5BEsZ5t6rJ",
"balance": "108648000"
},
{
"account": "12zSBXtK9evQRCG9Gsdr72RbqNzbNn2Suox2cTfugCLmWjqG",
"balance": "108648000"
},
{
"account": "15zF7zvdUiy2eYCgN6KWbv2SJPdbSP6vdHs1YTZDGjRcSMHN",
"balance": "108448000"
},
{
"account": "15zF7zvdUiy2eYCgN6KWbv2SJPdbSP6vdHs1YTZDGjRcSMHN",
"balance": "108448000"
}
]
}
}
}
What we have done here is queried for the balance of DOT tokens for all addresses (accounts) on the Polkadot mainnet blockchain. We have limited this to the first 10 and sorted it by the “richest” account holders first.
Bonus
As a bonus, try to aggregate the balances across addresses so you can find the total balance of an address.
##E References