LFJ LBPair storage layout — UNVERIFIED-PENDING¶
STATUS: UNVERIFIED — DO NOT DECODE FROM THIS DOCUMENT.
No decoder, journal extraction, journal
PoolFamilyarm, or capability row for the LFJ (Trader Joe) Liquidity BookLBPairmay 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
LBPairevent 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
LBPairviaeth_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 |
|
Capability family + NONE rows |
present, all-false |
|
Journal extraction arm |
present, declines to decode |
|
DB kind / subclass table |
present |
|
Loud unsupported marker (D8) |
present |
|
|
absent |
no |
|
absent |
|
Verified |
absent |
|
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¶
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.LBPairfactory address per chain — the address whose logs seed pool discovery, and the canonical factory creation event signature +indexedlayout (PairCreated/LBPairCreated-style; exact name and parameters TBD).Which Liquidity Book version? The
LBPairhas 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.A concrete
LBPairaddress 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.
Swap — the swap event consumed by journal extraction. Parameter list, indexed placement, and fee/amount fields TBD.
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.
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.
Any LP/receipt token event (e.g.
Transfer) emitted byLBPair, if the LP position representation matters to admission.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:
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).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.
Per-bin state structure: is it a
mappingor an array (packed bin-array chunks)? What is the key type (e.g.uint24/int24/int32/ abytes32composed 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?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.
Mapping slot-derivation base(s). For every mapping, the exact base slot and the hashing convention:
keccak256(abi.encode(key, base))vskeccak256(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).Read strategy. Whether a public per-bin view getter exists (prefer it), or raw
eth_getStorageAt/ batchedextsloadis required.
D. Bin indexing scheme¶
Bin id type and range, and how the active bin id is stored.
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.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:
derive slot assignment with
forge inspectfrom that source;capture one real
Swap/Mint/Burnlog from a suppliedLBPairaddress and record it as a decoder fixture;cross-check every candidate slot with
cast storage/cast callagainst the live pair and assert the decoded value is non-degenerate (not all-zero, bin id in range, reserves consistent with aSwapevent 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 { .. })returnsNone— pinned byidentity_projection_admits_only_the_structures_extraction_types(rust/crates/engine/degenbot-simulation/src/sim/evm/journal_pools.rs).capabilities(arm, Family::LfjBinned) == FamilyCapabilities::NONEon both arms — pinned bylfj_is_declared_unsupported_on_every_armandlfj_identity_reaches_the_declared_lfj_family(rust/crates/foundation/degenbot-pools/src/capability.rs).A touched
lfj_binnedpools row observesfamily-unsupported:lfj_binned— pinned byload_tolerates_unsupported_pool_kind(rust/crates/engine/degenbot-bot/src/connector_index.rs).The Python manifest declares
lfj_binnedunsupported with no invented deployment — pinned bytest_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:
A real deployed
LBPairaddress on the target chain, plus the target chain id and an RPC URL for it if not chain 1.The LFJ
LBPairfactory address per target chain.The verified
LBPairsource code (vendored intocontract_reference/traderjoe/, as with the Uniswap/Aave references) or an equivalent verified ABI JSON plus the source revision identifier.Confirmation of the target Liquidity Book version/revision (A3).
Optionally, one captured
Swap(andMint/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.