sui-indexer

Use when building custom indexers, data pipelines, or event processors for the SUI blockchain. Triggers on "indexer", "data pipeline", "backfill", "event processor", "index transactions", "analytics dashboard", "aggregate on-chain data", "historical query", "track all trades", or any custom data extraction from SUI chain history. Also use when the user needs to build dashboards from on-chain data, process historical transactions, or set up real-time event streams.

Best use case

sui-indexer is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when building custom indexers, data pipelines, or event processors for the SUI blockchain. Triggers on "indexer", "data pipeline", "backfill", "event processor", "index transactions", "analytics dashboard", "aggregate on-chain data", "historical query", "track all trades", or any custom data extraction from SUI chain history. Also use when the user needs to build dashboards from on-chain data, process historical transactions, or set up real-time event streams.

Teams using sui-indexer should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/sui-indexer/SKILL.md --create-dirs "https://raw.githubusercontent.com/first-mover-tw/sui-dev-agents/main/skills/sui-indexer/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/sui-indexer/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How sui-indexer Compares

Feature / Agentsui-indexerStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when building custom indexers, data pipelines, or event processors for the SUI blockchain. Triggers on "indexer", "data pipeline", "backfill", "event processor", "index transactions", "analytics dashboard", "aggregate on-chain data", "historical query", "track all trades", or any custom data extraction from SUI chain history. Also use when the user needs to build dashboards from on-chain data, process historical transactions, or set up real-time event streams.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# SUI Indexer

**Build custom indexer pipelines for SUI blockchain data extraction and processing.**

## Overview

The SUI Indexing Framework (`sui-indexer-alt-framework`) lets you build custom data pipelines that process blockchain checkpoints and write structured data to your own storage (typically PostgreSQL). Use this when gRPC/GraphQL queries are insufficient — e.g., you need full historical event aggregation, custom analytics, or real-time derived data.

**When to use an indexer vs gRPC/GraphQL:**

| Use Case | gRPC/GraphQL | Custom Indexer |
|----------|-------------|----------------|
| Read current object state | ✓ | |
| Query recent events | ✓ | |
| Full historical event aggregation | | ✓ |
| Custom analytics / derived data | | ✓ |
| Real-time price feeds | | ✓ |
| Cross-object correlation at scale | | ✓ |

## Architecture

```
Checkpoint Stream → Ingestion Client → Processor(s) → Store (PostgreSQL / custom)
                                            ↓
                                      Service lifecycle
                                    (start, shutdown, metrics)
```

**Components:**
1. **Ingestion Client** — fetches checkpoints from the network
2. **Processor** — transforms checkpoint data into your domain model
3. **Store** — writes processed data to your database
4. **Service** — manages lifecycle, shutdown signals, error handling

## Core API (Protocol 119+)

### CheckpointEnvelope

As of Protocol 119, `IngestionClientTrait::checkpoint()` returns a `CheckpointEnvelope` containing both checkpoint data and chain identification:

```rust
/// Returned by IngestionClientTrait::checkpoint()
pub struct CheckpointEnvelope {
    /// The full checkpoint data (transactions, effects, events, objects)
    pub data: CheckpointData,
    /// Chain identifier — full 32-byte Base58-encoded digest
    pub chain_id: String,
}
```

**Breaking change from Protocol 118:** The method was renamed from `fetch()` to `checkpoint()` and the return type changed from `CheckpointData` to `CheckpointEnvelope`. Update existing indexers accordingly.

### IngestionClientTrait

```rust
#[async_trait]
pub trait IngestionClientTrait: Send + Sync {
    /// Fetch a checkpoint by sequence number (renamed from `fetch` in Protocol 119)
    async fn checkpoint(&self, checkpoint: u64) -> Result<Arc<CheckpointEnvelope>>;
}
```

**Built-in implementations:**
- `StoreIngestionClient` — reads from any `object_store::ObjectStore` (S3, GCS, local filesystem)
- Remote checkpoint fetching via full node gRPC

### Processor Trait

```rust
#[async_trait]
pub trait Processor: Send + Sync + 'static {
    /// Human-readable name for logging and metrics
    const NAME: &'static str;

    /// Process a single checkpoint envelope
    async fn process(&self, envelope: &CheckpointEnvelope) -> Result<()>;
}
```

