Simple Substrate squid
Objective
The goal of this tutorial is to guide you through creating a simple blockchain indexer (“squid”) using Squid SDK. In this example we will query the Crust storage network. Our objective will be to observe which files have been added and deleted from the network. Additionally, our squid will be able to tell us the groups joined and the storage orders placed by a given account. We will start with thesubstrate squid template, then go on to run the project, define a schema, and generate TypeScript interfaces. From there, we will be able to interact directly with SQD Network, using the and the metadata service to get a Crust types bundle.
We expect that experienced software developers should be able to complete this tutorial in around 10-15 minutes.
Pre-requisites
- Familiarity with Git
- A properly set up development environment consisting of Node.js, Git and Docker
- Squid CLI
Scaffold with sqd init
Use sqd init and come up with some unique name for your squid. This tutorial will index data on Crust, a Substrate-based network, so use the substrate template:
Run the project
Now you can follow the quickstart guide to get the project up and running. Here is a summary:Define the schema and generate entity classes
Next, we make changes to the data schema of the squid and define entities that we would like to track. We are interested in:- Files added to and deleted from the chain;
- Active accounts;
- Groups joined by accounts;
- Storage orders placed by accounts.
schema.graphql:
schema.graphql
Account entity is almost completely derived. It is there to tie the other three entities together.
To finalize this step, run the codegen tool:
src/model/generated folder of the project.
Generate TypeScript wrappers for events
We generate these using the squid-substrate-typegen tool. Its configuration file istypegen.json; there, we need to
- Set the
"specVersions"field to a valid source of Crust chain runtime metadata. We’ll use an URL of SQD-maintained metadata service: - List all Substrate pallets we will need the data from. For each pallet we list all events, calls, storage items and constants needed.
Refer to this note if you are unsure what Substrate data to use in your project.
typegen.json looks like this:
typegen.json
src/types.
Set up the processor object
The next step is to create aSubstrateBatchProcessor object which subscribes to all the events we need. We do it at src/processor.ts:
src/processor.ts
- Uses SQD Network as its main data source and a chain RPC for real-time updates. URLs of the SQD Network gateways are available on this page and via
sqd gateways. See this page for the reference on data sources configuration; - Subscribes to
Market.FileSuccess,Swork.JoinGroupSuccessandSwork.WorksReportSuccessevents emitted at heights starting at 583000; - Additionally subscribes to calls that emitted the events and the corresponding extrinsics;
- Requests the
hashdata field for all retrieved extrinsics and thetimestampfield for all block headers.
ProcessorContext type to be able to pass the sole argument of the batch handler function around safely.
Define the batch handler
Squids batch process chain data from multiple blocks. Compared to the handlers approach this results in a much lower database load. Batch processing is fully defined by processor’s batch handler, the callback supplied to theprocessor.run() call at the entry point of each processor (src/main.ts by convention).
We begin defining our batch handler by importing the entity model classes and Crust event types that we generated in previous sections. We also import the processor and its types:
src/main.ts
process.run() call - we are going to come back to it in a second - and scroll down to the getTransferEvents function. In the template repository this function loops through the items contained in the context, extracts the events data and stores it in a list of objects.
For this project we are still going to extract events data from the context, but this time we have more than one event type so we have to sort them. We also need to handle the account information. Let’s start with deleting the TransferEvent interface and defining this instead:
getTransferEvents function with the below snippet that
- extracts event information in a manner specific to its name (known from
e.name); - stores event information in an object (we are going to use entity classes for that) and extracts
accountIds from it; - store all
accountIds in a set.
Account) object for every accountId in the set, then add the Account information to every event entity object. Finally, we save all the created and modified entity models into the database.
Take the code inside processor.run() and change it so that it looks like this:
Apply changes to the database
Squid projects automatically manage the database connection and schema via an ORM abstraction provided by TypeORM. Previously we changed the data schema atschema.graphql and reflected these changes in our Typescript code using npx squid-typeorm-codegen. Here, we apply the corresponding changes to the database itself.
First, we’ll need to compile our updated project code. Do this with:
schema.graphql. Apply it with:
Launch the project
It’s finally time to run the project! Runlocalhost:4350/graphql in a browser and accessing the GraphiQL console.
From this window we can perform queries. This one displays info on ten latest work reports, including all involved files and the account id:

