LFJ LBPair storage layout — UNVERIFIED-PENDING

STATUS: UNVERIFIED — DO NOT DECODE FROM THIS DOCUMENT.

No decoder, journal extraction, journal PoolFamily arm, or capability row for the LFJ (Trader Joe) Liquidity Book LBPair may land until every open question below is answered against the real contract. This file records the verification gap and the exact inputs the operator must supply; it deliberately asserts no event signature, no storage slot, and no bin formula, because a plausible-but-wrong slot layout silently produces false pool state — the worst failure mode this system can have (ADR-014 / ADR-059 D2’s no-guessing rule).

Why this document exists

ADR-059 E3 makes LFJ binned liquidity the first genuinely new structure through the pool-family kernel (Structure::BinnedLiquidity, PoolFamily::LfjBinned). Its contract is the Liquidity Book LBPair: a pair of tokens plus a fixed bin_step price granularity over indexed bins rather than Uniswap ticks. The current chunk (ergo QZ7ONQ) owns the decoders + journal extraction. Both need contract-level truth:

  • decoders need the exact LBPair event signatures and indexed-parameter layout to build topic0 constants and data slicing;

  • journal extraction needs the exact storage layout to read per-bin state from a touched LBPair via eth_getStorageAt / extsload.

Neither can be derived from this repository. The verification boundary for this chunk was explicit: derive the layout authoritatively or stop and document. The repo has no LFJ artifact to derive from, so the chunk stops here and documents.

Evidence: what this repository actually contains

Verified by searching the working tree at HEAD 2ccb1f8ee:

Surface

State

Location

Binned-liquidity taxonomy arm

present

rust/crates/foundation/degenbot-pools/src/pool.rs (Structure::BinnedLiquidity, BinnedLiquidityVariant::Lfj)

Capability family + NONE rows

present, all-false

rust/crates/foundation/degenbot-pools/src/capability.rs (Family::LfjBinned)

Journal extraction arm

present, declines to decode

rust/crates/engine/degenbot-simulation/src/sim/evm/journal_pools.rs

DB kind / subclass table

present

rust/crates/foundation/degenbot-db/src/{schema.rs,schema_head.sql} (lfj_pools, kind lfj_binned); Python consumers use the typed degenbot.db mirror

Loud unsupported marker (D8)

present

rust/crates/engine/degenbot-bot/src/connector_index.rs (unsupported_kind)

LBPair event ABI / topic constants

absent

no lfj_* module in rust/crates/foundation/degenbot-decoders/src/

LBPair deployed address

absent

src/degenbot/registry/deployments.json, rust/crates/foundation/degenbot-db/src/species.toml carry no LFJ/Trader Joe row; the species shape test uses a placeholder factory 0x…dead

Verified LBPair source

absent

contract_reference/ holds only uniswap/{V2,V3,V4} and aave/; no traderjoe/

Factory / creation-event data

absent

no LFJ factory address, no creation-event topic

A sweep for trader, lbpair, and liquidity book (case-insensitive, all file types, excluding build/caches) returns only the taxonomy/manifest surfaces table above plus ADR-059 and the ergo plan body — i.e. the repo knows the LFJ family exists but holds zero contract-interface data.

The environment’s configured RPC (DEGENBOT_RPC_HTTP_CHAINID_1) reaches Ethereum mainnet (chain id 1, cast 1.7.1 available), but a live cast cross-check is only possible once a real deployed LBPair address is known. Locating such an address from outside this repository was explicitly out of scope for this chunk, and guessing one would defeat the purpose of the check.

Open questions (the operator asks)

Each item below is a question with the shape of the answer required. Nothing is asserted; candidates named in parentheses are placeholders to be confirmed or discarded, not values to implement against.

A. Deployment identity

  1. Which chain(s) ship first? The environment is configured for chain 1 only (DEGENBOT_RPC_HTTP_CHAINID_1); the operator must name the target chain(s) and supply an RPC for each.

  2. LBPair factory address per chain — the address whose logs seed pool discovery, and the canonical factory creation event signature + indexed layout (PairCreated/LBPairCreated-style; exact name and parameters TBD).

  3. Which Liquidity Book version? The LBPair has shipped multiple deployed revisions with differing storage/event surfaces. The operator must pin the exact version/revision to target; this is a correctness input, not a nicety.

  4. A concrete LBPair address per chain for fixture capture and layout cross-checking.

B. Event shapes (required before any decoder lands)

For each event, the exact canonical Solidity signature (name + parameter order + types) and which parameters are indexed are required; once the canonical text is verified, cast sig-event '<signature>' computes topic0 and eth_getLogs against a real pair confirms it.

  1. Swap — the swap event consumed by journal extraction. Parameter list, indexed placement, and fee/amount fields TBD.

  2. Mint / Burn (per-bin liquidity) — the add/remove-liquidity events. Whether one bin per event, a bin array per event, and the exact signature TBD.

  3. Sync / fee-accrual / bins-updated equivalent — the event (if any) that reports accrued fees or a changed bin set, needed to keep extracted bins current between swaps.

  4. Any LP/receipt token event (e.g. Transfer) emitted by LBPair, if the LP position representation matters to admission.

  5. Factory creation event (see A2).

