Introduction
What we will do here is to write a query to list all the transaction for a given Polkadot address as seen in the screenshot below.
Pre-requisites
Have gone through the SubQuery Hello World exercise. This ensures your environment has all the required applications.
Step 1: Run subql init
This will set up the scaffolding making everything a lot easier.
~/Code/subQuery/projects$ subql init
Project name [subql-starter]: subql-list-transactions
Git repository:
RPC endpoint [wss://polkadot.api.onfinality.io/public-ws]:
Authors: sa
Description:
Version: [1.0.0]:
License: [Apache-2.0]:
Init the starter package... subql-list-transactions is ready
~/Code/subQuery/projects$
Step 2: Create a schema
The first step is to create your schema file which “defines the shape of your data”. Here we will define 2 entities or objects, Account and Transfer.
type Account @entity {
id: ID!
sentTransfers: [Transfer] @derivedFrom(field: "from")
receivedTransfers: [Transfer] @derivedFrom(field: "to")
}
type Transfer @entity {
id: ID!
amount: BigInt
to: Account!
from: Account!
}
Step 3: Update the manifest file
In the manifest file, we want to create a handler called handleTransfer (to be written next). This is a standard substrate/Eventhandler and we will filter on the balances module. The filter is optional but recommended as they significantly reduce the amount of data processed by your SubQuery project and will improve indexing performance.
specVersion: 0.0.1
description: ""
repository: ""
schema: ./schema.graphql
network:
endpoint: wss://polkadot.api.onfinality.io/public-ws
dataSources:
- name: main
kind: substrate/Runtime
startBlock: 6716560
mapping:
handlers:
- handler: handleTransfer
kind: substrate/EventHandler
filter:
module: balances
method: Transfer
Step 4: Writing the mappings
This is perhaps the most challenging part for those still learning SubQuery and Polkdadot. What the code below does is create a helper function called “ensureAccounts” and a handleTransfer EventHandler function to grab the transferred amount along with the to and from Id.
import {SubstrateExtrinsic,SubstrateEvent,SubstrateBlock} from "@subql/types";
import {Transfer, Account} from "../types";
import {Balance} from "@polkadot/types/interfaces";
async function ensureAccounts(accountIds: string[]): Promise<void> {
for (const accountId of accountIds) {
const account = await Account.get(accountId);
if (!account) {
await new Account(accountId).save();
}
}
}
export async function handleTransfer(event: SubstrateEvent): Promise<void> {
const {
event: {
data: [from, to, amount],
},
} = event;
await ensureAccounts([from.toString(), to.toString()]);
const transferInfo = new Transfer(
`${event.block.block.header.number.toNumber()}-${event.idx}`,
);
transferInfo.fromId = from.toString();
transferInfo.toId = to.toString();
transferInfo.amount = (amount as Balance).toBigInt();
await transferInfo.save();
}
Step 5: Build and deploy
Run the standard commands.
yarn codegen
yarn build
docker-compose pull & docker-compose up
Step 6: Query
Now comes the fun part, the query. Below is an example query that can be used.
query {
accounts (first:15 orderBy:CREATED_AT_DESC filter:{id: {equalTo: "12qvDzHzs3LFBnir7UAYR154t4ZWNLjMehniqLhTp2CBCeW4"}} ) {
nodes {
id
createdAt
updatedAt
recievedTransfers {
edges {
node {
id
amount
}
}
}
}
}
transfers ( orderBy:CREATED_AT_DESC filter:{id: {equalTo: "12qvDzHzs3LFBnir7UAYR154t4ZWNLjMehniqLhTp2CBCeW4"}} ) {
nodes {
id
createdAt
updatedAt
amount
toId
fromId
}
}
}
This will return:
{
"data": {
"accounts": {
"nodes": [
{
"id": "12qvDzHzs3LFBnir7UAYR154t4ZWNLjMehniqLhTp2CBCeW4",
"createdAt": "2021-09-07T00:01:15.719+00:00",
"updatedAt": "2021-09-07T00:01:15.719+00:00",
"recievedTransfers": {
"edges": [
{
"node": {
"id": "6716562-3",
"amount": "20000000000"
}
},
{
"node": {
"id": "6716591-1",
"amount": "700000000000"
}
}
]
}
}
]
},
"transfers": {
"nodes": []
}
}
}
And this matches subscan.