uniswapv2

package module
v0.0.0-...-3f9f1c9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 23, 2025 License: MIT Imports: 13 Imported by: 0

README

Uniswap V2 In-Memory Indexing System

A high-performance, resilient, and secure Go service for tracking Uniswap V2-style liquidity pools in real-time. At its core, the package processes a live stream of blockchain blocks to maintain an in-memory, thread-safe registry of vetted pools and their current reserves. This provides consuming applications with a consistently up-to-date, queryable snapshot of on-chain liquidity, making it a robust component for DeFi analytics platforms, monitoring tools, or trading bots.

This is not just a simple log parser; it is a production-grade engine built with a strong emphasis on security, scalability, and operational resilience.


Core Features

  • Secure by Design: Does not blindly trust any contract. Pools are only initialized if they originate from a curated, whitelisted list of trusted DEX factories, preventing interaction with malicious or non-standard contracts.
  • Scalable & Resilient: Utilizes an asynchronous, batch-based architecture to initialize new pools. This decouples slow network I/O from the main block processing loop, ensuring the system can never fall behind the head of the chain, even during periods of intense on-chain activity.
  • Self-Healing: A background reconciliation loop periodically re-validates the state of all tracked pools against the blockchain. This makes the system fault-tolerant, automatically correcting any state drift caused by missed events or temporary node issues.
  • Highly Observable: Exposes a comprehensive set of Prometheus metrics for monitoring system health, performance, and data integrity. This includes critical metrics like block processing lag, initialization queue depth, and error rates by type.
  • High-Performance: Employs a data-oriented design for the in-memory registry, ensuring CPU-cache efficiency for fast data access. Read-only views of the entire system state are provided through a lock-free atomic.Pointer, preventing readers from ever blocking writers.
  • Extensible: All external dependencies (ETH client, pool initializer, log parsers, etc.) are injected, making the system highly testable and easy to integrate into a larger infrastructure.

Architectural Overview

The system is composed of several key components with a clear separation of concerns:

  • UniswapV2System: The central orchestrator. It manages the main event loop, handles concurrency with a sync.RWMutex, and coordinates all background processes (initializer, pruner, reconciler).
  • PoolInitializer: A configurable "gatekeeper" responsible for safely initializing new pools. It is configured with a list of KnownFactory addresses and their associated fees.
  • UniswapV2Registry: A highly efficient, data-oriented in-memory database that stores the state of all tracked pools.
  • reserves, logs, errors, metrics: Supporting packages that provide specialized, decoupled functionality.
Operational Model

The system is designed to be a core engine that operates under two key assumptions, with responsibilities cleanly separated between this engine and its surrounding infrastructure:

  1. A Reliable Block Stream is Provided: The system assumes it is fed a complete, ordered, and gap-free stream of blocks from an upstream "Block Provider." That provider is responsible for handling node connections, persistence, and historical catch-up logic.
  2. Processing Speed is Decoupled from Block Time: The system solves the challenge of keeping pace with the chain by separating work into a "fast path" (synchronous reserve updates) and a "slow path" (asynchronous, batch-based initialization of new pools), ensuring it can never fall behind.

Key Design Decisions (The "Why")

This project's value comes from a few key architectural decisions:

1. Why use a Factory Whitelist? (Security)

We explicitly reject pools that do not originate from a known, trusted factory address. This is the cornerstone of the system's security. It prevents our service from interacting with malicious contracts that mimic the Uniswap V2 interface or non-standard pools with unpredictable internal logic (e.g., fee-on-transfer tokens). By verifying a pool's origin, we can be certain of its internal logic.

2. Why use an Asynchronous Initializer? (Scalability)

A block can contain thousands of new pool creation events. Initializing these synchronously would require thousands of slow network calls, causing the system to stall and fall behind the chain. By placing new pools in a queue and processing them periodically in a background batch, we ensure the main block processing loop remains fast and responsive, guaranteeing the system always keeps up with the chain's head.

3. Why use a State Reconciler? (Resilience)

Any event-based system is vulnerable to missing an event. The state reconciler is a self-healing mechanism that runs periodically, re-fetching the on-chain state for all known pools and correcting any discrepancies. This ensures that even if events are missed, the system's state will always converge to the source of truth.

Usage Example

package main

