An app state migration service that allows projects to migrate public and private state from an old rollup to a new rollup. Continuum indexes events from Aztec contracts and exposes them via a REST API for migration attestation.
┌─────────────┐ ┌─────────────┐ ┌─────────────────┐
│ MongoDB │◄───│ Indexer │◄───│ Fastify API │
│ (Events, │ │ (Cron Jobs)│ │ (REST Server) │
│ SyncState,│ │ │ │ :3004 │
│ Migration)│ │ │ │ │
└─────────────┘ └─────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ Aztec Old Node │
│ (Event Source) │
└─────────────────┘
- Docker and Docker Compose
- (Optional) Bun or Node.js for local development
-
Clone the repository
git clone <repository-url> cd continuum
-
Configure environment
cp .env.example .env # Edit .env with your settings -
Configure artifacts
Edit
indexer/artifacts.jsonto add your Aztec contract artifacts:{ "artifacts": [ { "id": "my-contract", "name": "My Contract", "artifact_path": "./artifacts/MyContract.json", "addresses": { "devnet": "0x...", "testnet": "0x...", "sandbox": "0x..." }, "enabled": true, "event_types": ["Transfer", "Mint"], "start_block": { "devnet": 1000, "testnet": 5000, "sandbox": 0 } } ] } -
Start all services
docker compose up -d
This starts:
- MongoDB on port 27017
- Event Indexer (background service)
- REST API on port 3004
-
Check service status
docker compose ps
-
View logs
# All services docker compose logs -f # Specific service docker compose logs -f indexer docker compose logs -f api docker compose logs -f mongodb
-
Stop services
docker compose down
To also remove the MongoDB volume (WARNING: deletes all data):
docker compose down -v
docker-compose.override.yml is automatically merged in when you run docker compose up -d locally. It mounts the source files as volumes so changes are reflected without rebuilding.
# First time — build images to bake in dependencies
docker compose up -d --build
# After any code change — just restart, no rebuild
docker compose up -d
# Only rebuild when adding/removing npm packages
docker compose up -d --buildIn production, explicitly pass only the base file so the override is ignored and self-contained built images are used:
docker compose -f docker-compose.yml up -dAll endpoints are on port 3004.
curl http://localhost:3004/health# Get all events for an artifact
curl http://localhost:3004/events/my-contract
# Get specific event type
curl http://localhost:3004/events/my-contract/Transfer
# With pagination and block range
curl "http://localhost:3004/events/my-contract?event_type=Transfer&from_block=1000&to_block=2000&page=1&limit=100"# Get sync status for all artifacts
curl http://localhost:3004/sync
# Get sync status for specific artifact
curl http://localhost:3004/sync/my-contract# List all registered artifacts
curl http://localhost:3004/artifacts
# Get specific artifact details
curl http://localhost:3004/artifacts/my-contract# Upload contract ABI and extract events with selectors
curl -X POST http://localhost:3004/contracts/upload \
-H "Content-Type: application/json" \
-d '{
"name": "MyContract",
"abi": { /* Noir ABI JSON */ }
}'
# List all uploaded contracts
curl "http://localhost:3004/contracts?page=1&limit=10"
# Get specific contract by ID
curl http://localhost:3004/contracts/<contract-id>
# Find event by selector
curl http://localhost:3004/contracts/event/0x12345678Migration keys let users prove ownership of their old-rollup wallet when Aztec upgrades to a new rollup. Each wallet gets one key, stored securely in MongoDB.
# Register (or retrieve) a migration key for a wallet
curl -X POST http://localhost:3004/migration/register \
-H "Content-Type: application/json" \
-d '{ "walletAddress": "0x...", "network": "devnet" }'
# Check if a wallet has a key (key is masked in response)
curl http://localhost:3004/migration/0x...
# Verify a secret key → resolve to wallet address (used during new-rollup migration)
curl -X POST http://localhost:3004/migration/verify \
-H "Content-Type: application/json" \
-d '{ "secretKey": "<64-char hex key>" }'continuum/
├── docker-compose.yml # Production Docker Compose
├── docker-compose.override.yml # Dev overrides (auto-merged locally, ignored in prod)
├── docker-compose.local.yml # Alternative local-only setup
├── .env.example # Environment variables template
├── indexer/artifacts.json # Contract artifacts configuration used by the indexer
│
├── database/ # MongoDB initialization
│ └── init.js # Collections, indexes, sample data
│
├── indexer/ # Event indexer
│ ├── Dockerfile # Indexer container
│ ├── index.ts # Scheduler entry point
│ ├── lib/ # Indexer logic (EventIndexer, ArtifactRegistry)
│ └── shared/ # Shared utilities (aztecNode, mongodb, utils)
│
├── api/ # REST API server (Fastify, port 3004)
│ ├── Dockerfile # API container
│ ├── app.js # Fastify app entry
│ ├── routes/ # Route handlers
│ │ ├── health/
│ │ ├── contracts/
│ │ ├── listings/
│ │ └── migration/ # Migration key endpoints
│ └── plugins/ # Fastify plugins (mongodb, cors, env)
│
└── artifacts/ # Contract artifacts (user-provided .json files)
| Variable | Description | Default |
|---|---|---|
MONGO_ROOT_USERNAME |
MongoDB root username | root |
MONGO_ROOT_PASSWORD |
MongoDB root password | password |
CONTINUUM_DB_CONNECTION_STRING |
MongoDB connection string | mongodb://root:password@localhost:27017 |
CONTINUUM_DB_NAME |
Database name | continuum |
CONTINUUM_INDEXER_INTERVAL |
Indexer run interval (ms) | 30000 |
CONTINUUM_INDEXER_BLOCK_RANGE |
Blocks per batch | 14 |
CONTINUUM_AZTEC_NODE_URL_DEVNET |
Aztec node URL for devnet | - |
CONTINUUM_AZTEC_NODE_URL_TESTNET |
Aztec node URL for testnet | - |
CONTINUUM_AZTEC_NODE_URL_SANDBOX |
Aztec node URL for sandbox | http://sandbox:8080 |
CONTINUUM_API_PORT |
API server port | 3004 |
CONTINUUM_ARTIFACTS_PATH |
Path to artifacts directory | ./artifacts |
Each artifact in artifacts.json supports:
id: Unique identifier for the artifactname: Human-readable nameartifact_path: Path to the contract artifact JSON fileaddresses: Contract addresses per network (devnet, testnet, sandbox)enabled: Whether to index this artifactevent_types: List of event types to indexstart_block: Block to start indexing from per network
| Collection | Purpose |
|---|---|
events |
All indexed contract events |
sync_state |
Last indexed block per artifact per network |
artifacts |
Artifact configuration and metadata |
contracts |
Uploaded contract ABIs and extracted event selectors |
migration_keys |
Wallet → secret key mappings for rollup migration |
See database/init.js for the full schema and indexes. Collections and indexes are created automatically when MongoDB first initializes.
# Check MongoDB is running
docker compose ps mongodb
# Check MongoDB logs
docker compose logs mongodb
# Connect to MongoDB shell
docker compose exec mongodb mongosh -u root -p password# Check indexer logs
docker compose logs -f indexer
# Verify artifact configuration
curl http://localhost:3004/artifacts
# Check sync status
curl http://localhost:3004/sync# Check API logs
docker compose logs -f api
# Verify API is running
curl http://localhost:3004/healthMIT
Contributions are welcome! Please open an issue or pull request.