sqd scripts defined in commands.json to simplify your workflow. See sqd CLI cheatsheet for a short intro.Prepare the environment
- Node.js 20 or newer
- Git
- Squid CLI
- Docker (if your squid will store its data to PostgreSQL)
Understand your technical requirements
Consider your business requirements and find out-
How the data should be delivered. Options:
- PostgreSQL with an optional GraphQL API - can be real-time
- file-based dataset - local or on S3
- Google BigQuery
- What data should be delivered
-
What are the technologies powering the blockchain(s) in question. Supported options:
- Ethereum Virtual Machine (EVM) chains like Ethereum - supported networks
- Substrate-powered chains like Polkadot and Kusama - supported networks
- What exact data should be retrieved from blockchain(s)
- Whether you need to mix in any off-chain data
Example requirements
DEX analytics on Polygon
DEX analytics on Polygon
- A delay of a few hours typically won’t matter for training, so you may want to deliver the data as files for easier handling.
- The output could be a simple list of swaps, listing pair, direction and token amounts for each.
- Polygon is an EVM chain.
- All the required data is contained within
Swapevents emitted by the pair pool contracts. Uniswap deploys these dynamically, so you will also have to capturePoolCreatedevents from the factory contract to know whichSwapevents are coming from Uniswap and map them to pairs. - No off-chain data will be necessary for this task.
NFT ownership on Ethereum
NFT ownership on Ethereum
- For this application it makes sense to deliver a GraphQL API.
- Output data might have
Token,OwnerandTransferdatabase tables / entities, with e.g.Tokensupplying all the fields necessary to show ownership history and the image. - Ethereum is an EVM chain.
- Data on token mints and ownership history can be derived from
Transfer(address,address,uint256)EVM event logs emitted by the contract. To render images, you will also need token metadata URLs that are only available by querying the contract state with thetokenURI(uint256)function. - You’ll need to retrieve the off-chain token metadata (usually from IPFS).
Kusama transfers BigQuery dataset
Kusama transfers BigQuery dataset
- The delivery format is BigQuery.
- A single table with
from,toandamountcolumns may suffice. - Kusama is a Substrate chain.
- The required data is available from
Transferevents emitted by theBalancespallet. Take a look at our Substrate data sourcing miniguide for more info on how to figure out which pallets, events and calls are necessary for your task. - No off-chain data will be necessary for this task.
Start from a template
Although it is possible to compose a squid from individual packages, in practice it is usually easier to start from a template.- EVM
- Substrate
Templates for the PostgreSQL+GraphQL data destination
Templates for the PostgreSQL+GraphQL data destination
- A minimal template intended for developing EVM squids. Indexes ETH burns.
- A starter squid for indexing ERC20 transfers.
- Classic example Subgraph after a migration to SQD.
- A template showing how to combine data from multiple chains. Indexes USDC transfers on Ethereum and Binance.
Templates for storing data in files
Templates for storing data in files
- USDC transfers -> local CSV
- USDC transfers -> local Parquet
- USDC transfers -> CSV on S3
Templates for the Google BigQuery data destination
Templates for the Google BigQuery data destination
- USDC transfers -> BigQuery dataset
Configure v2 gateway access
.env file:EvmBatchProcessor and SubstrateBatchProcessor read SQD_API_KEY automatically when .setGateway() receives a URL. Portal-native processors configured with .setPortal() do not use this v2 gateway key.sqd auth.- PostgreSQL+GraphQL
- Filesystem dataset
- BigQuery
Start the GraphQL server
docker compose down.The bottom-up development cycle
The advantage of this approach is that the code remains buildable at all times, making it easier to catch issues early.Regenerate task-specific utilities
Configure data requests
Decode and normalize the data
Enrich the data when needed
Prepare the store
Persist transformed data
Regenerate the task-specific utilities
- EVM
- Substrate
./abi, you can then regenerate the utilities withsrc/abi.See also EVM typegen code generation.Configure the data requests
Data requests are customarily defined atsrc/processor.ts. The details depend on the network type:
- EVM
- Substrate
const processor to-
Use a data source appropriate for your chain and task.
- It is possible to use RPC as the only data source, but adding a SQD Network data source will make your squid sync much faster.
- RPC is a hard requirement if you’re building a real-time API.
- If you’re using RPC as one of your data sources, make sure to set the number of finality confirmations so that hot blocks ingestion works properly.
- Request all event logs, transactions, execution traces and state diffs that your task requires, with any necessary related data (e.g. parent transactions for event logs).
-
Select all data fields necessary for your task (e.g.
gasUsedfor transactions).
Decode and normalize the data
Next, change the batch handler to decode and normalize your data. In templates, the batch handler is defined at theprocessor.run() call in src/main.ts as an inline function. Its sole argument ctx contains:
- at
ctx.blocks: all the requested data for a batch of blocks - at
ctx.store: the means to save the processed data - at
ctx.log: aLogger - at
ctx.isHead: a boolean indicating whether the batch is at the current chain head - at
ctx._chain: the means to access RPC for state calls
ctx.blocks items varies.
- EVM
- Substrate
ctx.blocks contains the data for the requested logs, transactions, traces and state diffs for a particular block, plus some info on the block itself. See EVM batch context reference.Use the .decode methods from the contract ABI utilities to decode events and transactions, e.g.Mix in external data and chain state call output (optional)
If you need external (i.e. non-blockchain) data in your transformation, take a look at the External APIs and IPFS page. If any of the onchain data you need is unavailable from the processor or inconvenient to retrieve with it, you can use direct chain queries.Prepare the store
Atsrc/main.ts, change the Database object definition to accept your output data. The methods for saving data will be exposed by ctx.store within the batch handler.
- PostgreSQL+GraphQL
- filesystem dataset
- BigQuery
-
Define the schema of the database (and the core schema of the OpenReader GraphQL API if it is used) at
schema.graphql. -
Regenerate the TypeORM model classes with
The classes will become available at
src/model. -
Compile the models code with
-
Ensure that the squid has access to a blank database. The easiest way to do so is to start PostgreSQL in a Docker container with
If the container is running, stop it and erase the database withbefore issuing a
docker compose up -d. The alternative is to connect to an external database. See this section to learn how to specify the connection parameters. -
Regenerate a migration with
ctx.store.upsert() and ctx.store.insert(), as well as various TypeORM lookup methods to access the database.See the typeorm-store guide and reference for more info.Persist the transformed data to your data sink
Once your data is decoded, optionally enriched with external data and transformed the way you need it to be, it is time to save it.- PostgreSQL+GraphQL
- filesystem dataset
- BigQuery
upsert() or insert(), e.g.:The top-down development cycle
The bottom-up development cycle described above is convenient for initial squid development and for trying out new things, but it has the disadvantage of not having the means of saving the data ready at hand when initially writing the data decoding/transformation code. That makes it necessary to come back to that code later, which is somewhat inconvenient, for example when adding new squid features incrementally. The alternative is to do the same steps in a different order:Update the store
Regenerate utility classes
Update the processor configuration
Decode and normalize the added data
Retrieve external data when needed
Add the persistence code
GraphQL options
Store your data to PostgreSQL, then consult Serving GraphQL for options.Scaling up
If you’re developing a large squid, make sure to use batch processing throughout your code. A common mistake is to make handlers for individual event logs or transactions; for updates that require data retrieval that results in lots of small database lookups and ultimately in poor syncing performance. Collect all the relevant data and process it at once. A simple architecture of that type is discussed in the BAYC tutorial. You should also check the Cloud best practices page even if you’re not planning to deploy to SQD Cloud - it contains valuable performance-related tips. Many issues commonly arising when developing larger squids are addressed by the third party@belopash/typeorm-store package. Consider using it.
For complete examples of complex squids take a look at the Giant Squid Explorer and Thena Squid repos.
Next steps
- Learn about batch processing.
- Learn how squids deal with unfinalized blocks.
- Use external APIs and IPFS in your squid.
- See how squid should be set up for the multichain setting.
- Deploy your squid on own infrastructure or to SQD Cloud.