Skip to content

pool-indexer: support multiple V3 factories per network - #4735

Open
AryanGodara wants to merge 5 commits into
aryan/remove-v3-subgraph-sourcefrom
aryan/be-35-pool-indexer-multi-factory
Open

pool-indexer: support multiple V3 factories per network#4735
AryanGodara wants to merge 5 commits into
aryan/remove-v3-subgraph-sourcefrom
aryan/be-35-pool-indexer-multi-factory

Conversation

@AryanGodara

Copy link
Copy Markdown
Member

Description

Lift the one-factory-per-network cap now that the indexer is subgraph-free, so a network can index several Uniswap V3 factories and serve their pools simultaneously.

Stacked on #4734.

Changes

  • validate(): allow >1 factory (was exactly one); reject empty + duplicate addresses (checkpoints/pool rows are keyed by factory).
  • Union envelope block is the MIN across per-factory checkpoints, not MAX, so a caller never gets a result that is ahead of the slowest/lagging factory.
  • Add a factory label to the per-factory metrics (chunk/events/indexed_block/lag/errors).

How to test

  • unit and e2e tests

@AryanGodara AryanGodara self-assigned this Aug 12, 2026
@linear-code

linear-code Bot commented Aug 12, 2026

Copy link
Copy Markdown

BE-35

@AryanGodara
AryanGodara marked this pull request as ready for review August 12, 2026 20:34
@AryanGodara
AryanGodara requested a review from a team as a code owner August 12, 2026 20:34
@claude

claude Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Claude finished @AryanGodara's task in 2m 43s —— View job


PR Review: pool-indexer multi-factory support

Reviewed against origin/aryan/remove-v3-subgraph-source. The change is clean and well-tested — validation, per-factory metric labels, and the union-envelope semantics all hang together. One substantive concern posted inline.

  • Read changed files & diff
  • Analyze config/validation changes
  • Analyze indexer/db/metrics changes
  • Post inline findings
  • Post summary

Findings

1. MIN envelope is not scoped to configured factoriesinline (db/uniswap_v3.rs:617)
The MAXMIN switch is correct in principle, but the query aggregates over all checkpoint rows. Because nothing deletes checkpoint rows, removing a factory from config permanently pins the served envelope to that dead factory's frozen block (under MAX this was harmless). Adding a factory has a milder version during its cold-seed. Suggest scoping the aggregate to the configured factory addresses.

Looks good

  • validate(): empty + duplicate rejection with a clear rationale (checkpoints/pool rows keyed by factory). 👍
  • Per-factory metric labels are consistent in order across all call sites; cardinality stays bounded by factory count.
  • MIN direction is safe for the driver: served pool data is always ≥ the advertised envelope block, never ahead of the slowest factory (matches the PR intent).
  • E2E two_factories and min_envelope tests exercise both the union and the conservative-envelope behavior directly.

Nits (non-blocking)

  • The two new e2e tests seed checkpoints above the anvil head and never call wait_for_indexer — that's fine since the checkpoints are pre-seeded, but a one-line comment in min_envelope on why no wait_for_indexer is needed would help future readers (the existing "Above the head, so the live loops stay put" comment mostly covers it).

Comment thread crates/pool-indexer/src/db/uniswap_v3.rs Outdated

@MartinquaXD MartinquaXD left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Only nits.
Good test.

Comment thread crates/e2e/tests/e2e/pool_indexer.rs
Comment thread crates/e2e/tests/e2e/pool_indexer.rs Outdated
Comment thread crates/e2e/tests/e2e/pool_indexer.rs Outdated
Comment thread crates/e2e/tests/e2e/pool_indexer.rs Outdated
Comment thread crates/pool-indexer/src/api/mod.rs Outdated

@jmg-duarte jmg-duarte left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly this PR changes the semantics of most ops requiring the events from "the latest indexed" to "the latest indexed from the slower pool"

Do you foresee any issues where a pool is too slow and we get stale data because of that?

Comment on lines +133 to +137
/// Builds a URL against the pool-indexer's Uniswap V3 API (all test fixtures
/// index `mainnet`), so call sites don't repeat the route prefix by hand.
fn v3_api(path: &str) -> String {
format!("{POOL_INDEXER_HOST}/api/v1/mainnet/uniswap/v3/{path}")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If &str is always &'static str (which seems to be the case) you can use concat! or similar to avoid the String allocation

Since this is test code is not super important, but something to keep in mind for the future


/// Two factories on one network are served as a single union: each factory's
/// pools show up in `/pools`, attributed to that factory.
async fn two_factories(web3: Web3) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There doesn't seem to be a test ensuring the events for each pool are registered correctly, just the initialization

Should at least an event be performed for one of them and asserted? No need for a separate test

Comment on lines 126 to 129
anyhow::ensure!(
self.factories.len() == 1,
"network {}: exactly one factory per network is supported in this release, got {}",
!self.factories.is_empty(),
"network {}: at least one factory is required",
self.name,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can extract Tiago's custom non-empty deserializer instead of validating post-hoc

#4727 (comment)

Additionally you could extend it to remove the duplicates

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants