MetaLayer
A chain-agnostic metadata and indexing layer for cross-chain dApps.
Overview
MetaLayer is an open-source protocol and toolkit that sits between blockchains and applications, normalizing on-chain data — token metadata, NFT attributes, contract events, and transaction context — into a single, queryable format. Instead of writing separate indexers for every chain you support, MetaLayer gives you one consistent API and data schema across all of them.
It's built for teams shipping multi-chain dApps, wallets, explorers, and analytics tools who are tired of reconciling inconsistent RPC responses, mismatched decimals, and chain-specific event formats by hand.
Features
- Unified data schema — token, NFT, and contract metadata normalized into one format regardless of source chain
- Multi-chain indexing — pluggable adapters for EVM chains, with a common interface for adding non-EVM chains
- Real-time event streaming — subscribe to normalized on-chain events via WebSocket
- GraphQL & REST API — query indexed data without running your own node infrastructure
- Caching layer — configurable TTLs to reduce redundant RPC calls
- Self-hostable — run your own instance, or point at a hosted endpoint
Architecture
Blockchain RPCs ──► Chain Adapters ──► Normalizer ──► Index Store ──► API Layer ──► Your App
(EVM, etc.) (schema map) (Postgres) (GraphQL/REST)
Getting Started
Prerequisites
- Node.js >= 18
- PostgreSQL >= 14
- An RPC endpoint for each chain you want to index (e.g. via Alchemy, Infura, or your own node)
Installation
git clone https://github.com/<org>/MetaLayer.git
cd MetaLayer
npm install
cp .env.example .env # add your RPC URLs and DB connection string
npm run migrate
npm run devUsage
Query normalized token metadata across chains:
curl -X POST http://localhost:4000/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ token(address: \"0x...\", chain: \"ethereum\") { name symbol decimals totalSupply } }"}'Subscribe to live contract events:
const socket = new MetaLayerClient({ endpoint: "ws://localhost:4000" });
socket.subscribe({ chain: "polygon", contract: "0x..." }, (event) => {
console.log(event);
});Project Structure
MetaLayer/
├── src/
│ ├── adapters/ # per-chain RPC adapters
│ ├── normalizer/ # raw data -> unified schema
│ ├── api/ # GraphQL + REST resolvers
│ └── stream/ # WebSocket event pipeline
├── tests/
└── docs/
Configuration
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
Postgres connection string | — |
RPC_ETHEREUM |
RPC endpoint for Ethereum | — |
RPC_POLYGON |
RPC endpoint for Polygon | — |
CACHE_TTL |
Cache lifetime for indexed records (sec) | 300 |
PORT |
API server port | 4000 |
Contributing
Contributions are welcome. Please open an issue or submit a pull request.
- Fork the repo
- Create a feature branch (
git checkout -b feature/your-feature) - Commit your changes
- Push and open a PR
License
This project is licensed under the MIT License.