├── .gitignore ├── README.md ├── docker-compose.yml ├── package.json ├── project.yaml ├── schema.graphql ├── src ├── index.ts └── mappings │ └── mappingHandlers.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # These are some examples of commonly ignored file patterns. 2 | # You should customize this list as applicable to your project. 3 | # Learn more about .gitignore: 4 | # https://www.atlassian.com/git/tutorials/saving-changes/gitignore 5 | 6 | # Node artifact files 7 | node_modules/ 8 | dist/ 9 | 10 | # lock files 11 | yarn.lock 12 | package-lock.json 13 | 14 | # Compiled Java class files 15 | *.class 16 | 17 | # Compiled Python bytecode 18 | *.py[cod] 19 | 20 | # Log files 21 | *.log 22 | 23 | # Package files 24 | *.jar 25 | 26 | # Maven 27 | target/ 28 | dist/ 29 | src/types 30 | 31 | # JetBrains IDE 32 | .idea/ 33 | 34 | # Unit test reports 35 | TEST*.xml 36 | 37 | # Generated by MacOS 38 | .DS_Store 39 | 40 | # Generated by Windows 41 | Thumbs.db 42 | 43 | # Applications 44 | *.app 45 | *.exe 46 | *.war 47 | 48 | # Large media files 49 | *.mp4 50 | *.tiff 51 | *.avi 52 | *.flv 53 | *.mov 54 | *.wmv 55 | 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is SubQuery? 2 | 3 | SubQuery powers the next generation of Polkadot dApps by allowing developers to extract, transform and query blockchain data in real time using GraphQL. In addition to this, SubQuery provides production quality hosting infrastructure to run these projects in. 4 | 5 | # SubQuery Example - Account balance 6 | 7 | This subquery example indexes the deposit balance of each account and it is an example of an EventHandler. By processing substrate events with the use of the "balances/Deposit" mapping filter, we can obtain account and the DOT balance of the account. 8 | 9 | # Getting Started 10 | 11 | ### 1. Clone the entire subql-example repository 12 | 13 | ```shell 14 | git clone https://github.com/subquery/tutorials-account-balances.git 15 | 16 | ``` 17 | 18 | ### 2. Install dependencies 19 | 20 | ```shell 21 | cd tutorials-account-balance 22 | yarn install 23 | ``` 24 | 25 | ### 3. Generate types 26 | 27 | ```shell 28 | yarn codegen 29 | ``` 30 | 31 | ### 4. Build the project 32 | 33 | ```shell 34 | yarn build 35 | ``` 36 | 37 | ### 5. Start Docker 38 | 39 | ```shell 40 | docker-compose pull & docker-compose up 41 | ``` 42 | 43 | ### 6. Run locally 44 | 45 | Open http://localhost:3000/ on your browser 46 | 47 | ### 7. Example query to run 48 | 49 | ```shell 50 | query { 51 | accounts(first:10 orderBy:BALANCE_DESC){ 52 | nodes{ 53 | account 54 | balance 55 | } 56 | } 57 | } 58 | ``` 59 | 60 | # Understanding this project 61 | 62 | This project has a function called handleEvent. It uses an [EventHandler](https://doc.subquery.network/create/mapping#event-handler) which is defined in the [manifest file](https://doc.subquery.network/create/manifest.html) (project.yaml) as "kind: substrate/EventHandler" 63 | 64 | The [schema.graphql](https://doc.subquery.network/create/graphql.html) file defines the variables blockHeight which is mandatory and of type Int. 65 | 66 | If we examine the function handleEvent in more detail, you can see that this function takes one argument of type SubstrateEvent. It then creates a new instance of Account passing in the event.extrinsic.block.block.header.hash argument as a string and assigning this to the variable record. 67 | 68 | Next, the account is converted to a string via toString() and assigned to record.account. The balance is type cast as Balance and converted to a big integer. 69 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.1" 2 | 3 | services: 4 | postgres: 5 | image: postgres:12-alpine 6 | ports: 7 | - 5432:5432 8 | volumes: 9 | - .data/postgres:/var/lib/postgresql/data 10 | environment: 11 | POSTGRES_PASSWORD: postgres 12 | healthcheck: 13 | test: ["CMD-SHELL", "pg_isready -U postgres"] 14 | interval: 5s 15 | timeout: 5s 16 | retries: 5 17 | 18 | subquery-node: 19 | image: onfinality/subql-node:latest 20 | depends_on: 21 | "postgres": 22 | condition: service_healthy 23 | restart: always 24 | environment: 25 | DB_USER: postgres 26 | DB_PASS: postgres 27 | DB_DATABASE: postgres 28 | DB_HOST: postgres 29 | DB_PORT: 5432 30 | volumes: 31 | - ./:/app 32 | command: 33 | - -f=/app 34 | - --db-schema=app 35 | healthcheck: 36 | test: ["CMD", "curl", "-f", "http://subquery-node:3000/ready"] 37 | interval: 3s 38 | timeout: 5s 39 | retries: 10 40 | 41 | graphql-engine: 42 | image: onfinality/subql-query:latest 43 | ports: 44 | - 3000:3000 45 | depends_on: 46 | "postgres": 47 | condition: service_healthy 48 | "subquery-node": 49 | condition: service_healthy 50 | restart: always 51 | environment: 52 | DB_USER: postgres 53 | DB_PASS: postgres 54 | DB_DATABASE: postgres 55 | DB_HOST: postgres 56 | DB_PORT: 5432 57 | command: 58 | - --name=app 59 | - --playground 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "account-balance", 3 | "version": "1.0.0", 4 | "description": "A tutorial for querying account balances on polkadot", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "./node_modules/.bin/subql build", 8 | "prepack": "rm -rf dist && npm build", 9 | "test": "jest", 10 | "codegen": "./node_modules/.bin/subql codegen" 11 | }, 12 | "homepage": "https://github.com/subquery/subql-starter", 13 | "repository": "github:subquery/subql-starter", 14 | "files": [ 15 | "dist", 16 | "schema.graphql", 17 | "project.yaml" 18 | ], 19 | "author": "sa", 20 | "license": "Apache-2.0", 21 | "devDependencies": { 22 | "@polkadot/api": "^9", 23 | "@subql/types": "latest", 24 | "typescript": "4.4.4", 25 | "@subql/cli": "latest" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /project.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 1.0.0 2 | name: account-balance 3 | version: 1.0.0 4 | runner: 5 | node: 6 | name: '@subql/node' 7 | version: '*' 8 | query: 9 | name: '@subql/query' 10 | version: '*' 11 | description: A tutorial for querying account balances on polkadot 12 | repository: https://github.com/subquery/tutorials-account-balances 13 | schema: 14 | file: ./schema.graphql 15 | network: 16 | chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3' 17 | endpoint: wss://polkadot.api.onfinality.io/public-ws 18 | dictionary: https://api.subquery.network/sq/subquery/polkadot-dictionary 19 | dataSources: 20 | - kind: substrate/Runtime 21 | startBlock: 1 22 | mapping: 23 | file: ./dist/index.js 24 | handlers: 25 | - handler: handleEvent 26 | kind: substrate/EventHandler 27 | filter: 28 | module: balances 29 | method: Deposit 30 | -------------------------------------------------------------------------------- /schema.graphql: -------------------------------------------------------------------------------- 1 | type Account @entity { 2 | id: ID! #id is a required field 3 | account: String #This is a Polkadot address 4 | balance: BigInt # This is the amount of DOT 5 | } 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import "@polkadot/api-augment"; 2 | //Exports all handler functions 3 | export * from "./mappings/mappingHandlers"; 4 | -------------------------------------------------------------------------------- /src/mappings/mappingHandlers.ts: -------------------------------------------------------------------------------- 1 | import {SubstrateEvent} from "@subql/types"; 2 | import {Account} from "../types"; 3 | import {Balance} from "@polkadot/types/interfaces"; 4 | 5 | export async function handleEvent(event: SubstrateEvent): Promise { 6 | const {event: {data: [account, balance]}} = event; 7 | //Create a new Account entity with ID using block hash 8 | let record = new Account(event.extrinsic.block.block.header.hash.toString()); 9 | // Assign the Polkadot address to the account field 10 | record.account = account.toString(); 11 | // Assign the balance to the balance field "type cast as Balance" 12 | record.balance = (balance as Balance).toBigInt(); 13 | await record.save(); 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "esModuleInterop": true, 6 | "declaration": true, 7 | "importHelpers": true, 8 | "resolveJsonModule": true, 9 | "module": "commonjs", 10 | "outDir": "dist", 11 | "rootDir": "src", 12 | "target": "es2017" 13 | }, 14 | "include": [ 15 | "src/**/*", 16 | "node_modules/@subql/types/dist/global.d.ts" 17 | ] 18 | } 19 | --------------------------------------------------------------------------------