import (
    "context"
    "time"

    "[github.com/Iwinswap/iwinswap-uniswap-v2-system/uniswapv2](https://github.com/Iwinswap/iwinswap-uniswap-v2-system/uniswapv2)"
    "[github.com/Iwinswap/iwinswap-uniswap-v2-system/initializer](https://github.com/Iwinswap/iwinswap-uniswap-v2-system/initializer)"
    "[github.com/Iwinswap/iwinswap-uniswap-v2-system/logs](https://github.com/Iwinswap/iwinswap-uniswap-v2-system/logs)"
    "[github.com/Iwinswap/iwinswap-uniswap-v2-system/reserves](https://github.com/Iwinswap/iwinswap-uniswap-v2-system/reserves)"
    "[github.com/prometheus/client_golang/prometheus](https://github.com/prometheus/client_golang/prometheus)"
    "[github.com/ethereum/go-ethereum/common](https://github.com/ethereum/go-ethereum/common)"
)

func main() {
    // 1. Setup dependencies and configuration
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // Example Known Factories for Ethereum Mainnet
    knownFactories := []initializer.KnownFactory{
        {
            Address:      common.HexToAddress("0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"),
            ProtocolName: "Uniswap-V2",
            FeeBps:       30,
        },
        // ... add other factories for SushiSwap, etc.
    }
    
    // Create the initializer with our trusted factory list
    poolInit := initializer.NewPoolInitializer(knownFactories)

    // Setup other dependencies (omitted for brevity)
    // var getClient func() (ethclients.ETHClient, error)
    // var newBlockEventer chan *types.Block
    // var inBlockedList func(poolAddr common.Address) bool
    // var errorHandler func(err error)
    // ... etc.

    // 2. Instantiate the system
    system, err := uniswapv2.NewUniswapV2System(
        ctx,
        "ethereum_mainnet_v2",
        prometheus.NewRegistry(),
        newBlockEventer,
        getClient,
        inBlockedList,
        poolInit.Initialize, // Inject the initializer's method
        logs.DiscoverPools,
        logs.UpdatedInBlock,
        reserves.GetReserves, // Inject the reserves fetcher
        // ... other persistence functions
        errorHandler,
        logs.SwapEventInBloom,
        1*time.Minute,  // pruneFrequency
        10*time.Second, // initFrequency
        5*time.Minute,  // resyncFrequency
    )
    if err != nil {
        panic(err)
    }

    // 3. The system is now running its background processes.
    // You can now access its state via the View() method or an API layer.
    
    // ... (run forever or until shutdown signal)
}

Metrics

Metric Description
uniswap_system_last_processed_block Heartbeat — should continuously increase.
uniswap_system_errors_total Counter of all errors, labeled by type (e.g., pool_initialization, critical_consistency).
uniswap_system_pending_initialization_queue_size Backlog of uninitialized pools. If rising, the initializer may be saturated.
uniswap_system_pools_in_registry_total Total number of pools currently tracked.

Future Development

  • Uniswap V3 Engine — concentrated liquidity, multiple fee tiers
  • Curve & Balancer Engines — modules tailored to their unique AMM logic
  • API Layer — public endpoints to serve indexed data
  • Reliable Block Provider — service ensuring gap-free block streams and re-org handling

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrPoolExists is returned when attempting to add a pool that is already in the registry.
	ErrPoolExists = errors.New("pool already exists in registry")
	// ErrPoolNotFound is returned when attempting to access a pool that is not in the registry.
	ErrPoolNotFound = errors.New("pool not found in registry")
)

Functions

This section is empty.

Types

type AddressToIDFunc

type AddressToIDFunc func(common.Address) (uint64, error)

type Config

type Config struct {
	SystemName       string
	PrometheusReg    prometheus.Registerer
	NewBlockEventer  chan *types.Block
	GetClient        GetClientFunc
	InBlockedList    InBlockedListFunc
	PoolInitializer  PoolInitializerFunc
	DiscoverPools    DiscoverPoolsFunc
	UpdatedInBlock   UpdatedInBlockFunc
	GetReserves      GetReservesFunc
	TokenAddressToID AddressToIDFunc
	PoolAddressToID  AddressToIDFunc
	PoolIDToAddress  IDToAddressFunc
	RegisterPool     RegisterPoolFunc
	RegisterPools    RegisterPoolsFunc
	ErrorHandler     ErrorHandlerFunc
	TestBloom        TestBloomFunc
	FilterTopics     [][]common.Hash
	PruneFrequency   time.Duration
	InitFrequency    time.Duration
	ResyncFrequency  time.Duration
	LogMaxRetries    int
	LogRetryDelay    time.Duration
	Logger           Logger
}

