Skip to content

Using Index Supply as the Indexer

The Ethereum Comments Protocol can be integrated with Index Supply as an alternative indexing solution. This guide will walk you through the steps to setup Index Supply to query comments from the protocol.

Event Signatures

All events emitted by the CommentManager contract can be found here.

Struct to Tuple Conversion

Index Supply does not recognize struct in event signatures. all structs must be converted into a tuple according to Human Readable ABI format.

For example, the CommentAdded event was defined as:

event CommentAdded(
  bytes32 indexed commentId,
  address indexed author,
  address indexed app,
  CommentData commentData
);

It must be converted to the following tuple form in order to work with Index Supply:

CommentAdded(bytes32 indexed commentId, address indexed author, address indexed app, (string content, string metadata, string targetUri, bytes32 parentId, address author, address app, bytes32 salt, uint256 deadline) commentData)

please note: at the time of writing, Index Supply's query interface doesn't support multi-line event signatures.

Dependencies

All examples below requires installing the following dependencies:

npm install @ecp.eth/sdk zod

Fetching Comments

We will build a fetchComments() function to fetch comments for a given URI via Index Supply.

Create Index Supply Client

Create a simple client wrapper for Index Supply:

export type ISPrimitive = string | number | boolean | null;
export type ISRow = ISPrimitive[];
export type ISRows = ISRow[];
export type ISResponse = {
  block_height: number;
  result: [[string[], ...ISRows]];
};
 
export const indexSupplyClient = {
  async query(chain: string, query: string, eventSignatures: string[]) {
    const params = new URLSearchParams({
      chain,
      query,
      event_signatures: eventSignatures.join(","),
    });
 
    const response = await fetch(
      `https://api.indexsupply.net/query?${params.toString()}`
    );
 
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
 
    return response.json() as Promise<ISResponse>;
  },
};

Fetch Comments function for a URI

Here's how to fetch comments for a specific URI using Index Supply:

import { z } from "zod";
import { COMMENT_MANAGER_ADDRESS } from "@ecp.eth/sdk";
import { HexSchema } from "@ecp.eth/sdk/core";
import { indexSupplyClient, ISPrimitive } from "./isClient";
 
const COMMENT_ADDED_EVENT = `CommentAdded(
    bytes32 indexed commentId,
    address indexed author,
    address indexed app, 
    (
      string content,
      string metadata,
      string targetUri,
      bytes32 parentId,
      address author,
      address app,
      bytes32 salt,
      uint256 deadline
    ) commentData
  )`;
 
const commentSchema = z.object({
  id: z.string(),
  content: z.string(),
  targetUri: z.string(),
  author: HexSchema,
  app: HexSchema,
  parentId: z.string().nullable(),
  blockNumber: z.number(),
  transactionHash: HexSchema,
});
 
type Comment = z.infer<typeof commentSchema>;
 
async function fetchComments(
  targetUri: string,
  app: string,
  chain = "8453"
): Promise<Comment[]> {
  const query = `
    SELECT
      commentId as "id",
      (commentData->>'content') as "content",
      (commentData->>'targetUri') as "targetUri",
      "author", 
      "app",
      (commentData->>'parentId') as "parentId",
      block_num as "blockNumber",
      tx_hash as "transactionHash"
    FROM
      CommentAdded
    WHERE
      address = ${COMMENT_MANAGER_ADDRESS}
    AND
      app = ${app}
    AND
      commentData->>'targetUri' = '${targetUri}'
    ORDER BY block_num DESC
    LIMIT 10
  `;
 
  try {
    const response = await indexSupplyClient.query(chain, query, [
      COMMENT_ADDED_EVENT,
    ]);
 
    if (!response.result?.[0]) {
      return [];
    }
 
    const [columns, ...rows] = response.result[0];
 
    if (!columns) {
      return [];
    }
 
    return rows
      .map<Record<string, ISPrimitive>>((row: ISPrimitive[]) => {
        return row.reduce<Record<string, ISPrimitive>>(
          (record, value: ISPrimitive, index: number) => {
            if (!columns[index]) {
              return record;
            }
 
            record[columns[index]] = value;
            return record;
          },
          {} as Record<string, ISPrimitive>
        );
      })
      .filter((maybeComment): maybeComment is Comment => {
        const result = commentSchema.safeParse(maybeComment);
        if (!result.success) {
          console.warn("Invalid comment", result.error);
        }
        return result.success;
      });
  } catch (error) {
    console.error("Error fetching comments:", error);
    throw error;
  }
}

Run it

import { fetchComments } from "./fetchComments";
 
console.log(
  await fetchComments(
    "https://demo.ethcomments.xyz/",
    "0xe86a6df8ead873600050fb669e7bc8d3b8587f3d"
  )
);

Done

Limitations and Considerations

  • Index Supply requires proper query optimization for best performance
  • Consider using pagination for large result sets
  • Add appropriate error handling for API rate limits and network issues
  • The free tier has limitations on query complexity and frequency

Additional Resources