**Example — Event indexer:**

```rust
use sui_indexer_alt_framework::prelude::*;

struct MyEventProcessor {
    db: PgPool,
}

#[async_trait]
impl Processor for MyEventProcessor {
    const NAME: &'static str = "my-event-processor";

    async fn process(&self, envelope: &CheckpointEnvelope) -> Result<()> {
        let checkpoint = &envelope.data;
        for tx in &checkpoint.transactions {
            for event in &tx.events {
                if event.type_.module == "my_module" {
                    sqlx::query("INSERT INTO events (tx_digest, type, data, checkpoint, chain_id) VALUES ($1, $2, $3, $4, $5)")
                        .bind(&tx.transaction.digest().to_string())
                        .bind(&event.type_.to_string())
                        .bind(&serde_json::to_value(&event.parsed_json)?)
                        .bind(checkpoint.checkpoint_summary.sequence_number as i64)
                        .bind(&envelope.chain_id)
                        .execute(&self.db)
                        .await?;
                }
            }
        }
        Ok(())
    }
}
```

### Service Lifecycle

```rust
use sui_indexer_alt_framework::Service;

// Build and run the indexer service
let service = Service::builder()
    .ingestion_client(store_client)
    .add_processor(MyEventProcessor { db: pool.clone() })
    .add_processor(MyObjectTracker { db: pool.clone() })
    .build()
    .await?;

// Run with clean shutdown handling
// Blocks until SIGINT/SIGTERM or fatal error
service.main().await?;
```

**Key points:**
- `Service` replaces the old `JoinHandle<()>` pattern (breaking change from v1.63)
- Call `service.main()` for clean shutdown handling (responds to SIGINT/SIGTERM)
- Multiple processors run in parallel within a single service

## Quick Start

### 1. Set up project

```bash
cargo new my-indexer
cd my-indexer
```

**Cargo.toml:**
```toml
[package]
name = "my-indexer"
version = "0.1.0"
edition = "2021"

[dependencies]
sui-indexer-alt-framework = { git = "https://github.com/MystenLabs/sui.git", branch = "mainline" }
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
anyhow = "1"
async-trait = "0.1"
```

### 2. Implement processor

See the Event indexer example in Core API section above.

### 3. Wire up main

```rust
use sui_indexer_alt_framework::{Service, StoreIngestionClient};
use sqlx::postgres::PgPoolOptions;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let pool = PgPoolOptions::new()
        .max_connections(10)
        .connect(&std::env::var("DATABASE_URL")?)
        .await?;

    let ingestion = StoreIngestionClient::new_remote(
        "https://fullnode.testnet.sui.io:443".to_string(),
    )?;

    let service = Service::builder()
        .ingestion_client(ingestion)
        .add_processor(MyEventProcessor { db: pool.clone() })
        .build()
        .await?;

    service.main().await
}
```

### 4. Run

```bash
export DATABASE_URL="postgres://user:pass@localhost/my_indexer"
cargo run
```

## Advanced Patterns

### Multi-Processor Parallel Pipelines

Run multiple processors concurrently — each processes the same checkpoint stream independently:

```rust
let service = Service::builder()
    .ingestion_client(ingestion)
    .add_processor(EventProcessor { db: pool.clone() })
    .add_processor(ObjectTracker { db: pool.clone() })
    .add_processor(BalanceAggregator { db: pool.clone() })
    .build()
    .await?;
```

Each processor runs in its own task. Failures in one processor do not affect others. The service logs errors and continues.

### Backfill Strategy

For historical data, configure the starting checkpoint:

```rust
let service = Service::builder()
    .ingestion_client(ingestion)
    .add_processor(processor)
    .start_checkpoint(0)  // Start from genesis for full backfill
    .build()
    .await?;
```

**Tips:**
- For large backfills, use `StoreIngestionClient` pointed at a checkpoint archive (S3/GCS) — much faster than fetching from a full node
- Track your last-processed checkpoint in the database so you can resume after restarts
- Use separate processor instances for backfill vs live indexing

### Concurrency Control

