Uniswap V4 Hooks DCI
Complete Indexing Solution for All Uniswap V4 Hooks
Introduction
What is the Hooks DCI?
The Hooks DCI (Dynamic Contract Indexer) is Tycho's specialized indexing plugin for all Uniswap V4 hooks. It extends the standard DCI with capabilities designed specifically for hooks, including automatic balance tracking, sophisticated entrypoint generation, and optional external metadata collection.
The Hooks DCI is required for indexing all Uniswap V4 hooks. It provides a complete solution with sensible defaults that work out-of-the-box for most hooks, and optional extension points for hooks with advanced requirements.
In this document, we break down UniswapV4 pools into different categories, describe the challenges to index each one, and provide a guide on how to index pools that need custom integration to be indexed by Tycho.
Hook Types:
Before diving into the solution, we need to understand the different categories that differentiate hooks Indexing:
1. Composable vs Non-Composable
Composable Hooks: Work with empty
hookDatain swapsNon-Composable Hooks: Require custom
hookDatafor before or after swap hooks
2. Internal vs External Liquidity
Internal Liquidity: Tokens accounting in PoolManager as ERC6909 claims
External Liquidity: Tokens in external contracts, outside UniswapV4's Pool Manager.
We define deeper these categories further on the Hook Classification section
Currently, Tycho only supports Composable Hooks. Non-composable support is coming soon.
Why Hooks DCI Exists
The standard DCI works well for self-contained protocols, but Uniswap V4 hooks require some extra steps for correct indexing. For Tycho to index all the state necessary for simulating each hook, it needs to have well-defined Entrypoints that cover all the possible Hook execution paths. This was achieved by adding:
V4-specific entrypoint generation with custom swap encoding and state overrides, aiming to cover all the paths that a hook might take
Flexible metadata collection supporting both internal (automatic) and external (custom) liquidity sources
Registry-based extension system for hooks with specialized requirements
State-aware processing to optimize performance and handle failures gracefully
On Background & Concepts section below, we provide detailed explanations of hook types and architecture.
Background & Concepts
Uniswap V4 Hooks Primer
Uniswap V4 introduces hooks - smart contracts that can execute custom logic at specific points in the pool lifecycle. Hooks enable powerful features like:
Dynamic fees based on market conditions
Custom oracle integrations
Liquidity management strategies
Integration with external DeFi protocols
Each hook address encodes permissions in its bytes, indicating which lifecycle events it handles:
The Hooks DCI only processes hooks with swap permissions (beforeSwap and/or afterSwap), as these are the ones that manage liquidity and affect swap behavior.
Hook Classification
Understanding hook types helps determine what (if anything) you need to implement for your hook.
1. Composable vs Non-Composable Hooks
1.1 - Composable Hooks (Currently Supported)
Composable hooks do NOT require custom calldata (hookData) to be passed during swaps. They work with empty or default hookData.
Examples:
Dynamic fee hooks (calculate fees from pool state)
Oracle integration hooks (read from external oracles, no user input needed)
Internal liquidity management hooks
Eulerswap's external liquidity hooks
1.2 - Non-Composable Hooks (Future Support)
⚠️ Not Currently Supported: Non-composable hooks REQUIRE specific calldata to be passed in hookData for each swap. Support for these hooks is planned for a future release.
Examples (not yet supported):
Hooks requiring user signatures per swap
Intent-based routing hooks
Hooks with swap-specific configuration
2. Internal vs External Liquidity (Primary Classification)
This is the key distinction that determines what you, as a hook integrator, need to implement.
2.1 - Internal Liquidity Hooks
✅ No Custom Implementation Required - Composable Internal liquidity hooks are automatically indexed by Tycho.
Characteristics:
All liquidity tracked in PoolManager as ERC6909 claims
Balances automatically extracted from blockchain state
No external calls needed for Metadata (Pool balances and Limits)
Works with default orchestrator out-of-the-box
How It Works:
Hooks DCI extracts pool balances from
BlockChanges.balance_changesDefault orchestrator's
enrich_metadata_from_block_balances()builds metadata from the pool internal balanceEntrypoint generator creates state overrides for PoolManager ERC6909 only
Everything works automatically - no custom code needed
Examples:
Dynamic fee hooks using PoolManager liquidity
Hooks with custom AMM curves but standard storage
Time-weighted average price (TWAP) hooks
Most hooks that don't integrate with external DeFi
2.2 - External Liquidity Hooks
⚙️ Requires Custom Metadata Implementation
Characteristics:
Liquidity stored in external contracts (lending vaults, yield protocols, etc.)
Requires custom RPC or API calls to fetch current balances and withdrawal limits
Needs custom
MetadataRequestGeneratorandMetadataResponseParserMay need balance slot detection for accurate simulations
What You Need to Implement:
MetadataRequestGenerator- Creates RPC requests for balances/limitsMetadataResponseParser- Parses RPC responses into structured data(Optional) Custom
HookOrchestrator- Only if entrypoint encoding is non-standard
On Metadata Collection Systemwe go deeper on the Metadata collection and how you can implement to track any hook with External Liquidity. We also provide an Hook Integration Guideto guide you through the implementation steps.
Examples:
Euler Hooks: Tokens in Euler lending vaults
Yearn Integration: Tokens in Yearn vaults earning yield
Staking Hooks: Tokens locked in staking contracts
What You Need to Implement (Decision Tree)
Architecture Overview
High-Level System Diagram
Core Components
1. UniswapV4HookDCI
The main orchestrator that coordinates all hook indexing operations. It:
Filters components with swap hook permissions
Categorizes components by processing state
Coordinates metadata collection
Manages component lifecycle (success, failure, retry, pause)
Delegates to inner DCI for tracing operations
2. Metadata Orchestrator System
Purpose: Collects external metadata for hooks with external liquidity. Optional - only used when a metadata generator is registered for a hook.
For increased performance, the external data collection is split into a three-layer architecture:
Layer 1: Request Generation (Protocol-Specific - Optional)
Creates
MetadataRequestobjects specifying what data to fetchSupports different request types: Balances, Limits, TVL
Not needed for internal liquidity hooks - system uses block balances instead
Layer 2: Request Execution (Transport-Specific)
Implemented by providers (e.g.,
RPCMetadataProvider)Handles batching, deduplication, retries
Routes requests to appropriate backends (RPC, HTTP APIs)
Layer 3: Response Parsing (Protocol-Specific - Optional)
Converts raw responses into structured metadata
Handles errors and validation
Fallback for Internal Liquidity: When no metadata generator is registered, the default orchestrator automatically enriches metadata from BlockChanges.balance_changes - no RPC calls needed.
3. Hook Orchestrator Registry
Maps hook addresses/identifiers to orchestrators that handle component processing. The default orchestrator (DefaultUniswapV4HookOrchestrator) handles both internal and external liquidity hooks automatically.
Lookup Priority:
By Hook Address: Direct mapping for specific hook deployments
By Identifier: String-based lookup (e.g., "euler_v1")
Default Orchestrator: Fallback for all hooks
Orchestrator Responsibilities:
Generating entrypoints with appropriate tracing parameters
Injecting balances and limits into components
Updating component state attributes
Key Feature: The default orchestrator's enrich_metadata_from_block_balances() method automatically extracts balances from blockchain state for hooks without custom metadata generators. This means internal liquidity hooks work with zero custom code.
Internal vs External Liquidity Paths
The system automatically chooses the appropriate path based on whether a metadata generator is registered:
Path A: Internal Liquidity (Automatic)
Path B: External Liquidity (Custom Metadata)
Key Takeaway: The only difference is Step 3 (Metadata Collection). The rest of the flow is identical. This is why internal liquidity hooks require no custom implementation - they automatically use Path A.
Metadata Collection System
The metadata collection system uses a three-layer architecture that separates protocol-specific logic from transport concerns. This system is optional and only activated when you register a custom metadata generator for your hook.
Two Paths for Metadata Collection
Path A: Internal Liquidity (Automatic - No Implementation Needed)
System checks:
generator_registry.get_generator(component)→NoneDefault orchestrator calls
enrich_metadata_from_block_balances()Balances extracted from
BlockChanges.balance_changesZero RPC calls, zero custom code required
Path B: External Liquidity (Requires Implementation)
System checks:
generator_registry.get_generator(component)→Some(generator)Generator creates RPC requests for external data
Provider executes requests
Parser converts responses to structured metadata
Requires implementing Generator + Parser traits
Layer 1: Request Generation (Protocol-Specific - External Liquidity Only)
Purpose: Create metadata requests specific to your hook's data needs.
Interface:
Metadata Request Types:
ComponentBalance: Fetch token balances for the componentLimits: Fetch maximum swap amounts (withdrawal limits, liquidity caps)Tvl: Total value locked calculationCustom: Extensible for hook-specific needs
Euler Example - Balance Request:
Euler Example - Limits Request with State Overrides:
The lens contract pattern allows querying multiple values in a single RPC call using a custom contract deployed via state overrides.
Layer 2: Request Execution (Transport-Specific)
Purpose: Execute metadata requests efficiently, handling batching and retries.
Interface:
RPCMetadataProvider Features:
Batching: Groups multiple
eth_callrequests into JSON-RPC batchesDeduplication: Avoids duplicate requests in the same batch
Retry Logic: Exponential backoff for transient RPC failures
Concurrency Limiting: Prevents overwhelming RPC endpoints
Configuration:
Request Flow:
Layer 3: Response Parsing (Protocol-Specific)
Purpose: Convert raw RPC responses into structured metadata.
Interface:
Metadata Value Types:
Euler Example - Balance Parsing:
Euler Example - Limits Parsing:
Assembled Metadata
All parsed metadata for a component is assembled into:
Note that each field is Option<Result<...>>:
None: Metadata type not requestedSome(Ok(...)): Successfully collectedSome(Err(...)): Collection failed (triggers component failure)
4.3 Hook Orchestrators
Hook orchestrators coordinate the processing of components, including entrypoint generation and metadata injection.
Orchestrator Responsibilities
Entrypoint Generation: Create
EntryPointWithTracingParamsfor tracingBalance Injection: Add balances to
ProtocolComponentfor storageLimits Injection: Provide limits for RPC query optimization
State Updates: Modify component state attributes as needed
Interface
Parameters:
block_changes: Mutable reference to modify transactions and componentscomponents: Components to process in this callmetadata: Collected external metadata (balances, limits, TVL)generate_entrypoints:truefor full processing,falsefor balance-only
Registry Lookup Mechanisms
The HookOrchestratorRegistry provides multiple lookup strategies:
1. By Hook Address (Highest Priority)
2. By Hook Identifier (Medium Priority)
3. Default Orchestrator (Lowest Priority)
Lookup Order:
Try hook address lookup
Try hook identifier lookup (from component static attributes)
Fall back to default orchestrator
Return error if no orchestrator found
Default Orchestrator
The DefaultUniswapV4HookOrchestrator handles most hook types:
Features:
Extracts balances from block changes for components without external metadata
Delegates entrypoint generation to
UniswapV4DefaultHookEntrypointGeneratorInjects balances and limits into
BlockChangesHandles both full processing and balance-only updates
When to Use Custom Orchestrator:
Hook requires special entrypoint encoding
Balance/limit data needs transformation before injection
Component state updates follow custom logic
Hook uses non-standard token accounting
Euler Example - When Default is Sufficient:
For Euler hooks, the default orchestrator works well because:
Balances come directly from metadata (no transformation needed)
Limits are standard max withdrawal amounts
Entrypoints follow standard Uniswap V4 swap encoding
No special state updates required
Therefore, Euler only requires custom metadata generator/parser, not a custom orchestrator.
4.4 Entrypoint Generation
Entrypoints define the calls that will be traced to understand how a component behaves under different conditions.
Entrypoints allow Tycho to:
Simulate swaps at various amounts to understand pricing curves
Test edge cases (e.g., swaps at 1%, 50%, 95% of liquidity)
Understand touched contracts and state that are necessary for reproducing a hook's behavior
For hooks with external liquidity, accurate entrypoints require:
Correct balance overwrites (both in PoolManager and external contracts)
Appropriate swap amounts based on limits
State overrides to simulate external contract states
Swap Amount Estimation
The system supports two estimation strategies:
1. Limits-Based Estimation (Preferred)
When limits are available, generate samples at:
1% of limit (test small swaps)
10% of limit (test medium swaps)
50% of limit (test large swaps)
95% of limit (test near-maximum swaps)
2. Balance-Based Estimation (Fallback)
When limits are unavailable, generate samples at:
1% of balance
2% of balance
5% of balance
10% of balance
Euler Example - Limits-Based Amounts:
V4MiniRouter Pattern
For Uniswap V4, entrypoints use a custom router deployed via state overrides:
Purpose: Execute swap operations against the PoolManager with proper token settlements
Pattern:
ERC6909 Overwrites
Uniswap V4 uses ERC6909 for internal PoolManager accounting. To simulate swaps, we must set balances:
Balance Slot Detection
For hooks with external liquidity, tokens may need balances set in external contracts:
Optional Feature: EVMBalanceSlotDetector
Euler Example - Balance Overwrites:
For Euler hooks, tokens are held in external vaults. The entrypoint generator:
Detects balance slots for vault tokens (wstETH, WETH, etc.)
Overwrites those slots with swap amounts
Ensures PoolManager has ERC6909 balances
Simulates full swap flow including vault withdrawals
This allows accurate tracing even though liquidity is external to PoolManager.
\
Last updated
Was this helpful?