Config holds all the dependencies and settings for the UniswapV2System. Using a configuration struct makes initialization cleaner and more extensible.

type DataConsistencyError

type DataConsistencyError struct {
	SystemError
	PoolAddress common.Address
	Details     string
}

DataConsistencyError indicates a critical internal state mismatch, for example, failing to find a pool ID immediately after it was registered.

func (*DataConsistencyError) Error

func (e *DataConsistencyError) Error() string

Error provides a descriptive message, distinguishing between background and block-specific errors.

type DependencyError

type DependencyError struct {
	// Dependency is the name of the function that failed (e.g., "poolAddressToID").
	Dependency string
	// Input is the value that was passed to the failing dependency.
	Input any
	// Err is the underlying error returned by the dependency.
	Err error
}

DependencyError is returned when a function dependency required by the registry fails.

func (*DependencyError) Error

func (e *DependencyError) Error() string

func (*DependencyError) Unwrap

func (e *DependencyError) Unwrap() error

Unwrap allows the error to be inspected with errors.Is and errors.As.

type DiscoverPoolsFunc

type DiscoverPoolsFunc func([]types.Log) ([]common.Address, error)

type ErrorHandlerFunc

type ErrorHandlerFunc func(err error)

type GetClientFunc

type GetClientFunc func() (ethclients.ETHClient, error)

type GetReservesFunc

type GetReservesFunc func(ctx context.Context, poolAddrs []common.Address, client ethclients.ETHClient) (reserve0, reserve1 []*big.Int, errs []error)

type IDToAddressFunc

type IDToAddressFunc func(uint64) (common.Address, error)

type InBlockedListFunc

type InBlockedListFunc func(poolAddr common.Address) bool

type InitializationError

type InitializationError struct {
	SystemError
	PoolAddress common.Address
}

InitializationError indicates a failure during the initialization of a new pool.

func (*InitializationError) Error

func (e *InitializationError) Error() string

Error provides a descriptive message, distinguishing between background and block-specific errors.

type Logger

type Logger interface {
	Debug(msg string, args ...any)
	Info(msg string, args ...any)
	Warn(msg string, args ...any)
	Error(msg string, args ...any)
}

Logger defines a standard interface for structured, leveled logging, compatible with the standard library's slog.

type Metrics

type Metrics struct {
	// --- Tier 1: Critical System Health & Liveness ---
	LastProcessedBlock *prometheus.GaugeVec
	ErrorsTotal        *prometheus.CounterVec

	// --- Tier 2: Performance & Bottleneck Identification ---
	PendingInitQueueSize   *prometheus.GaugeVec
	BlockProcessingDur     *prometheus.HistogramVec
	PoolInitDur            *prometheus.HistogramVec
	ReconciliationDuration *prometheus.HistogramVec
	PruningDuration        *prometheus.HistogramVec

	// --- Tier 3: Data & State Integrity ---
	PoolsInRegistry  *prometheus.GaugeVec
	PoolsInitialized *prometheus.CounterVec
	PoolsPruned      *prometheus.CounterVec
}

Metrics contains all the Prometheus metrics for the UniswapV2System. Encapsulating them in a struct keeps the main system struct clean and organized.

func NewMetrics

func NewMetrics(reg prometheus.Registerer, systemName string) *Metrics

NewMetrics creates and registers all the Prometheus metrics for the system. It takes a prometheus.Registerer to allow for flexible registration (e.g., default vs. custom).

type PoolInitializerFunc

type PoolInitializerFunc func(ctx context.Context, poolAddr []common.Address, client ethclients.ETHClient) (token0, token1 []common.Address, poolType []uint8, feeBps []uint16, reserve0, reserve1 []*big.Int, errs []error)

type PoolView

type PoolView struct {
	ID       uint64   `json:"id"`
	Token0   uint64   `json:"token0"`
	Token1   uint64   `json:"token1"`
	Reserve0 *big.Int `json:"reserve0"`
	Reserve1 *big.Int `json:"reserve1"`
	Type     uint8    `json:"type"`
	FeeBps   uint16   `json:"feeBps"` // i.e 30 for 0.3%
}

type PrunerError

type PrunerError struct {
	Err    error
	PoolID uint64
}

PrunerError indicates a failure during the periodic pruning process.

func (*PrunerError) Error

func (e *PrunerError) Error() string

