⛓️ Blockchain Learning Path GitHub

Indexador de eventos

Navegación: Inicio · Currículo · Módulo 10 · Oráculos e indexación

Indexador mínimo en Node.js que reconstruye una vista de campañas de CommunityFunding a partir de sus logs de eventos, y guarda un checkpoint (último bloque procesado) para reanudar sin releer toda la cadena. Es la contraparte del contrato y de la interfaz web: un patrón que aparece en todo backend serio de dApp.

Por qué un indexador y no leer la cadena por clic

Leer el estado con readContract en cada interacción no escala: cada consulta paga una latencia de red, no permite ordenar ni filtrar por criterios propios y multiplica la carga sobre el nodo RPC. Un indexador lee los eventos una vez, los reduce a una vista consultable localmente y solo pide a la cadena el rango de bloques nuevos desde el último checkpoint.

Enfoque Latencia por consulta Historial Coste RPC
Leer la cadena por clic Alta (una llamada por dato) No lo tienes; solo el estado actual Crece con cada usuario
Indexador con checkpoint Baja (lee tu vista local) Reconstruido desde eventos Acotado al rango nuevo

Cómo se ejecuta

Desde la raíz del monorepo, la suite de pruebas del reducer:

pnpm test:indexer

Salida esperada:

✔ reconstruye una campaña desde eventos (Xms)
ℹ tests 1
ℹ pass 1
ℹ fail 0

Para correr el proceso completo contra un nodo real (Anvil o testnet):

cp .env.example .env
set -a; source .env; set +a
pnpm --filter @blockchain-course/event-indexer start

Salida esperada tras un ciclo:

Indexado hasta bloque 128. Campañas: 3

Variables de entorno

Variable Ejemplo Rol
RPC_URL http://127.0.0.1:8545 Endpoint RPC del que se leen los logs.
CONTRACT_ADDRESS 0x5FbDB… Dirección de CommunityFunding; se normaliza con getAddress.
START_BLOCK 0 Primer bloque a indexar cuando no existe checkpoint previo.
CHECKPOINT_PATH event-indexer-state.json Archivo donde se persiste { lastBlock, campaigns }.

Arquitectura

Pieza Archivo Rol
Reducer src/reducer.mjs Función pura reduceFundingEvent(state, event): aplica un evento y devuelve el nuevo estado sin mutar el anterior (structuredClone).
Serialización src/reducer.mjs serializeState convierte los bigint a texto para poder persistir en JSON.
Bucle de ingesta src/index.mjs Carga el checkpoint, calcula fromBlock, pide los logs por evento y aplica el reducer.
Checkpoint event-indexer-state.json Guarda lastBlock y el mapa de campañas; permite reanudar de forma incremental.
Pruebas src/reducer.test.mjs Verifica que una campaña se reconstruye a partir de CampaignCreated + Contributed.

Eventos que se reducen: CampaignCreated, Contributed, Claimed y Refunded.

Pipeline evento → reducer → estado

flowchart LR

    A["Nodo RPC"] --> B["getLogs desde lastBlock+1"]
    B --> C["reduceFundingEvent"]
    C --> D["Estado de campañas"]
    D --> E["serializeState"]
    E --> F["Checkpoint JSON"]
    F -->|"reanuda"| B

Reducer como función pura

Separar la reducción del bucle de red tiene dos ventajas: se prueba sin nodo (ver reducer.test.mjs) y el mismo evento aplicado dos veces produce el mismo resultado, base de un diseño idempotente. El estado nunca se muta en sitio: cada evento devuelve una copia nueva.

Limitaciones que el estudiante debe resolver

El indexador es deliberadamente ingenuo. Convertirlo en robusto exige:

Limitación Qué falta Riesgo si se ignora
Orden entre eventos Ordenar por blockNumber y logIndex al mezclar eventos distintos Estado inconsistente
Reorganizaciones Esperar N confirmaciones y poder revertir bloques huérfanos Contabilizar logs que desaparecen
Paginación Trocear rangos grandes de getLogs El nodo rechaza rangos amplios
Persistencia transaccional Escribir estado y checkpoint de forma atómica Checkpoint adelantado al estado
Verificación de red Validar chainId y dirección esperados Indexar la cadena o el contrato equivocado

Relación con el currículo

Este laboratorio concreta las ideas del módulo 10 · Oráculos e indexación: cómo el dato on-chain se transforma en una vista consultable y por qué la seguridad ante reorgs es parte del diseño, no un extra.