C. Storage layout (required before journal extraction lands)

The journal extractor reads raw storage from a touched address. The following must be resolved from the verified source (e.g. forge inspect), exactly as v4_poolmanager_storage_layout.md does for the Uniswap V4 PoolManager:

  1. Top-level slot assignment. The LBPair’s C3-linearized base contracts and the resulting slot index of every storage-bearing variable (immutables vs storage must be distinguished — immutables are in bytecode, not storage).

  2. Which top-level slot holds each piece of pair-wide state:

    • active bin id;

    • token0 / token1 / factory / bin_step (where not immutable);

    • the root of the per-bin state structure.

  3. Per-bin state structure: is it a mapping or an array (packed bin-array chunks)? What is the key type (e.g. uint24 / int24 / int32 / a bytes32 composed key)? What is the value struct and its field order, packing, and width — specifically where liquidity and reserves X/Y live, and where fee accrual (cumulative / per-bin owed fees) lives?

  4. Bin-position / tree index: the structure that resolves the next non-empty bin (a position array plus a search tree), its shape, base slot, and key derivation.

  5. Mapping slot-derivation base(s). For every mapping, the exact base slot and the hashing convention: keccak256(abi.encode(key, base)) vs keccak256(key ‖ base), and the key sign-extension rule for signed keys (int24/int32 keys must be sign-extended to 256 bits before hashing, or the computed slot diverges for negative bins).

  6. Read strategy. Whether a public per-bin view getter exists (prefer it), or raw eth_getStorageAt / batched extsload is required.

D. Bin indexing scheme

  1. Bin id type and range, and how the active bin id is stored.

  2. Price formula — the exact integer expression (base/precision order with the decimals adjustment and bin_step). The formula must come from the verified source, not from memory or this doc.

  3. token0/token1 ordering and decimal scaling as they affect decoded bin price.

How to close each question (verification procedure)

Preferred, and the pattern the repo already uses for Uniswap V4 — vendor the verified deployed source under contract_reference/traderjoe/ (mirroring contract_reference/uniswap/V4/PoolManager.sol), then:

  1. derive slot assignment with forge inspect from that source;

  2. capture one real Swap / Mint / Burn log from a supplied LBPair address and record it as a decoder fixture;

  3. cross-check every candidate slot with cast storage / cast call against the live pair and assert the decoded value is non-degenerate (not all-zero, bin id in range, reserves consistent with a Swap event from the same block).

Alternative when only an ABI is available: operator supplies the verified ABI + a real address; topic0s come from cast sig-event '<canonical text>' and are then confirmed against eth_getLogs output for that pair.

A layout that cannot be cross-checked against a real pair is not verified and must not land.

Consequence until closed (the shipped honest state)

The following are the current, test-pinned truths and must not change until the questions above are answered:

  • rust/crates/foundation/degenbot-decoders/src/ exports no LFJ module or topic constant.

  • PoolFamilyTag::from_identity(Identity::BinnedLiquidity { .. }) returns None — pinned by identity_projection_admits_only_the_structures_extraction_types (rust/crates/engine/degenbot-simulation/src/sim/evm/journal_pools.rs).

  • capabilities(arm, Family::LfjBinned) == FamilyCapabilities::NONE on both arms — pinned by lfj_is_declared_unsupported_on_every_arm and lfj_identity_reaches_the_declared_lfj_family (rust/crates/foundation/degenbot-pools/src/capability.rs).

  • A touched lfj_binned pools row observes family-unsupported:lfj_binned — pinned by load_tolerates_unsupported_pool_kind (rust/crates/engine/degenbot-bot/src/connector_index.rs).

  • The Python manifest declares lfj_binned unsupported with no invented deployment — pinned by test_lfj_kind_is_declared_unsupported_until_a_tier_admits_it (tests/database/test_species_manifest.py).

Gap analysis — exact operator inputs that unblock this chunk

To lift the block, supply all of the following:

  1. A real deployed LBPair address on the target chain, plus the target chain id and an RPC URL for it if not chain 1.

  2. The LFJ LBPair factory address per target chain.

  3. The verified LBPair source code (vendored into contract_reference/traderjoe/, as with the Uniswap/Aave references) or an equivalent verified ABI JSON plus the source revision identifier.

  4. Confirmation of the target Liquidity Book version/revision (A3).

  5. Optionally, one captured Swap (and Mint/Burn) log from (1) to seed decoder fixtures.

With (1)–(4), this chunk can land: lfj_swap_decoder.rs / lfj_mint_burn_decoder.rs / pair-created decoder with verified topic0 constants, the storage-layout doc promoted from UNVERIFIED-PENDING to the v4_poolmanager_storage_layout.md format, and a journal extraction arm that decodes bins from a known-pool set. Until then, the capability rows stay NONE and the D8 marker stays loud.