func (*PrunerError) Unwrap

func (e *PrunerError) Unwrap() error

type RegisterPoolFunc

type RegisterPoolFunc func(token0, token1, poolAddr common.Address) (poolID uint64, err error)

type RegisterPoolsFunc

type RegisterPoolsFunc func(token0s, token1s, poolAddrs []common.Address) (poolIDS []uint64, error []error)

type RegistrationError

type RegistrationError struct {
	InitializationError
	Token0Address common.Address
	Token1Address common.Address
}

RegistrationError is a critical error when the system fails to register a new, valid pool with the upstream persistence layer.

func (*RegistrationError) Error

func (e *RegistrationError) Error() string

Error provides a descriptive message, distinguishing between background and block-specific errors.

type SystemError

type SystemError struct {
	BlockNumber uint64
	Err         error
}

SystemError is a base type for errors originating from the UniswapV2System.

func (*SystemError) Error

func (e *SystemError) Error() string

func (*SystemError) Unwrap

func (e *SystemError) Unwrap() error

type TestBloomFunc

type TestBloomFunc func(types.Bloom) bool

type UniswapV2Registry

type UniswapV2Registry struct {
	// contains filtered or unexported fields
}

UniswapV2Registry manages a large number of v2 pools using a data-oriented design.

func NewUniswapV2Registry

func NewUniswapV2Registry() *UniswapV2Registry

func NewUniswapV2RegistryFromViews

func NewUniswapV2RegistryFromViews(views []PoolView) *UniswapV2Registry

NewUniswapV2RegistryFromViews reconstructs a UniswapV2Registry from a slice of PoolView structs. This is the primary mechanism for restoring the registry's state from a snapshot. It is highly optimized, pre-allocating all necessary memory and performing deep copies of mutable data like big.Int reserves to ensure data integrity.

type UniswapV2System

type UniswapV2System struct {
	// contains filtered or unexported fields
}

UniswapV2System is the main orchestrator that connects the data registry to the live blockchain. It handles block events, discovers and updates pools, and manages state with thread-safety.

func NewUniswapV2System

func NewUniswapV2System(ctx context.Context, cfg *Config) (*UniswapV2System, error)

NewUniswapV2System constructs a new, live system with an empty initial state.

func NewUniswapV2SystemFromViews

func NewUniswapV2SystemFromViews(ctx context.Context, cfg *Config, views []PoolView) (*UniswapV2System, error)

NewUniswapV2SystemFromViews constructs a new, live system from a snapshot of pool views. This is the primary entry point for rehydrating the system's state on startup.

func (*UniswapV2System) DeletePool

func (s *UniswapV2System) DeletePool(poolID uint64) error

DeletePool removes a pool from the UniswapV2System's internal registry.

@note This is a low-level method that must be called hierarchically, typically from a central registry manager that can orchestrate the deletion across all necessary application subsystems.

⚠️ WARNING: Calling this function in isolation WILL lead to state inconsistency, as it does not affect dependent components. For a safe, application-wide deletion, use the appropriate manager-level method.

func (*UniswapV2System) DeletePools

func (s *UniswapV2System) DeletePools(poolIDs []uint64) []error

DeletePools removes multiple pools from the UniswapV2System's internal registry.

@note This is a low-level method that must be called hierarchically, typically from a central registry manager that can orchestrate the deletion across all necessary application subsystems.

⚠️ WARNING: Calling this function in isolation WILL lead to state inconsistency, as it does not affect dependent components. For a safe, application-wide deletion, use the appropriate manager-level method.

func (*UniswapV2System) LastUpdatedAtBlock

func (s *UniswapV2System) LastUpdatedAtBlock() uint64

LastUpdatedAtBlock returns the block number of the last successfully processed block.

func (*UniswapV2System) View

func (s *UniswapV2System) View() []PoolView

View returns a copy of the latest registry view. This operation is lock-free.

type UpdateError

type UpdateError struct {
	SystemError
	PoolAddress common.Address
	PoolID      uint64
	Reserve0    *big.Int
	Reserve1    *big.Int
}

UpdateError indicates a failure to update the reserves of a known, existing pool.

func (*UpdateError) Error

func (e *UpdateError) Error() string

Error provides a descriptive message, distinguishing between background and block-specific errors.

type UpdatedInBlockFunc

type UpdatedInBlockFunc func([]types.Log) (pools []common.Address, reserve0, reserve1 []*big.Int, err error)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL