Frontier EVM-indexing Squid
Objective
The goal of this tutorial is to guide you through creating a simple blockchain indexer (“squid”) using Squid SDK. The squid will be indexing the data from two contracts (AstarDegens and AstarCats) deployed on the Astar network. The objective will be to track ownership and transfers of all NFTs issued by these contracts. A somewhat outdated version of the final result can be browsed here.Pre-requisites
- Familiarity with Git
- A properly set up development environment consisting of Node.js, Git and Docker
- Squid CLI
Scaffold using sqd init
We will start with the frontier-evm squid template available through sqd init. It is built to index EVM smart contracts deployed on Astar/Shiden, but it is also capable of indexing Substrate events. To retrieve the template and install the dependencies, run
Define Entity Schema
Next, we ensure that the data schema of the squid defines entities that we would like to track. We are interested in:- Token transfers
- Ownership of tokens
- Contracts and their minted tokens
schema.graphql
@entity: Signals that this type will be translated into an ORM model that is going to be persisted in the database.@derivedFrom: Signals that the field will not be persisted in the database. Instead, it will be derived from the entity relations.- type references (e.g.
from: Owner): When used on entity types, they establish a relation between two entities.
squid-typeorm-codegen tool:
src/model/generated.
ABI Definition and Wrapper
SQD maintains tools for automated generation of TypeScript classes for handling EVM logs and transactions based on a JSON ABI of the contract. For our squid we will need such a module for the ERC-721-compliant part of the contracts’ interfaces. Once again, the template repository already includes it, but it is still important to explain what needs to be done in case one wants to index a different type of contract. Place any ABIs you requre for interfacing your contracts at./abi and run
src/abi. One module will be generated for each ABI file, and it will include constants useful for filtering and functions for decoding EVM events and functions defined in the ABI.
Processor object and the batch handler
Squid SDK provides users with theSubstrateBatchProcessor class. Its instances connect to SQD Network gateways at chain-specific URLs, to get chain data and apply custom transformations. The indexing begins at the starting block and keeps up with new blocks after reaching the tip.
SubstrateBatchProcessors expose methods that “subscribe” them to specific data such as Substrate events and calls. There are also specialized methods for subscribing to EVM logs and transactions by address. The actual data processing is then started by calling the .run() function. This will start generating requests to the SQD Network gateway for batches of data specified in the configuration, and will trigger the callback function, or batch handler (passed to .run() as second argument) every time a batch is returned by the gateway.
It is in this callback function that all the mapping logic is expressed. This is where chain data decoding should be implemented, and where the code to save processed data on the database should be defined.
Managing the EVM contract
Before we begin defining the mapping logic of the squid, we are going to write asrc/contracts.ts utility module for managing the involved EVM contracts. It will export:
- Addresses of astarDegens and astarCats contracts.
- A
Mapfrom the contract addresses to hardcodedContractentity instances.
src/contracts.ts
Create the processor object
Thesrc/processor.ts file is where squids instantiate and configure their processor objects. We will use an instance of SubstrateBatchProcessor.
We adapt the template code to handle two contracts instead of one and point the processor data source setting to the astar SQD Network gateway URL. Here is the end result:
src/processor.ts
Define the batch handler
We change the batch handler logic taking care to avoid token ID clashing:src/main.ts
The
contract.tokenURI call is accessing the state of the contract via a chain RPC endpoint. This is slowing down the indexing a little bit, but this data is only available this way. You’ll find more information on accessing state in the dedicated section of our docs.Database and the migration
Before giving your squid processor a local test, launch a PostgreSQL container with-
Build the code:
-
Make sure you start with a clean Postgres database. The following commands drop-create the Postgres instance in Docker:
Skip this step if you haven’t used your database since the last
docker compose up -d. -
Regenerate the DB migration:
-
Apply the migration:
Launch the Project
To launch the processor run the following command (this will block the current terminal):localhost:4350/graphql to access the GraphiQL console. From this window, you can perform queries such as this one, to find out the account owners with the biggest balances:

