Skip to content

Latest commit

 

History

51 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Chaindexing TypeScript

Index any EVM chain and query in SQL - TypeScript implementation based on the Rust version.

Features

  • 🌐 Multi-chain support - Index multiple EVM chains simultaneously
  • Real-time indexing - Process events as they happen
  • 🗄️ SQL queries - Query indexed data using standard SQL
  • 🔄 State management - Track contract states, chain states, and multi-chain states
  • 🎯 Event handlers - Pure handlers for deterministic indexing and side-effect handlers for notifications
  • 🏗️ Type-safe - Full TypeScript support with comprehensive type definitions
  • 🚀 Production-ready - Based on battle-tested Rust implementation
  • 🔧 Configurable - Extensive configuration options for performance tuning

Quick Start

Installation

npm install @chaindexing/chaindexing
npm install @chaindexing/postgres  # For PostgreSQL support

Basic Example

import {
  indexStates,
  Chain,
  createContract,
  PureHandler,
  PureHandlerContext,
  BaseContractState,
  createFilters,
  createUpdates,
} from '@chaindexing/chaindexing';
import { Config } from '@chaindexing/config';
import { PostgresRepo } from '@chaindexing/postgres';

// Define your state
class Nft extends BaseContractState {
  constructor(
    public tokenId: number,
    public ownerAddress: string
  ) {
    super();
  }

  tableName(): string {
    return 'nfts';
  }
}

// Create event handler
class TransferHandler implements PureHandler {
  abi(): string {
    return 'event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)';
  }

  async handleEvent(context: PureHandlerContext): Promise<void> {
    const eventParams = context.getEventParams();

    const from = eventParams.getAddressString('from');
    const to = eventParams.getAddressString('to');
    const tokenId = eventParams.getU32('tokenId');

    if (from === '0x0000000000000000000000000000000000000000') {
      // Mint: create new NFT
      const newNft = new Nft(tokenId, to);
      await newNft.create(context);
    } else {
      // Transfer: update existing NFT
      const existingNft = new Nft(tokenId, from);
      const updates = createUpdates('owner_address', to);
      await existingNft.update(updates, context);
    }
  }
}

// State migrations
class NftMigrations {
  migrations(): string[] {
    return [
      `CREATE TABLE IF NOT EXISTS nfts (
        token_id INTEGER NOT NULL,
        owner_address TEXT NOT NULL
      )`,
    ];
  }
}

// Setup and start
async function main() {
  const repo = new PostgresRepo('postgresql://localhost:5432/chaindexing');

  const config = new Config(repo)
    .addChain(Chain.Mainnet, 'https://eth-mainnet.g.alchemy.com/v2/your-api-key')
    .addContract(
      createContract('ERC721Token')
        .addAddress('0x...', Chain.Mainnet, 18000000)
        .addEventHandler(new TransferHandler())
        .addStateMigrations(new NftMigrations())
        .build()
    );

  await indexStates(config);
}

main().catch(console.error);

Core Concepts

States

Chaindexing supports three types of states:

  1. ContractState - States derived from a single contract
  2. ChainState - States derived from multiple contracts within a chain
  3. MultiChainState - States derived from contracts across multiple chains
import { BaseContractState } from '@chaindexing/chaindexing';

class MyContractState extends BaseContractState {
  constructor(
    public id: number,
    public value: string
  ) {
    super();
  }

  tableName(): string {
    return 'my_states';
  }
}

Event Handlers

Pure Handlers

Deterministic handlers for indexing states:

class MyPureHandler implements PureHandler {
  abi(): string {
    return 'event MyEvent(uint256 indexed id, string value)';
  }

  async handleEvent(context: PureHandlerContext): Promise<void> {
    const params = context.getEventParams();
    const id = params.getU32('id');
    const value = params.getString('value');

    const state = new MyContractState(id, value);
    await state.create(context);
  }
}

Side Effect Handlers

Non-deterministic handlers for notifications, bridging, etc:

class MySideEffectHandler implements SideEffectHandler<AppState> {
  abi(): string {
    return 'event MyEvent(uint256 indexed id, string value)';
  }

  async handleEvent(context: SideEffectHandlerContext<AppState>): Promise<void> {
    const params = context.getEventParams();
    const sharedState = await context.getSharedState();

    // Send notification, update external system, etc.
    await sendNotification(params.getString('value'));

    // Update shared state
    sharedState.notificationCount++;
  }
}

Contract Builder

Create contracts with addresses, handlers, and migrations:

const contract = createContract<SharedState>('MyContract')
  .addAddress('0x...', Chain.Mainnet, 18000000)
  .addAddress('0x...', Chain.Polygon, 25000000)
  .addEventHandler(new MyPureHandler())
  .addSideEffectHandler(new MySideEffectHandler())
  .addStateMigrations(new MyMigrations())
  .build();

Configuration

const config = new Config(repo)
  .addChain(Chain.Mainnet, rpcUrl)
  .addContract(contract)
  .withBlocksPerBatch(450) // Blocks per ingestion batch
  .withHandlerRateMs(4000) // Handler execution interval
  .withIngestionRateMs(20000) // Ingestion interval
  .withMinConfirmationCount(40) // Confirmations before processing
  .withChainConcurrency(4) // Concurrent chain processing
  .withInitialState(initialState) // Shared state for side effects
  .reset(1); // Reset indexing (optional)

Architecture

The TypeScript implementation mirrors the Rust version's architecture:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Config        │    │   Contracts     │    │   Chains        │
│                 │    │                 │    │                 │
│ - Repo          │    │ - Handlers      │    │ - RPC URLs      │
│ - Chains        │    │ - Migrations    │    │ - Chain IDs     │
│ - Contracts     │    │ - Addresses     │    │                 │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                       │                       │
         └───────────────────────┼───────────────────────┘
                                 │
                    ┌─────────────────┐
                    │  Orchestrator   │
                    │                 │
                    │ - Event Ingestion│
                    │ - Event Handling │
                    │ - State Management│
                    └─────────────────┘
                                 │
                    ┌─────────────────┐
                    │   Repository    │
                    │                 │
                    │ - PostgreSQL    │
                    │ - Migrations    │
                    │ - Transactions  │
                    └─────────────────┘

Database Support

Currently supports PostgreSQL with Drizzle ORM:

import { PostgresRepo } from '@chaindexing/postgres';

const repo = new PostgresRepo('postgresql://user:pass@localhost:5432/db');

Performance Tuning

Adjust these configuration parameters based on your needs:

  • blocksPerBatch: Higher values = faster historical sync, higher RPC usage
  • handlerRateMs: Lower values = faster processing, higher CPU usage
  • ingestionRateMs: Lower values = more real-time, higher RPC usage
  • chainConcurrency: Higher values = faster multi-chain, more resources

Error Handling

The library includes comprehensive error handling:

try {
  await indexStates(config);
} catch (error) {
  if (error instanceof ConfigError) {
    console.error('Configuration error:', error.message);
  } else {
    console.error('Indexing error:', error);
  }
}

Contributing

This TypeScript implementation is based on the battle-tested Rust version. Contributions are welcome!

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Submit a pull request

License

MIT License - see LICENSE file for details.

Related Projects

Releases

Used by

Contributors

Languages