npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

argus-indexer

v1.0.7

Published

An Ethereum indexer for monitoring and processing blockchain events. Works on all EVM chains.

Readme

Eth Indexer

Eth Indexer is a lightweight and efficient tool for indexing EVM-based blockchain events. It fetches filtered events and passes them to a custom handler defined by you.


✨ Features

  • Indexes historical and live blockchain events.
  • Prevents duplicate event processing using a customizable storage strategy.
  • Simple and extensible architecture.
  • Easy to configure with batching and delay support.

🚀 How It Works

argus-indexer queries the blockchain using the RPC provider and contract ABIs you supply. It:

  1. Fetches and parses events using ethers.js.
  2. Delivers both raw and parsed events to your custom handler.
  3. Ensures event processing order matches the order of your provided contracts — useful when contracts are interdependent.

Example Use Case

Suppose you have:

  • An ERC-721 NFT contract
  • A Market contract referencing token IDs from the NFT contract

In this case, provide the NFT contract first so its data is available before processing market events.


🛠️ Usage

1. Create a Contract Class

Extend the IndexerContract abstract class and define how events should be handled:

import { IndexerContract } from "argus-indexer";

class MarketContract extends IndexerContract {
  protected contractAddress = "0x..."; // Contract address
  protected contractAbi: ethers.InterfaceAbi = {}; // Contract ABI
  protected initialBlockNumber = 0; // Starting block number. It's usually the number of the block which the contract is deployed on.
  protected name = "Market"; // Contract name for logs

  public queryFilter(): ethers.ContractEventName {
    return "*"; // Catch all events.
  }

  public async handleEvent(
    parsedEvent: ethers.LogDescription | null,
    event: ethers.EventLog | ethers.Log
  ): Promise<void> {
    // Your custom event handler logic. E.g: Save the event details
    console.log(`Fetched ${parsedEvent.name}: ${parsedEvent?.args?.order_id}`);
  }
}

2. Initiate the indexer

Call the initiate() function with your contract classes and config:

import { initiate } from "argus-indexer";

initiate({
  rpcUrl: "https://your-rpc-url",
  logFilePath: "logs.log",
  eventHandlerSleep: 200,
  refetchInterval: 30000,
  batchSize: 10000,
  txRepositoryFilePath: "transactions.json",
  lastBlock: 72000000,
});

3. Start indexing

import { initiate, index } from "argus-indexer";
import CustomTransactionRepository from "CustomTransactionRepository";

initiate({
  rpcUrl: "https://your-rpc-url",
  logFilePath: "logs.log",
  eventHandlerSleep: 200,
  refetchInterval: 30000,
  batchSize: 10000,
  txRepositoryFilePath: "transactions.json",
  lastBlock: 72000000,
});

index([NFTContract, MarketContract], new CustomTransactionRepository());

⚙️ Configuration Options

| Key | Required | Description | | ---------------------- | -------- | ---------------------------------------------------------------------------- | | rpcUrl* | ✅ | Full URL of your JSON-RPC provider | | logFilePath* | ✅ | Path to your log file | | eventHandlerSleep | ❌ | Delay (in ms) between event handling, useful for rate-limiting. Default: 0 | | refetchInterval | ❌ | Polling interval (in ms) for live events. Default: 30000 | | batchSize | ❌ | Block range per fetch. Adjust to avoid RPC limits. Default: 10000 | | txRepositoryFilePath | ❌ | File path for storing handled events. Default: ./.transactions.json | | lastBlock | ❌ | Stop indexing at this block. Useful for snapshots. |


🧩 Custom Transaction Repository

To avoid re-processing events, you can provide a custom event storage strategy by implementing the TransactionRepositoryInterface. Example using Prisma:

import { ethers } from "argus-indexer";
import type { TransactionRepositoryInterface } from "argus-indexer";
import { PrismaClient } from "@prisma/client";

class TransactionRepository implements TransactionRepositoryInterface {
  protected prisma = new PrismaClient();

  constructor() {
    this.prisma.$connect();
  }

  public async submit(
    event: ethers.EventLog | ethers.Log,
    name: string | null
  ) {
    await this.prisma.transaction.create({
      data: {
        eventName: name ?? "",
        transactionHash: event.transactionHash,
        blockHash: event.blockHash,
        blockNumber: event.blockNumber,
        address: event.address,
        data: event.data,
        topics: event.topics.toString(),
        transactionIndex: event.transactionIndex,
      },
    });
  }

  public async existed(
    event: ethers.EventLog | ethers.Log,
    name: string | null
  ) {
    const existedTx = await this.prisma.transaction.findFirst({
      where: {
        eventName: name ?? "",
        transactionHash: event.transactionHash,
        blockHash: event.blockHash,
        blockNumber: event.blockNumber,
        address: event.address,
        data: event.data,
        topics: event.topics.toString(),
        transactionIndex: event.transactionIndex,
      },
    });
    return !!existedTx;
  }
}

Note: It's strongly recommended to store all event attributes to ensure accurate duplication checks.


🔧 Built-in Helpers in IndexerContract

Each class that extends IndexerContract has access to:

| Property | Description | | ------------------------ | ----------------------------------------------------------------------- | | this.logger | A pre-configured winston logger | | this.rpcProvider | An ethers.JsonRpcProvider using your RPC URL | | this.contractInstance | An ethers.Contract instance of your contract | | this.contractInterface | An ethers.Interface for advanced parsing |

You can also use any part of ethers.js by importing directly from argus-indexer.


📬 Contributing & Questions

Have a question, bug report, or feature request? Feel free to open an issue — contributions are welcome!