Since Protocol 118, the framework uses Adaptive Concurrency Control instead of fixed `FANOUT`:

```rust
// Old (removed):
// const FANOUT: usize = 10;

// New: framework automatically scales concurrency based on throughput
// No configuration needed — the framework adapts to your processor speed
```

### Sequential pipeline tuning (1.71+)

Both `checkpoint_lag` and `checkpoint_buffer_size` were **removed** in v1.71. Sequential pipelines now participate in the same adaptive ingestion concurrency system as concurrent pipelines.

Available knobs:
- `subscriber_channel_size` — per-pipeline, under the pipeline's `ingestion` section. Defaults to `max(num_cpus / 2, 4)`. Drives fetch concurrency via bounded-channel fill.
- `pipeline-depth` — new in v1.72: lets a sequential pipeline keep building batches while one is flushing.

> **Upgrade note (v1.72):** `rpc-index` DB version bumped to `4`. First start after upgrade triggers a full re-index of object history. Duration scales with object count — plan accordingly.

### Metrics & Monitoring

The framework exposes Prometheus metrics automatically:

```rust
let service = Service::builder()
    .ingestion_client(ingestion)
    .add_processor(processor)
    .metrics_address("0.0.0.0:9184".parse()?)
    .build()
    .await?;
```

**Key metrics:**
- `indexer_checkpoint_processed_total` — checkpoints processed per processor
- `indexer_checkpoint_latency_seconds` — processing time histogram
- `indexer_ingestion_lag` — how far behind the tip

## Breaking Changes Log

| Version | Change |
|---------|--------|
| v1.73 (Protocol 125) | Testnet v1.73.0 / mainnet P125 (v1.72.3+). JSON-RPC permanent deactivation **2026-07-31** — migrate indexer reads to gRPC / GraphQL before the cutoff |
| v1.72 (Protocol 124) | `rpc-index` DB v4 — first start re-indexes full object history; added `pipeline-depth` for sequential pipelines |
| v1.71 (Protocol 123) | `checkpoint_lag` / `checkpoint_buffer_size` **removed**; sequential pipelines use adaptive concurrency + `subscriber_channel_size` |
| v1.69.1 (Protocol 119) | `IngestionClientTrait::fetch` → `checkpoint`; returns `CheckpointEnvelope` with `chain_id` |
| v1.68 (Protocol 118) | `Processor::FANOUT` removed; Adaptive Concurrency Control replaces fixed workers |
| v1.65.2 (Protocol 111) | `RemoteIngestionClient` renamed to `StoreIngestionClient`; supports any `ObjectStore` |
| v1.63.3 (Protocol 107) | Indexer/ingestion services return `Service` instead of `JoinHandle<()>`; use `Service::main()` |

Related Skills

sui-zklogin

7
from first-mover-tw/sui-dev-agents

Use when implementing zkLogin on SUI — OAuth login (Google, Facebook, Apple, Twitch) with zero-knowledge proofs for privacy-preserving authentication. Triggers on "zkLogin", "social login on SUI", "Google login", "OAuth", "ephemeral keypair", "JWT proof", or any authentication flow that derives a SUI address from an OAuth provider. Also use when the user mentions "login without wallet extension".

sui-walrus

7
from first-mover-tw/sui-dev-agents

Use when storing or retrieving files using Walrus — SUI's decentralized blob storage. Triggers on "Walrus", "blob storage", "upload file to chain", "decentralized storage", "store NFT image", "IPFS alternative on SUI", "where to store NFT metadata", "host a site on-chain", or any off-chain data storage needs on SUI. Also use for Walrus Sites (decentralized web hosting), storing game assets, media files, or when the user asks "where do I put large files on SUI".

sui-wallet

7
from first-mover-tw/sui-dev-agents

Use when performing on-chain transactions (transfer, Move call, publish) through the agent's CLI wallet via MCP tools. Triggers on "transfer SUI", "call Move function", "publish package", "wallet status", "sign transaction", or any agent-driven on-chain operation. This is for headless/backend wallet operations — for browser wallet UI (React/Vue), use sui-frontend instead.

sui-tester

7
from first-mover-tw/sui-dev-agents

