--- title: Pool CLI Commands category: cli tags: - state-management - uniswap-v2 - uniswap-v3 - uniswap-v4 - aerodrome - pancakeswap - sushiswap - swapbased - liquidity related_files: - ../rust-cli.md - ../../src/degenbot/db/__init__.py - ../../rust/crates/foundation/degenbot-db/src/schema.rs - ../../src/degenbot/uniswap/v3_liquidity_pool.py - ../../src/degenbot/uniswap/v4_liquidity_pool.py complexity: complex --- ## Overview The Pool CLI provides commands for managing liquidity pool metadata and tracking liquidity positions across multiple DEX versions (Uniswap 2, 3, and 4) on Base and Ethereum mainnet. The main command `pool update` fetches blockchain events for pool discovery and liquidity updates, synchronizing database with current pool states. ## Background: Pool Architecture ### Uniswap V2 Architecture V2 pools use a **constant product AMM** (x*y=k) formula. All liquidity is distributed evenly across entire price range. Pools are created by factories with a `PairCreated` event containing token addresses and pool address. ### Uniswap V3 Architecture V3 introduces **concentrated liquidity** - liquidity providers can allocate capital to custom price ranges using tick boundaries. This requires tracking tick-level liquidity with: - **Tick bitmap**: 256-bit words mapping initialized tick positions - **Liquidity mapping**: Net and gross liquidity at each tick for price traversal - **Tick spacing**: Constraints on allowed tick positions per fee tier Pools emit `Mint` and `Burn` events for liquidity changes. ### Uniswap V4 Architecture V4 uses a **centralized pool manager** (instead of per-pool contracts) with hooks for custom logic: - **PoolManager**: Single contract managing all pools - **PoolKey**: Keccak256 hash identifying pools (currency0, currency1, fee, tickSpacing, hooks) - **Hooks**: Customizable behavior at pool boundaries - **ModifyLiquidity**: Single event type for liquidity operations V4 uses separate `ManagedPool` database tables with PoolManager contract references. ## Commands The command vocabulary is **Rust-owned**: [`degenbot-cli`](../../rust/crates/shells/degenbot-cli/src/argv.rs) declares it over `degenbot-cli-core`'s [pool](../../rust/crates/shells/degenbot-cli-core/src/pool.rs) and [exchange](../../rust/crates/shells/degenbot-cli-core/src/exchange.rs) arms. The authoritative flag/exit-code reference is the [Rust CLI page](../rust-cli.md); the domain behaviour below is unchanged. ### `degenbot pool update` Update pool metadata and liquidity positions for all activated exchanges. ```bash degenbot pool update [--chunk SIZE] [--to-block BLOCK] [--verify-chunk|--no-verify-chunk] [--verify-all|--no-verify-all] [--verify-all-interval BLOCKS] ``` #### Parameters | Parameter | Default | Description | |-----------|---------|-------------| | `--chunk` | 10,000 | Maximum number of blocks to process per database commit | | `--to-block` | `latest:-64` | Last block in update range. Format: `TAG[:OFFSET]` | #### Block Identifiers Valid block tags: `earliest`, `finalized`, `safe`, `latest`, `pending` Examples: - `latest` - Latest block - `latest:-64` - 64 blocks before chain tip (default, ensures finality) - `safe:128` - 128 blocks after last safe block - `12345678` - Specific block number #### Behavior 1. **Identify active chains**: Queries database for chains with active exchanges 2. **Determine update range**: Starts from `min(last_update_block + 1)` across exchanges 3. **Process in chunks**: Iteratively processes blocks up to `chunk_size` 4. **Track progress**: Displays progress bar showing blocks processed 5. **Skip up-to-date chains**: If no new blocks exist since last update, skips #### Example Usage ```bash degenbot pool update --to-block "latest:-128" degenbot pool update --chunk 5000 ``` #### Failure output A failed run names the endpoint, the chain, and the requested block range. When the failure is the RPC-connection class (a dropped or unreachable transport — the `backend connection task has stopped` family), the report says so directly, states that chunks already committed are kept, points at rerunning from the recorded per-exchange cursors, and then lists the per-exchange resume state (current at the requested target / behind with the last committed block / never updated) read from `exchanges.last_update_block` in the operator database. The updater commits per chunk, so this is the authoritative outstanding-work record; there is no automatic transport retry (retrying a dropped transport has partial-chunk correctness implications and needs its own decision). ### `degenbot exchange activate` Activate an exchange for pool tracking. Creates the database entry if it does not exist; idempotent when already active. ```bash degenbot exchange activate --chain --name ``` `CHAIN` is a chain slug (`base`, `ethereum`) or a numeric chain id; `NAME` is the DEX slug (`aerodrome_v2`, `uniswap_v3`, `uniswap_v4`, …). ADR-051 D5 collapses the retired per-`(chain, DEX)` click verbs onto this one data-driven pair. V4 exchanges additionally create a `pool_managers` row. ### `degenbot exchange deactivate` Deactivate an exchange (its pools are not updated). ```bash degenbot exchange deactivate --chain --name ``` ## Supported Exchanges ### Base Mainnet - Aerodrome V2, V3 - Pancakeswap V2, V3 - Sushiswap V2, V3 - Swapbased V2 - Uniswap V2, V3, V4 ### Ethereum Mainnet - Pancakeswap V2, V3 - Sushiswap V2, V3 - Uniswap V2, V3, V4 ## Fee Structure ### V2 Fees Fixed fee per pool: - **Uniswap V2**: 0.3% (3/1000) - **Sushiswap V2**: 0.3% (3/1000) - **Swapbased V2**: 0.3% (3/1000) - **Pancakeswap V2**: 0.25% (25/10000) - **Aerodrome V2**: Variable (queried from factory via `getFee()`), includes stable pair flag ### V3 Fees Tiered fee structure (basis points, denominator = 1,000,000): - **500 bps** (0.05%) - Stable pairs - **3000 bps** (0.3%) - Standard pairs - **10000 bps** (1.0%) - Exotic pairs ### V4 Fees Dynamic fee support: - Range: 0 to 1,000,000 bps (0% to 100%) - Specified per pool in PoolKey ## Pool Discovery Events ### V2: PairCreated Event **Event Hash**: `0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9` Used by: Uniswap V2, Pancakeswap V2, Sushiswap V2, Swapbased V2 ### V3: PoolCreated Event **Event Hash**: `0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118` Used by: Uniswap V3, Pancakeswap V3, Sushiswap V3, Aerodrome V3. **Aerodrome V3**: Fetches fee from factory after discovery. ### V4: PoolCreated Event **Event Hash**: `0xdd466e674ea557f56295e2d0218a125ea4b4f0f6f3307b95f85e6110838d6438` ## Liquidity Update Processing ### V3 Mint Event **Event Hash**: `0x7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde` **Processing**: Adds `liquidity_gross` and `liquidity_net` across tick range. Updates tick bitmap at word boundaries. ### V3 Burn Event **Event Hash**: `0x0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c` **Processing**: Subtracts `liquidity_gross` and `liquidity_net` across tick range. Deletes position if balance reaches zero. ### V4 ModifyLiquidity Event **Event Hash**: `0xf208f4912782fd25c7f114ca3723a2d5dd6f3bcc3ac8db5af63baa85f711d5ec` **Processing**: Single event with signed `liquidityDelta` (positive for add, negative for remove). Updates managed pool tables. ## Mock Pool Helpers Mock pool classes (`MockV3LiquidityPool`, `MockV4LiquidityPool`) are lightweight pool implementations used to simulate liquidity updates and validate tick calculations without expensive operations (state locking, notifications, caching). **Simplifications**: - No-op `_state_lock`, empty `_notify_subscribers()`, `_invalidate_range_cache_for_ticks()` - `_initial_state_block` set to `MAX_UINT256` to skip in-range modifications - Full (non-sparse) liquidity mapping **Usage**: Load state from database → create mock → process events chronologically → export validated mappings back to database. ## Data Model Updates The Rust `degenbot-db` schema owns the tables below. Python callers receive typed row mirrors from `degenbot.db`; the updater writes through Rust-owned seams: | Table | Fields Updated | Notes | |-------|----------------|-------| | `exchanges` | `last_update_block` | After each chunk completes | | `erc20_tokens` | New rows created | For token0/token1/currency0/currency1 | | `pools` | New rows created | V2/V3 pool metadata from factory events | | `uniswap_v4_pools` | New rows created | V4 pool metadata from manager events | | `pool_managers` | New rows created | V4 manager metadata (on activate) | | `liquidity_positions` | `liquidity_net`, `liquidity_gross` | Upsert from V3 liquidity events | | `initialization_maps` | `bitmap` | Upsert from V3 tick bitmap updates | | `managed_pool_liquidity_positions` | `liquidity_net`, `liquidity_gross` | Upsert from V4 liquidity events | | `managed_pool_initialization_maps` | `bitmap` | Upsert from V4 tick bitmap updates | ## Algorithm Details ### Chunk Processing Blocks are processed in chunks to limit memory usage and enable incremental commits: 1. Calculate `working_end_block` as minimum of: - `last_block` (target block) - `working_start_block + chunk_size - 1` - All `exchange.last_update_block` values ahead of current chunk 2. Update exchanges where `last_update_block is None` or `last_update_block + 1 == working_start_block` 3. Commit changes and advance `working_start_block = working_end_block + 1` 4. Repeat until `working_end_block == last_block` ### Event Ordering Events are processed in block number, then log index order to ensure chronological processing within blocks: ```python sorted(all_events, key=operator.itemgetter("blockNumber", "logIndex")) ``` Invariants enforced by assertions: - New event block ≥ last update block - Same block events must have increasing log index ### SQLite Variable Limits Upsert operations are chunked to stay below SQLite's 32,766 variable limit per batch statement: - **Liquidity positions**: 30,000 rows per chunk (4 keys/row) - **Initialization maps**: 30,000 rows per chunk (3 keys/row) Zero-bitmap words (all ticks uninitialized) are excluded from upserts to reduce database size. ## Configuration The command uses Web3 connections from degenbot config file. Each active chain must have an RPC endpoint configured. ### Required Config ```toml [rpc] 1 = "https://mainnet.example.com" # Ethereum mainnet 8453 = "https://base.example.com" # Base mainnet ``` ## Dependencies - **Database**: Rust `degenbot-db` owner, consumed through the stable `degenbot.db` Python mirror - **Blockchain**: Rust `degenbot-rpc` / `degenbot-pool-updater` for RPC calls - **Math**: Rust `degenbot-math` + `degenbot-pools` (tick bitmap, tick math, liquidity math)