Skip to content

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
⚙️ Processor Configuration (processor.ts)
  • 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
🧠 Core Business Logic (BatchProcessor.ts)
  • 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
💰 Pricing Engine (pricing.ts)
  • 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
🏗️ Type System (types.ts)
  • Defines the complete state structure for Uniswap V2 processing
  • Extends base protocol state with pool-specific data
  • Ensures type safety across all components
📊 Database Schema (schema.graphql)
  • 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:

  1. Database Layer: Persistent storage of pool configs, state, and balances
  2. Memory Layer: Fast access to active balances and processing queues
  3. 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:

  1. Configuration Setup - Prepare your Uniswap V2 config and environment
  2. Schema Validation - Understand how config is validated with Zod
  3. Runtime Mapping - See how config becomes runtime objects
  4. Processor Initialization - Learn the EVM batch processor setup
  5. Main Execution - Understand the entry point and startup flow
  6. Core Business Logic - Deep dive into the BatchProcessor engine
  7. Utility Functions - Explore pool management and pricing calculations
  8. 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