Skip to content

Commit cd147d8

Browse files
authored
update docs (#99)
1 parent 1551aa9 commit cd147d8

4 files changed

Lines changed: 103 additions & 147 deletions

File tree

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ INDEXER_SUPABASE_ADMIN_KEY='JWT'
44
INDEXER_ETH_RPC_WS_URL='wss://url.com'
55
INDEXER_POSTGRES_CONNECTION_STRING='postgresql://postgres:[YOUR-PASSWORD]@127.0.0.1:5432/postgres'
66

7-
# For API
7+
# For Next.js API
88
API_SUPABASE_URL='http://127.0.0.1:54321'
99
API_SUPABASE_ANON_KEY='JWT'
1010
API_AUTH_KEY='random_string'

README.md

Lines changed: 102 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,99 @@
1-
# Indexer + API app
2-
The project has 3 parts
3-
- Postgres DB - schema defined in `supabase` hosted by supabase, uses some of their tools
4-
- Indexer "ETL" go code in `go-indexer` - long-running server that
5-
- connects to an ethereum node
6-
- reads events for specific contracts (configured in `config`)
7-
- write events to DB
8-
- transforms events into data required for the app (accounts, positions, etc)
9-
- Next JS API app in `app` - easy way to host the API. reads from the same schema. Deployed on vercel.
10-
11-
## Database
12-
### Prerequisites
13-
- npm
14-
- docker
15-
- supabase installed globally (`npm install supabase --save-dev`)
16-
17-
### Start locally
18-
start docker
1+
# ERC4626 Ethereum Vault Indexer and API Gateway
2+
This project demonstrates the design and implementation of a production-grade Ethereum indexer and API gateway, written entirely in Go.
3+
It ingests on-chain events from an ERC-4626 vault, processes and normalizes them into a Supabase PostgreSQL database, and exposes a public REST API with automatically generated documentation.
4+
The system is built around a concurrent, multi-threaded architecture designed for data integrity, fault tolerance, and maintainable scaling — with live and historical event ingestion, chain reorganization handling, and finalized block tracking.
5+
Deployed on a Linux server and managed via systemd, the project demonstrates a full end-to-end data pipeline: from blockchain ingestion to queryable structured data.
6+
7+
## Features
8+
- ***Ethereum Indexer in Go***: Listens to Deposit, WithdrawRequested, and Transfer events from an ERC-4626 vault contract
9+
- ***Supabase Integration***: Stores normalized event data in a hosted Postgres database
10+
- ***API Gateway***: Exposes REST endpoints (via Next.js) for querying indexed data
11+
- ***Auto-Generated Docs***: OpenAPI documentation generated automatically from Next.js endpoints, available at `<API_URL>/docs`
12+
- ***Resilient Architecture***: Includes queue-based event processing and reorg verification
13+
- ***Production-Ready Deployment***: Can run as a Linux systemd service for reliability and observability
14+
15+
## Architecture
16+
![Architecture](/docs/architecture.png)
17+
The Vault Indexer is designed as a concurrent Go service that ingests, processes, and serves Ethereum vault events in real time.
18+
19+
It operates through three coordinated threads — each responsible for a specific stage of the indexing pipeline — and exposes the processed data through an API gateway backed by Supabase Postgres.
20+
21+
### Main Indexer Thread
22+
- Connects to an Ethereum node via JSON-RPC and subscribes to the vault’s on-chain events.
23+
- Handles both historical backfill and live streaming of events such as Deposit, WithdrawRequested, and Transfer.
24+
- Writes all captured events into an in-memory event queue, ensuring they’re processed in chronological order.
25+
- Acts as the entry point for all blockchain data entering the system.
26+
27+
### Event Processing Queue
28+
- Serves as a buffer between event ingestion and downstream processing.
29+
- Maintains ordered delivery of events to prevent race conditions and ensure deterministic indexing.
30+
- Enables the system to handle bursts of incoming events without blocking network I/O.
31+
32+
### Data Processor Thread
33+
- Continuously fetches newly queued events and transforms them into structured index records suitable for querying.
34+
- Normalizes Ethereum event data (e.g., addresses, amounts, block metadata) into relational database entries.
35+
- Persists these records into the Postgres database, providing a clean and queryable representation of vault state over time.
36+
37+
### Finality Processor Thread
38+
- Monitors blockchain finality and safety levels to maintain data integrity.
39+
- Detects potential chain reorganizations (reorgs) by verifying block hashes and canonicality.
40+
- Triggers cleanup and rollback operations if non-finalized data becomes invalid.
41+
- Updates internal tables to reflect finalized blocks and ensures all indexed data corresponds to the canonical Ethereum chain.
42+
43+
### Postgres Database (Supabase)
44+
- Acts as the central data store for both raw events and processed indices.
45+
- Enables efficient queries over user positions, vault balances, and event histories.
46+
- Managed through Supabase, which provides hosted PostgreSQL, authentication, and built-in REST access.
47+
48+
### Next.js API Gateway
49+
- Provides a RESTful API layer for external clients, dashboards, or analytics tools.
50+
- Offers auto-generated documentation (Swagger / OpenAPI) for ease of exploration.
51+
- Fetches data directly from the Postgres database, exposing endpoints for querying vault events, user histories, and position summaries.
52+
- Enables fast, queryable access to indexed data by contract address or user wallet.
53+
54+
55+
## Getting Started
56+
### 1. Prerequisites
57+
- indexer
58+
- go
59+
- ethereum node (HTTP endpoint)
60+
- api gateway
61+
- npm
62+
- node.js
63+
- local postgres development
64+
- supabase installed globally (`npm install supabase --save-dev`)
65+
- docker
66+
67+
### 2. Supply environment
68+
Copy `.env.example` and create `.env.local`. You can also create an `.env.prod` and run the process with an environment arg. The default environment is dev.
69+
70+
### 3. Configure vault addresses to index at `go-indexer/config/config.yaml`.
71+
72+
### 4. Start Database
73+
- start docker
74+
- start database
1975
```bash
20-
npx supabase start
21-
npx supabase status
76+
npx supabase start # will deploy existing schema
77+
npx supabase status # view the available endpoints
2278
```
2379

80+
### 5. Start indexer
81+
```bash
82+
go mod tidy # installs Go dependencies
83+
./start-indexer.sh # Start server in development (default)
84+
./start-indexer.sh prod # example with .env.prod file
85+
```
86+
87+
### 6. Start API gateway / docs
88+
```bash
89+
npm install
90+
npm run dev
91+
```
92+
Swagger docs will be available at `/docs`
93+
94+
## Working with Supabase
95+
Here are some useful commands for working with the database.
96+
2497
### Deploy schema change
2598
```bash
2699
npx suapbase migration new <name>
@@ -55,94 +128,33 @@ docker volume ls
55128
docker volume rm <volume name>
56129
```
57130

58-
## Indexer
131+
## Deployment
132+
### Indexer
133+
The indexer is meant to run as a constant-uptime script on a remote server. It is safe to stop and restart, and will backfill the data on each restart.
59134

60-
### Prereqs
61-
- go
62-
- docker
135+
This process was previously deployed on Linux servers, run with systemd commands which were configured via Nix.
63136

64-
### Local development
65-
1. install dependencies
66-
```bash
67-
go mod tidy
68-
```
69-
70-
2. set config in `go-indexer/config/config.yaml`
71-
- add contracts info + abis
72-
- check supabase local studio (http://127.0.0.1:54323) for anon key and connection string
137+
Persistent errors will cause the process to stop running. If you plan to deploy this yourself, make sure to add a configuration which will restart the process with a cooldown if it stops.
73138

74-
3. run server
75-
```bash
76-
# For development (default)
77-
./start-indexer.sh
78-
79-
# For production
80-
./start-indexer.sh prod
81-
```
82-
83-
###
84-
Run with nix
139+
## Running with nix
85140
```bash
86141
nix run .#indexer
87142
```
88143

89144
## Testing
90145
```bash
91146
go test ./...
92-
93147
go test ./go-indexer/indexer -v
94-
95148
```
96149

150+
97151
## Health checks
98-
Health checks should be deployed for every process. Currently there are only 2:
99-
1. historicla loading + event ingestion (main routine)
152+
The main thread spins up a lightweight HTTP server available for health checks.
100153
```bash
101154
curl http://localhost:8080/health
102155
```
103-
2. event processing (transform.go)
104-
```bash
105-
curl http://localhost:8081/health
106-
```
107-
108-
## Deploying
109-
1. stop server + update
110-
2. run database migration [doc](https://supabase.com/docs/guides/deployment/database-migrations#deploy-your-project)
111-
3. if you don't want to refetch all historical events, update `go-indexer/config/config.yaml` to fetch events after a specific block
112-
4. restart server
113-
```
114-
115-
```
116-
117-
# API server
118-
Simple nextjs app to provide an API for the indexer database
119-
120-
## Prereqs
121-
- node.js
122-
- npm
123-
124-
## Start API server
125-
1. install dependencies
126-
```bash
127-
npm install
128-
```
129-
130-
2. copy `.env.example`
131-
- SUPABASE_URL: http url for postgres db
132-
- SUPABASE_ANON_KEY: read-only key
133-
- API_KEY: random string to give access to API
134-
135-
3. start server
136-
```bash
137-
npm run dev
138-
```
139-
Visit the api at `localhost:3000/v1`
140-
141-
## API docs and schema
142-
Docs served at `/docs`. Generated automatically
143-
144156

145-
# Data Testing
157+
## Data Testing
146158
Manaul check which can be run to load all historical data locally, and compare that production matches the historical load.
147159

148160
1. Start with fresh local DB

docs/Architecture.md

Lines changed: 0 additions & 56 deletions
This file was deleted.

docs/architecture.png

258 KB
Loading

0 commit comments

Comments
 (0)