midnight-indexer:索引器服务Skill midnight-indexer:indexer-service

Midnight索引器服务技能提供区块链数据查询接口,用于获取账户余额、交易历史、合约状态等链上信息。支持GraphQL API查询、UTXO模型数据访问、隐私交易处理、分页查询优化,是构建DApp后端和区块链数据分析的核心工具。关键词:Midnight区块链、索引器服务、GraphQL API、UTXO查询、隐私交易、DApp开发、区块链数据、分页查询、实时订阅、健康检查。

DApp开发 0 次安装 0 次浏览 更新于 2/26/2026

name: midnight-indexer:indexer-service description: 当需要查询Midnight索引器获取区块链数据、获取账户余额、列出交易记录、读取合约状态或构建数据驱动的DApp后端时使用。

索引器服务

连接并查询Midnight网络索引器,获取包括余额、交易和合约状态在内的区块链数据。

使用场景

  • 获取账户余额和UTXO数据
  • 列出地址的交易历史
  • 从区块链读取合约状态
  • 查询隐私与非隐私交易数据
  • 构建数据驱动的DApp后端
  • 为大型结果集实现分页

核心概念

索引器架构

Midnight索引器提供GraphQL API用于查询区块链数据。它索引所有链上活动并通过结构化查询暴露数据。

组件 用途
GraphQL API 区块链数据查询接口
WebSocket 实时订阅(参见event-subscriptions技能)
REST健康检查 服务健康状态检查

API版本

索引器API经历了多个版本的演进。请确保您的查询与目标版本兼容。

版本 关键特性 破坏性变更
2.0.0 基础GraphQL架构 初始发布
2.1.0 增强过滤器、分页 查询参数变更
2.1.4 性能优化 从2.1.0无变更

UTXO模型

Midnight使用UTXO(未花费交易输出)模型而非账户模型。余额查询返回地址拥有的未花费输出集合。

隐私交易

Midnight支持透明和隐私交易。索引器提供不同的访问模式:

  • 透明交易:完整交易细节可见
  • 隐私交易:仅承诺哈希可见,细节加密

参考文档

文档 描述
api-versions.md 版本差异与迁移指南
query-patterns.md 常见查询模式与优化

示例

示例 描述
balance-query/ 查询账户余额和UTXO
transaction-list/ 带分页的交易历史列表
contract-state/ 读取已部署合约状态

快速开始

1. 配置索引器客户端

import { createIndexerClient } from '@midnight-ntwrk/midnight-js-indexer';

const indexer = createIndexerClient({
  uri: 'https://indexer.testnet.midnight.network/api/v1/graphql',
  wsUri: 'wss://indexer.testnet.midnight.network/api/v1/graphql',
});

2. 查询余额

const balance = await indexer.query({
  query: `
    query GetBalance($address: String!) {
      balance(address: $address) {
        total
        utxos {
          txHash
          outputIndex
          amount
        }
      }
    }
  `,
  variables: { address: 'addr_test1...' },
});

console.log('总余额:', balance.data.balance.total);

3. 列出交易

const transactions = await indexer.query({
  query: `
    query GetTransactions($address: String!, $limit: Int!) {
      transactions(address: $address, first: $limit) {
        edges {
          node {
            hash
            blockNumber
            timestamp
            inputs { address amount }
            outputs { address amount }
          }
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
  `,
  variables: { address: 'addr_test1...', limit: 20 },
});

常见模式

基于环境的配置

interface IndexerConfig {
  uri: string;
  wsUri: string;
}

function getIndexerConfig(): IndexerConfig {
  const network = process.env.MIDNIGHT_NETWORK || 'testnet';

  const configs: Record<string, IndexerConfig> = {
    testnet: {
      uri: 'https://indexer.testnet.midnight.network/api/v1/graphql',
      wsUri: 'wss://indexer.testnet.midnight.network/api/v1/graphql',
    },
    mainnet: {
      uri: 'https://indexer.midnight.network/api/v1/graphql',
      wsUri: 'wss://indexer.midnight.network/api/v1/graphql',
    },
  };

  return configs[network] ?? configs.testnet;
}

使用游标分页

async function fetchAllTransactions(
  indexer: IndexerClient,
  address: string
): Promise<Transaction[]> {
  const allTransactions: Transaction[] = [];
  let cursor: string | null = null;
  let hasMore = true;

  while (hasMore) {
    const result = await indexer.query({
      query: TRANSACTIONS_QUERY,
      variables: {
        address,
        first: 100,
        after: cursor,
      },
    });

    const { edges, pageInfo } = result.data.transactions;
    allTransactions.push(...edges.map(e => e.node));

    hasMore = pageInfo.hasNextPage;
    cursor = pageInfo.endCursor;
  }

  return allTransactions;
}

健康检查

async function checkIndexerHealth(baseUrl: string): Promise<boolean> {
  try {
    const response = await fetch(`${baseUrl}/health`);
    return response.ok;
  } catch {
    return false;
  }
}

带退避的重试

async function queryWithRetry<T>(
  indexer: IndexerClient,
  query: string,
  variables: Record<string, unknown>,
  maxRetries = 3
): Promise<T> {
  let lastError: Error | null = null;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await indexer.query({ query, variables });
    } catch (error) {
      lastError = error as Error;
      if (attempt < maxRetries) {
        await new Promise(r => setTimeout(r, 1000 * attempt));
      }
    }
  }

  throw lastError;
}

相关技能

  • event-subscriptions - 实时WebSocket事件流
  • midnight-tooling:contract-calling - 调用改变状态的合约
  • midnight-dapp:state-management - 前端状态与索引器同步

相关命令

  • /midnight-tooling:check - 验证索引器连接性