Use when writing Move tests, setting up test suites, running gas benchmarks, or planning test strategy for SUI contracts. Triggers on "write tests", "test this module", "#[test]", "test coverage", "gas benchmark", "property-based test", or any Move testing task. Use even for simple "how do I test this function" questions.

sui-suins

7
from first-mover-tw/sui-dev-agents

Use when integrating SuiNS (SUI Name Service) — resolving .sui names to addresses, reverse lookups, or registering names. Triggers on "SuiNS", ".sui name", "name resolution", "reverse lookup", "human-readable address", or any name service integration. Also use when the user wants to display user-friendly names instead of hex addresses.

sui-security-guard

7
from first-mover-tw/sui-dev-agents

Use when setting up security scanning, detecting leaked secrets/API keys, implementing pre-commit hooks, or auditing a Sui Move contract for security/architecture/quality issues. Triggers on "security scan", "detect secrets", "pre-commit hook", "security audit setup", "API key leaked", and on contract-level review requests like "audit this contract", "review access control", "is this Move safe", "check for vulnerabilities", "Move security review" — these load the SEC/DES/PAT/TST/QA/CFG finding registry in references/move-security-findings.md. For offensive/adversarial testing (attack vector discovery, writing exploits/PoCs), use sui-red-team instead. For Move style/idiom quality (non-security), use move-code-quality.

sui-seal

7
from first-mover-tw/sui-dev-agents

Use when implementing data encryption, access control, or secrets management on SUI using the Seal protocol. Triggers on threshold encryption, data privacy, token-gated content, encrypted storage, decryption policies, paywall, gated access, encrypted NFT metadata, private data sharing, or any scenario requiring on-chain access control for off-chain data. Also use when the user mentions Seal, pay-to-decrypt, "only NFT holders can see", or subscriber-only content on SUI.

sui-red-team

7
from first-mover-tw/sui-dev-agents

Use when performing adversarial security testing on SUI Move contracts — generating attack tests for access control bypass, integer overflow, object manipulation, economic exploits, reentrancy, and DoS vectors. Triggers on "red team", "attack test", "find vulnerabilities", "exploit", "pentest", "security test", or when the user wants to stress-test their contract's security. For defensive security setup (scanning, hooks, checklists), use sui-security-guard instead.

sui-passkey

7
from first-mover-tw/sui-dev-agents

Use when implementing WebAuthn passkeys or biometric authentication (Face ID, fingerprint, hardware keys) on SUI. Triggers on "passkey", "WebAuthn", "biometric login", "Face ID", "fingerprint auth", "FIDO2", or passwordless auth that uses device authenticators instead of seed phrases. Different from zkLogin (which uses OAuth providers).

sui-nautilus

7
from first-mover-tw/sui-dev-agents

Use when building verifiable off-chain computation, integrating external APIs with on-chain proof, or running trusted execution environments on SUI. Triggers on Nautilus, off-chain oracle, "verify API data on-chain", "connect external API to Move", "prove off-chain result", trusted compute, AWS Nitro Enclave, attestation, price feed, weather data on-chain, or any scenario requiring cryptographically verified external data. Also use when the user asks "how do I get real-world data into my SUI contract" or needs an oracle-like pattern.

sui-kiosk

7
from first-mover-tw/sui-dev-agents

Use when building NFT marketplaces, enforcing royalties, or managing transfer policies using SUI's Kiosk standard. Triggers on "Kiosk", "NFT marketplace", "transfer policy", "royalty enforcement", "list NFT for sale", "purchase rules", or any NFT commerce on SUI. Also use when the user asks about listing, delisting, or trading NFTs with enforced rules.

sui-install

7
from first-mover-tw/sui-dev-agents

Use when installing or updating the Sui CLI, managing CLI versions with suiup, or resolving environment/setup problems — "install sui", "update sui", "command not found", "sui not found", "client/server api version mismatch", build errors about "old dependencies", switching CLI versions per network, or installing toolchain components (Walrus, MVR, Move Analyzer, site-builder). Also use for first-time client setup, getting faucet tokens, recovering keys from a phrase, or "Cannot find gas coin for signer address". For deploying/upgrading packages use sui-deployer; for on-chain data queries use sui-ts-sdk.