degenbot.uniswap.v4_liquidity_pool ================================== .. py:module:: degenbot.uniswap.v4_liquidity_pool .. autoapi-nested-parse:: UniswapV4Pool: concentrated liquidity AMM companion over a Pool handle. ADR-005 slice 9b — the V4 companion rewritten over the same `Pool` handle topology as the V3 companion. Rust `BotState` is the single source of truth for V4 mutable state (scalars, tick data, reorg journal); this companion reads it through `self._py_pool` (atomic `snapshot_v3()` for scalars — already V3/V4-generic via `get_v3_or_v4_pool` — + `tick_data_snapshot()`/ `tick_bitmap_snapshot()` for the tick maps) and delegates `external_update` (Swap) / `update_liquidity_map` (ModifyLiquidity) / `update_tick_data` (sparse-map backfill) / discard / restore to the handle. `_state_mgr` / `_state_cache` / `state_cache_depth` are dropped — the `StateCache` temporal-navigation layer lives in Rust now (journal + discard/restore). V3 already has none; V4 follows. V4-specific identity (pool_id, pool_manager_address, pool_key, hook_address, protocol_fee, lp_fee, state_view_address) stays Python-side — matches V3 keeping tokens/factory/fee Python-side. The hook admission floor (reject amount-modifying hooks + dynamic fees) lives in Rust (`BotState::register_v4_pool`), surfaced at `Bot.register_v4_pool` (ADR-005 slice 9a) so the companion never holds a hooked pool. Checked words (a tick-data fetcher probed `tickBitmap(word)` and the on-chain bitmap was zero) are tracked in Rust `known_bitmap_words` (Sparse only — see the V3 companion's sparse-map note); `tick_bitmap_snapshot()` surfaces a checked-but-empty word as `(0, block)` so the fetch loop breaks with no client-side bitmap shadow. Module Contents --------------- .. py:class:: SwapResult SwapResult class. .. py:attribute:: sqrt_price_x96 :type: int .. py:attribute:: tick :type: int .. py:attribute:: liquidity :type: int .. py:class:: SwapDelta SwapDelta class. .. py:attribute:: currency0 :type: int .. py:attribute:: currency1 :type: int .. py:property:: amount_in :type: int The deposited token amount. .. py:property:: amount_out :type: int The withdrawn token amount. .. py:class:: ProtocolFee ProtocolFee class. .. py:attribute:: zero_for_one :type: int .. py:attribute:: one_for_zero :type: int .. py:class:: Slot0 Slot0 class. .. py:attribute:: sqrt_price_x96 :type: int .. py:attribute:: tick :type: int .. py:attribute:: protocol_fee :type: ProtocolFee .. py:attribute:: lp_fee :type: int .. py:data:: NATIVE_CURRENCY_ADDRESS .. py:class:: Hooks(*args, **kwds) Bases: :py:obj:`enum.Enum` Hooks class. .. py:attribute:: BEFORE_INITIALIZE :value: 8192 .. py:attribute:: AFTER_INITIALIZE :value: 4096 .. py:attribute:: BEFORE_ADD_LIQUIDITY :value: 2048 .. py:attribute:: AFTER_ADD_LIQUIDITY :value: 1024 .. py:attribute:: BEFORE_REMOVE_LIQUIDITY :value: 512 .. py:attribute:: AFTER_REMOVE_LIQUIDITY :value: 256 .. py:attribute:: BEFORE_SWAP :value: 128 .. py:attribute:: AFTER_SWAP :value: 64 .. py:attribute:: BEFORE_DONATE :value: 32 .. py:attribute:: AFTER_DONATE :value: 16 .. py:attribute:: BEFORE_SWAP_RETURNS_DELTA :value: 8 .. py:attribute:: AFTER_SWAP_RETURNS_DELTA :value: 4 .. py:attribute:: AFTER_ADD_LIQUIDITY_RETURNS_DELTA :value: 2 .. py:attribute:: AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA :value: 1 .. py:class:: UniswapV4Pool(*args: Any, **kwargs: Any) Bases: :py:obj:`degenbot.uniswap.v4_pool_state.V4PoolState`, :py:obj:`degenbot.uniswap.v4_pool_calc.UniswapV4PoolCalc`, :py:obj:`degenbot.uniswap.cl_companion.ConcentratedLiquidityCompanion` A Uniswap V4 concentrated-liquidity pool companion over a ``Pool`` handle. Rust owns the mutable state (scalars + tick data + reorg journal) as ``V4PoolState``; this companion reads it through ``self._py_pool`` (one atomic ``snapshot_v3()`` for scalars — already V3/V4-generic via ``get_v3_or_v4_pool`` — + ``tick_data_snapshot()`` / ``tick_bitmap_snapshot()`` for the tick maps) and delegates ``external_update`` (Swap) / ``update_liquidity_map`` (ModifyLiquidity) / ``update_tick_data`` (sparse-map backfill) / discard / restore to the handle. V4-specific identity (pool_id, pool_manager, pool_key, hooks, protocol_fee, lp_fee, state_view_address) stays Python-side — matches V3. Construct via the V4 builder (which registers in Rust and hands the handle here); tests use ``make_v4_pool``. Hook admission floor: pools with amount-modifying hooks (`hook_flags & 0xCC != 0`) or dynamic fees (`fee == 0x100000`) are rejected in Rust (`BotState::register_v4_pool`), surfaced at `Bot.register_v4_pool` as typed exceptions — so this companion never holds a hooked/dynamic-fee pool. .. py:type:: PoolState :canonical: UniswapV4PoolState .. py:attribute:: hook_address :type: degenbot.types.chain.ChecksummedAddress .. py:attribute:: active_hooks :type: frozenset[Hooks] .. py:attribute:: name :type: str .. py:attribute:: protocol_fee :type: ProtocolFee .. py:attribute:: lp_fee :type: int .. py:method:: from_handle(py_pool: degenbot.types.Pool) -> Self :classmethod: Wrap a Rust-owned ``Pool`` handle as a Python companion. Internal seam (ADR-005, Polars-style ``_from_pydf`` pattern). The handle is self-describing: every identity field (pool_manager, pool_id, pool_key, hooks, tokens, fee, tick_spacing) is read off it — no identity is passed as constructor args. Rust owns the mutable state (slot0 + tick_data + reorg journal) as ``V4PoolState`` and the immutable registration metadata as ``V4PoolIdentity``; this companion reads both through ``self._py_pool``. Protocol fee / LP fee / state_view_address are builder-supplied values the seam defaults; the builder overrides them after ``from_handle`` (matches V3's deployer/init_hash override). :returns: A ``cls`` instance wrapping ``py_pool``. :raises DegenbotValueError: If the handle is not a V4-family pool. .. py:method:: calculate_tokens_in_from_tokens_out(token_out: degenbot.erc20.Erc20Token, token_out_quantity: int, override_state: degenbot.uniswap.v4_types.UniswapV4PoolState | None = None) -> int Calculate tokens in from tokens out. :returns: The required input token amount. :raises DegenbotValueError: If token_out is not held by this pool. :raises HookedPoolResult: If the pool has active hooks that affect the swap. :raises IncompleteSwap: If the swap cannot fulfill the full output amount. :raises LiquidityPoolError: If the simulated execution reverts. .. py:method:: calculate_tokens_out_from_tokens_in(token_in: degenbot.erc20.Erc20Token, token_in_quantity: int, override_state: degenbot.uniswap.v4_types.UniswapV4PoolState | None = None) -> int Calculate tokens out from tokens in. :returns: The expected output token amount. :raises DegenbotValueError: If token_in is not held by this pool. :raises HookedPoolResult: If the pool has active hooks that affect the swap. :raises IncompleteSwap: If the swap cannot fulfill the full input amount. :raises LiquidityPoolError: If the simulated execution reverts. .. py:property:: address :type: degenbot.types.chain.ChecksummedAddress Address. :returns: The pool manager address. .. py:property:: pool_id :type: bytes Pool id. :returns: The pool ID bytes. .. py:property:: pool_key :type: degenbot.uniswap.v4_types.UniswapV4PoolKey Pool key. :returns: The V4 pool key struct. .. py:property:: sqrt_price_x96 :type: int Sqrt price x96. :returns: The current sqrt price as a Q64.96 value (from Rust). .. py:property:: state :type: degenbot.uniswap.v4_types.UniswapV4PoolState State. :returns: The current pool state, built from one atomic Rust scalar snapshot (``_py_pool.snapshot_v3()`` — V3/V4-generic — ) + the tick-map snapshots. :raises DegenbotValueError: If the pool is not registered in Rust. .. py:property:: tick_spacing :type: int Tick spacing. :returns: The tick spacing for the pool (Python-side identity). .. py:property:: fee :type: int Fee. :returns: The fee in pips (Python-side identity).