Complete Uniswap V2 Adapter Tutorial - Deep Dive into Every Component
Tutorial Overview
This tutorial covers a sophisticated blockchain indexing system that tracks Uniswap V2 liquidity pools for time-weighted balance calculations and real-time yield tracking. You'll learn how to build a production-ready adapter that processes millions of blockchain events efficiently.
What You'll Build
A complete Uniswap V2 indexer that:
- Processes blockchain events in real-time (Swap and Transfer events)
- Calculates time-weighted balances for accurate yield tracking
- Handles complex pricing calculations using CoinGecko integration
- Manages state across multiple pools with database persistence
- Sends structured data to the Absinthe API for analytics
System Architecture Overview
├── src/
│ ├── main.ts # 🚀 Entry point
│ ├── processor.ts # ⚙️ Subsquid configuration
│ ├── BatchProcessor.ts # 🧠 Core business logic
│ ├── model/ # 📊 Database models
│ ├── utils/
│ │ ├── pool.ts # 🏊 Pool state management
│ │ ├── pricing.ts # �� Price calculations
│ │ └── types.ts # ��️ Type definitions
│ └── abi/ # 📜 Smart contract ABIs
├── schema.graphql # ��️ Database schema
└── package.json # �� Dependencies
Core Components
🚀 Entry Point (main.ts)- Initializes the processor with validated configuration
- Sets up API client with rate limiting
- Bootstraps chain metadata and starts the indexer
- Configures Subsquid's EVM batch processor
- Sets up event filtering for Swap and Transfer events
- Defines block ranges and finality confirmations
- Manages gateway and RPC endpoint connections
- Orchestrates the entire processing pipeline
- Manages protocol state initialization and updates
- Routes events to appropriate handlers
- Implements periodic balance flush system
- Handles error recovery and batch finalization
Supporting Utilities
🏊 Pool State Management (pool.ts)- Auto-discovers pool configuration from blockchain
- Loads and saves state to database with proper relationships
- Synchronizes in-memory state with blockchain data
- Handles graceful initialization of new pools
- Calculates LP token prices using reserve ratios
- Integrates with CoinGecko for historical token prices
- Implements intelligent caching to minimize API calls
- Handles edge cases like zero liquidity pools
- Defines the complete state structure for Uniswap V2 processing
- Extends base protocol state with pool-specific data
- Ensures type safety across all components
- Structured entity relationships for pools, tokens, and state
- Optimized for fast queries and data integrity
- Supports complex balance tracking requirements
Key Technical Concepts
Time-Weighted Balance Tracking
The system tracks how long users hold liquidity positions to calculate accurate yields:
- Balance Windows: Time periods showing user's LP token holdings
- Trigger Types: Events that create windows (transfers vs. time exhaustion)
- Weighted Calculations: Balance × Time × Price = Weighted Value
- Periodic Snapshots: Automatic windows every 6 hours even without activity
Event Processing Pipeline
Swap Events (Token exchanges):
- Decode swap amounts and calculate total volume
- Fetch historical token prices from CoinGecko
- Calculate gas fees in USD using ETH price
- Create structured transaction records for API
Transfer Events (LP token movements):
- Update active user balances in memory
- Calculate LP token value using current reserves
- Create time-weighted balance windows
- Handle mint/burn/transfer scenarios
State Management Strategy
Three-Layer Architecture:
- Database Layer: Persistent storage of pool configs, state, and balances
- Memory Layer: Fast access to active balances and processing queues
- Blockchain Layer: Real-time synchronization with contract state
Initialization Flow:
- Check database for existing pool configuration
- Auto-discover missing data from blockchain contracts
- Load user balances and processing state
- Resume from last processed block
Performance Optimizations
Price Caching System:
- Day-level bucketing reduces API calls by 24x
- Process-wide cache shared across all pools
- Parallel price fetching for multiple tokens
Batch Processing:
- Processes multiple blocks in single database transaction
- Queues events in memory, sends to API at batch end
- Conditional database updates only when needed
Error Handling:
- Graceful degradation on pricing failures
- Per-pool error isolation prevents cascading failures
- Comprehensive logging for debugging and monitoring
Configuration-Driven Design
Environment Setup
The adapter reads pool configurations from ABS_CONFIG
environment variable, supporting:
- Multiple pools per chain
- Flexible block ranges per pool
- Token metadata with CoinGecko integration
- Preferred pricing tokens for calculations
Schema Validation
Zod schemas ensure configuration correctness:
- Required fields validation at startup
- Type coercion for numeric values
- Clear error messages for misconfiguration
Chain Metadata Integration
Automatic mapping from chain IDs to:
- Gateway URLs for fast block data
- RPC endpoints for on-demand queries
- Chain names and identifiers for API
Production Considerations
Scalability Features
- Database Schema Isolation: Each processor uses unique schema
- Horizontal Scaling: Multiple instances can run different chains
- Memory Efficiency: Minimal state kept in memory
- API Rate Limiting: Built-in throttling prevents API abuse
Monitoring & Observability
- Structured Logging: Block ranges, processing times, error details
- Error Recovery: Graceful handling of temporary failures
- State Persistence: Can resume from any point after restart
- Health Checks: Validates configuration and dependencies
Data Quality Assurance
- Zero Division Protection: Safe handling of empty pools
- Price Validation: Fallbacks for missing or invalid prices
- State Consistency: Atomic updates across related entities
- Audit Trail: Complete history of balance changes and triggers
Tutorial Structure
This tutorial is organized into logical sections that build upon each other:
- Configuration Setup - Prepare your Uniswap V2 config and environment
- Schema Validation - Understand how config is validated with Zod
- Runtime Mapping - See how config becomes runtime objects
- Processor Initialization - Learn the EVM batch processor setup
- Main Execution - Understand the entry point and startup flow
- Core Business Logic - Deep dive into the BatchProcessor engine
- Utility Functions - Explore pool management and pricing calculations
- Database Design - Understand the entity relationships and schema
Each section includes real code examples, detailed explanations, and practical insights from production usage.
Prerequisites
Before starting this tutorial, you should be familiar with:
- TypeScript and modern JavaScript patterns
- Blockchain concepts (events, transactions, blocks)
- Uniswap V2 mechanics (LP tokens, reserves, pricing)
- Database design and entity relationships
- API integration and rate limiting concepts