Continuum solves one of the hardest problems in rollup development: how do users carry their state forward when aztec rollup upgrades
When an Aztec L2 rollup migrates to a new version, all on-chain state is reset. Users who held NFTs, tokens, or other assets on the old rollup need a way to prove those holdings and recreate them on the new rollup — without trusting any single party and without requiring access to the old rollup's private data.
Continuum provides a cryptographic bridge between old and new rollup state using:
- An event indexer that reads public events from the old rollup
- An attester service that signs user state claims with a Schnorr key
- A Noir smart contract on the new rollup that verifies those signatures and records the migration
Continuum is made of three off-chain services and a set of Noir contracts:
- MongoDB stores indexed events, sync cursors, contract ABIs, and old→new collection mappings.
- Indexer polls the old rollup’s Aztec node for public events and writes them to MongoDB.
- Fastify API exposes admin and public endpoints; it signs migration claims with a Schnorr attester key.
- Noir contracts live on both rollups. The old collection emits ownership and registration events; the new collection verifies Continuum’s attestation and mints the migrated asset.
- Register contracts: upload the old collection ABI to the API and register the old→new collection address mapping.
- Create a migration secret: the user calls
/migration/new-secretand saves the secret. - Register the commitment on the old rollup: the user calls
MigrationRegistry.register_migration(collection, commitment)(or the NFT contract’s own registration function), wherecommitment = Poseidon2("NFTMR" ‖ secret). - Index events:the indexer crawls the old rollup and stores
TransferandMigrationRegisteredevents. - Request signed claim data: the user reveals the secret to
/request_data; the API recomputes the commitment, resolves the verified owner, and returns Schnorr-signed attestations for each owned token. - Claim on the new rollup:the user calls
newNFT.migrate_and_claim(token_id, commitment, signature), which verifies the signature and mints the token.
For a complete step-by-step example, see e2e-tests/src/sandbox/index.ts. For a recent testnet run, see e2e-tests/src/testnet/run_logs_july_1.txt.
- Docker and Docker Compose
- Bun or Node.js for local development
-
Clone the repository
git clone git@github.com:raven-house/continuum.git cd continuum -
Configure environment
cp .env.example .env
If trying on Aztec Sandbox, download Aztec CLI from https://docs.aztec.network/developers/getting_started_on_local_network and start sandbox using
aztec start --local-networkSet
AZTEC_NETWORK=sandboxin.envfile -
Start all services
docker compose up -d
This starts:
- MongoDB on port 27017
- Event Indexer (background service)
- REST API on port 3004
-
Register contracts for indexing
Upload each Aztec contract ABI through the API. The API extracts event selectors and stores the indexing configuration in MongoDB for the indexer.
curl -X POST http://localhost:3004/contracts/upload \ -H "Content-Type: application/json" \ -d '{ "artifact_id": "my-contract", "name": "My Contract", "abi": { /* Noir ABI JSON */ }, "enabled": true, "event_types": ["Transfer", "Mint"], "start_block": { "testnet": 5000, "sandbox": 0 } }'
Omit
event_typesor pass an empty array to index all events in the ABI. -
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 downTo also remove the MongoDB volume (WARNING: deletes all data):
docker compose down -vList all docker volumes
docker volume lsdocker-compose.yml is the default local development stack. 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 --build# Run contract test cases
aztec testIn production, explicitly use the production compose file so self-contained built images are used and source code is not mounted from the host:
docker compose -f docker-compose.prod.yml up -dAll endpoints are on port 3004.
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
API liveness |
| POST | /contracts/upload |
Upload ABI and indexing config (admin) |
| GET | /contracts / /contracts/:id / /contracts/event/:selector |
Query uploaded contracts |
| POST | /collections/register |
Map old→new collection address (admin) |
| GET | /collections / /collections/:newAddress |
List / lookup mappings |
| GET | /migration/new-secret |
Generate migration secret + commitment |
| POST | /migration/commitment |
Recompute commitment from secret |
| POST | /request_data |
Request signed token attestations |
| GET | /attester |
Get the attester public key |
# Upload contract ABI and extract events with selectors
curl -X POST http://localhost:3004/contracts/upload \
-H "Content-Type: application/json" \
-d '{
"artifact_id": "my-contract",
"name": "MyContract",
"abi": { /* Noir ABI JSON */ },
"enabled": true,
"event_types": ["Transfer"],
"start_block": {
"testnet": 5000,
"sandbox": 0
}
}'
# 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/0x12345678# Check indexer logs
docker compose logs -f indexer
# Verify registered contracts
curl "http://localhost:3004/contracts?page=1&limit=10"
# Check indexer health (inside the indexer container)
curl http://localhost:8080/health# Check API logs
docker compose logs -f api
# Verify API is running
curl http://localhost:3004/healthnft_contract tests need the compiled generic_proxy artifact in their target folder:
cd contracts/generic_proxy
aztec-nargo compile
cp target/generic_proxy-GenericProxy.json ../nft_contract/target/
cd ../nft_contract
aztec testAddress of Migration registry contract on testnet version 5.0.0-rc2 is 0x0f10ac27189276a91039bd47317783b1f76ce2fa1f26b89be1df464c08a5b3b3
MIT
Contributions are welcome! Please open an issue or pull request.
