← back to projects

go-chainfeed

development

Idiomatic Go web3 event gateway — ingests on-chain swap logs from Solana and Ethereum over WebSocket, normalizes them into one event schema, and fans them out over WebSocket + Prometheus using goroutines, channels, and context.

GoSolanaEVMWebSocketPrometheus

Overview

chainfeed ingests heterogeneous on-chain swap logs from two different chains over WebSocket, normalizes them into one event schema, and fans them out over a WebSocket + Prometheus surface — using idiomatic Go concurrency. It is the Go expression of the same thesis as tape: normalize heterogeneous on-chain events to one schema so downstream consumers never branch on venue-specific wire formats. Where tape chases zero-allocation throughput on an SPSC ring, chainfeed makes the opposite, equally deliberate tradeoff — GC-managed simplicity with channels — to show both sides of that design space.

Architecture

A Solana mainnet logsSubscribe stream and an Ethereum eth_subscribe("logs") stream run on one goroutine each, over public RPC WebSockets with no API keys. Both flow into a single normalized event.Event (schema_version, seq, venue, ts_venue, ts_local, kind, on_chain, payload) that mirrors tape's MarketEvent envelope, then fan out over a buffered channel to a broadcast hub serving every connected /events WebSocket client, alongside /healthz and /metrics.

Reliability

Reconnects use bounded exponential backoff with full jitter; shutdown is context-based and graceful on SIGINT/SIGTERM. Prometheus instrumentation exposes chainfeed_events_total, chainfeed_reconnects_total, and chainfeed_decode_errors_total counters plus a chainfeed_event_latency_seconds histogram.

Decoding Scope (Stated Honestly)

The point is the pipeline and the Go idioms, not exhaustive on-chain decoding — so decoding is intentionally minimal and heuristic. Solana runs a keyword classifier over log lines (no Anchor/IDL instruction decode); EVM filters by the Uniswap-V2 Swap topic0 (guaranteeing swaps, but without ABI-decoding amounts). Unclassifiable logs are kept as kind: "unknown" rather than dropped, so decode coverage stays observable instead of silently lossy.