In the previous article, we learnt how blockchain indexers work, their purpose, and their importance in building fast decentralized applications.
In this article, we will build a program that indexes a smart contract, stores it in an SQL database, and creates a graphQL interface to query the data you need.
This article will also be focused on EVM-supported chains. However, the idea of indexing and querying blockchain data is the same across any chain.
An Autograph Indexer
You're a Web3 enthusiast, and you have many mentors, idols, and people you like to meet and interact with in the Web3 space. You create a contract where each person you meet can sign a message to you like an autograph
. You can collect as many autographs as possible, including multiples from the same person.
For this autograph, you want maximum flexibility to fetch information however you want. E.g. Select autographs from a specific person, select autographs from a certain period, get all autographs taken from a specific location, etc.
Autograph Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract Autograph {
event Signature(
address signer,
string message,
string location,
uint256 timestamp
);
function sign(string memory message, string memory location) public {
emit Signature(msg.sender, message, location, block.timestamp);
}
}
This is a simple contract written in solidity where any user can sign a message and the events would be emitted.
We're going to write an indexer that would take information from all signatures to this contract and store it in a database.
Deployed Contract Instance On Base Sepolia Testnet: https://sepolia.basescan.org/address/0x1852a7e0b37d8a08762053ea93bc140a5c58509f#code
Autograph Indexer
For this, we would use Ponder. Ponder is the best way especially to build a Proactive or An Active Indexer for smart contracts.
Prerequisites:
- [Node.js/Npm]
- Postgres Database URL (Optional)
Initialize Ponder
npm create ponder@latest ponder-new -- --template empty
Folder Structure
ποΈ abis
π ExampleContractAbi.ts
ποΈ node_modules
ποΈ src
ποΈ api
π index.ts
π index.ts
π .env.local
π .eslintrc.json
π .gitignore
π package.lock.json
π package.json
π ponder.env.d.ts
π ponder.config.ts
π ponder.schema.ts
π tsconfig.json
Add The Autograph Abi
Create a new file AutographAbi.ts
in the /abis
folder and add the following code: AutographAbi.ts
Update Config With Contract Details
Replace the content of ponder.config.ts
with the following code:
// ponder.config.ts
import { createConfig } from "ponder";
import { http } from "viem";
import { baseSepolia } from "viem/chains";
import { AutographAbi } from "./abis/AutographAbi";
export default createConfig({
networks: {
baseSepolia: {
chainId: baseSepolia.id,
transport: http(),
},
},
contracts: {
Autograph: {
network: "baseSepolia",
abi: AutographAbi,
address: "0x1852a7e0b37d8a08762053ea93bc140a5c58509f",
//explorer: https://sepolia.basescan.org/address/0x1852a7e0b37d8a08762053ea93bc140a5c58509f#code
//Block just before the contract was deployed. By default, it starts from 0
startBlock: 24946615
}
}
});
This adds a deployed instance (on base testnet) of the Autograph Contract.
Create DB Schema
Replace the content of ponder.schema.ts
with the following code:
// ponder.schema.ts
import { onchainTable } from "ponder";
export const autograph = onchainTable("autograph", (t) => ({
hash: t.hex().primaryKey(),
message: t.text().notNull(),
location: t.text().notNull(),
signer: t.text().notNull(),
timestamp: t.bigint().notNull(),
}));
This will generate a simple table with five columns you can use to query the Autograph signature data. It also generates a graphical schema as well.
Update Indexer File
Put the content below into ./src/index.ts
// src/index.ts
import { ponder } from 'ponder:registry'
import schema from 'ponder:schema'
ponder.on("Autograph:Signature", async ({ event, context }) => {
const { signer, message, location, timestamp } = event.args
const hash = event.transaction.hash
const res = await context.db.insert(schema.autograph).values({
hash,
signer,
message,
location,
timestamp
})
}
)
This will watch the autograph contract for each time the Signature event is fired and insert the data into our database.
Start Your Server
Once you ensure all the files are set up correctly, you can simply start your server to start indexing the contents.
npm run dev
Go to http://localhost:42069/
and you get a graphQl interface where you can query the data.
Testing Your Indexer
Enter the query into the query box.
query MyQuery {
autographs(orderBy: "timestamp", orderDirection: "desc", limit: 100) {
items {
hash
message
location
signer
timestamp
}
totalCount
}
}
You should get similar results.
{
"data": {
"autographs": {
"items": [
{
"hash": "0xa164030291bcb934f3488dc73a9f93a57882173f3d5a13f061a4f66e5c3d6335",
"message": "Happy Learning about Indexers",
"location": "Oslo",
"signer": "0x4aFE825e848b16475797F28cF892D084e2756eD2",
"timestamp": "1745780094"
},
{
"hash": "0xdf7ff3e88870a6130c292269c28a019fa1e19946fc20eeae260fc024420cfb22",
"message": "You are awesome",
"location": "ReykjavΓk",
"signer": "0x4aFE825e848b16475797F28cF892D084e2756eD2",
"timestamp": "1745780028"
}
],
"totalCount": 2
}
}
}
The returned data above represents some signature events that had occurred on the Autograph contract. Going forward, all other signatures will be automatically indexed, and stored in your database and you can query as you desire.
Deployment
The indexer can be deployed on any server that supports Node.js. But you need to set up your custom database for it. By default, it uses PGlite.
Get the URL from a database instance. This URL will be used as a DATABASE_URL
environment variable.
You also need to set the database schema location where the tables defined in ponder.schema.ts
are created when you deploy to production.
DATABASE_URL=<Your_Postgres_Or_Sqlite_Database_Url>
DATABASE_SCHEMA=myschema
Use the links below as a guide to deploy your database and application:
How to Deploy a Postgres Database
How To Deploy Nodejs Applications
Test Deployment And Source Code
I've deployed the Autograph indexer for you to test.
Autograph Indexer Live Deployment
Also, Get the full source code on Github
Conclusion
If you got to this point, Congratulations!! ππ. You can now get data from any contract on any EVM-supported chain if you have the following information:
- The contract address
- The contract ABI
Info
There are other alternatives to using ponder including manual indexing and using services that offer smart contract indexing as their model offering. Here are some of the alternatives:
- Viem: Viem exposes a Watch Contract Events function that gets the blockchain data as they happen. Under the hood, it polls the contract every few seconds or milliseconds (customizable) and returns the event data if found.
However, this will get complex for Proactive indexers since you have to fetch and sync the data before starting to watch the contract. You also have to figure out the choice of database, an easy way to query the data (like the ponder graphQl interface), etc. You own the entire process from start to finish. Ponder had that all figured out from the start.
-
Moralis: They run indexer as a service and provide an sdk you can query you data. However, for the service they provide, at some point, you have to pay for it. Also, since they hold the entire data, your business has to depend almost entirely on them.
-
The Graph Protocol: The graph is another that offers smart contract indexing as a service. However, there are some differences in how they approach it.
- They by default return a graphQl interface that makes it more flexible to query.
- The data is stored in a decentralized way. Meaning you can trust that your data will be available even if primary nodes go down.
- Dune: Built on SQL syntax, you can also query dune to get blockchain data directly. Similar to the other two services mentioned, it comes at some cost. These services are also limited in the chains they offer to index.
No Comments Added Yet