feat(ensindexer): per-chain end blocks via END_BLOCK_<chainId> - #2176
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 4d83b62 The changes in this PR will be included in the next version bump. This PR includes changesets to release 22 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Warning Review limit reached
More reviews will be available in 41 minutes and 14 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (24)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Adds support to ENSIndexer for per-chain indexing end blocks via END_BLOCK_<chainId> environment variables, enabling deterministic multi-chain checkpointing by constraining each chain independently (layered on top of the existing global START_BLOCK/END_BLOCK behavior).
Changes:
- Introduces
config.chainEndBlocks: Map<ChainId, number>parsed fromEND_BLOCK_<chainId>env vars, validated via a newinvariant_chainEndBlocks. - Adds
blockrangeForChain()and threadschainEndBlocksthroughchainConfigForContract()/mergedChainConfigForContracts()so plugin contract configs respect per-chain end blocks. - Updates plugins and tests to use the per-chain effective blockrange where needed (including Resolver configs built via
constrainBlockrange(...)).
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| apps/ensindexer/src/plugins/unigraph/plugin.ts | Passes chainEndBlocks into chain config helpers and applies per-chain blockrange when constraining Resolver ranges. |
| apps/ensindexer/src/plugins/tokenscope/plugin.ts | Passes chainEndBlocks into chainConfigForContract for all configured contracts/chains. |
| apps/ensindexer/src/plugins/subgraph/plugins/threedns/plugin.ts | Threads chainEndBlocks into chain config helper calls for 3DNS contracts on multiple chains. |
| apps/ensindexer/src/plugins/subgraph/plugins/subgraph/plugin.ts | Updates subgraph plugin contract configs to respect per-chain end blocks. |
| apps/ensindexer/src/plugins/subgraph/plugins/lineanames/plugin.ts | Updates lineanames subgraph plugin configs to include per-chain end blocks. |
| apps/ensindexer/src/plugins/subgraph/plugins/basenames/plugin.ts | Updates basenames subgraph plugin configs to include per-chain end blocks. |
| apps/ensindexer/src/plugins/registrars/plugin.ts | Threads chainEndBlocks into registrar-related contract config helpers, including merged registrar controller config. |
| apps/ensindexer/src/plugins/protocol-acceleration/plugin.ts | Passes chainEndBlocks into chain config helpers and applies per-chain blockrange to Resolver constraints. |
| apps/ensindexer/src/lib/ponder-helpers.ts | Adds blockrangeForChain() and layers per-chain end blocks into chainConfigForContract and mergedChainConfigForContracts. |
| apps/ensindexer/src/lib/ponder-helpers.test.ts | Adds focused unit tests for blockrangeForChain(). |
| apps/ensindexer/src/config/validations.ts | Adds invariant_chainEndBlocks to ensure each END_BLOCK_<chainId> corresponds to an indexed chain. |
| apps/ensindexer/src/config/types.ts | Extends EnsIndexerConfig with chainEndBlocks and documents its semantics/invariants. |
| apps/ensindexer/src/config/serialized-types.ts | Excludes chainEndBlocks from the serialized config type (since it’s a Map). |
| apps/ensindexer/src/config/environment.ts | Extends the environment type to allow END_BLOCK_<number> keys. |
| apps/ensindexer/src/config/config.test.ts | Adds config parsing/validation tests for END_BLOCK_<chainId> (multi-chain allowed, non-indexed chain rejected, negative/non-numeric rejected). |
| apps/ensindexer/src/config/config.schema.ts | Implements buildChainEndBlocksFromEnv, adds schema support for chainEndBlocks, and wires in the new invariant. |
| .changeset/ensindexer-per-chain-end-blocks.md | Changeset describing the new per-chain end block feature and semantics. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Greptile SummaryThis PR replaces the single global
Confidence Score: 5/5Safe to merge — the change is a well-scoped replacement of a global config field with a per-chain map, validated at parse time and applied consistently across all plugin call sites. Every contract call site is mechanically updated from globalBlockrange to chainEndBlocks. The new buildChainEndBlocksFromEnv function guards against lax numeric coercion, the invariant catches misconfigured chain IDs before indexing starts, and the Ponder build-ID serialisation correctly converts the Map to a sorted plain object. Both the Ponder config path and the SDK indexed-blockranges path produce consistent results, confirmed by new tests including the previously-missing case where a contract's explicit endBlock exceeds the chain end block. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["ENV: END_BLOCK_<chainId>=N"] --> B["buildChainEndBlocksFromEnv(env)\n/^\\d+$/ validation"]
B --> C["Map<ChainId, number>\nchainEndBlocks"]
C --> D["ENSIndexerConfigSchema.parse()\ninvariant_chainEndBlocks"]
D -->|"chainId not indexed"| E["Error: no active plugin indexes chain N"]
D -->|"all keys valid"| F["EnsIndexerConfig.chainEndBlocks"]
F --> G["blockrangeForChain(chainEndBlocks, chainId)\nbuildBlockNumberRange(undefined, endBlock)"]
G --> H["chainConfigForContract / mergedChainConfigForContracts"]
H --> I["Ponder contract chain config\nstartBlock / endBlock"]
F --> J["buildIndexedBlockranges(namespace, chainEndBlocks, plugins)\neffectiveEndBlock = Math.min(contract.endBlock, chainEndBlock)"]
J --> K["Map<ChainId, BlockNumberRange>\nindexed blockranges for status"]
F --> L["indexingBehaviorDependencies\nObject.fromEntries sorted to Ponder build ID"]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["ENV: END_BLOCK_<chainId>=N"] --> B["buildChainEndBlocksFromEnv(env)\n/^\\d+$/ validation"]
B --> C["Map<ChainId, number>\nchainEndBlocks"]
C --> D["ENSIndexerConfigSchema.parse()\ninvariant_chainEndBlocks"]
D -->|"chainId not indexed"| E["Error: no active plugin indexes chain N"]
D -->|"all keys valid"| F["EnsIndexerConfig.chainEndBlocks"]
F --> G["blockrangeForChain(chainEndBlocks, chainId)\nbuildBlockNumberRange(undefined, endBlock)"]
G --> H["chainConfigForContract / mergedChainConfigForContracts"]
H --> I["Ponder contract chain config\nstartBlock / endBlock"]
F --> J["buildIndexedBlockranges(namespace, chainEndBlocks, plugins)\neffectiveEndBlock = Math.min(contract.endBlock, chainEndBlock)"]
J --> K["Map<ChainId, BlockNumberRange>\nindexed blockranges for status"]
F --> L["indexingBehaviorDependencies\nObject.fromEntries sorted to Ponder build ID"]
Reviews (7): Last reviewed commit: "fix(ensnode-sdk): cap contract end block..." | Re-trigger Greptile |
Replace the global START_BLOCK/END_BLOCK indexing-range config with per-chain end blocks via END_BLOCK_<chainId> env vars (mirroring RPC_URL_<chainId>), parsed into config.chainEndBlocks. Each constrains its chain's indexing end block independently and may be set across any number of indexed chains. blockrangeForChain() resolves the per-chain range; buildIndexedBlockranges (@ensnode/ensnode-sdk) now takes a per-chain end-block map so indexing-status reaches omnichain-completed for bounded multichain runs. Enables deterministic, reproducible multichain checkpoints. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6966b41 to
7aa4410
Compare
Reject lax Number() coercions in buildChainEndBlocksFromEnv ("", "1e2",
"0x10") via a base-10 integer regex. Add chainEndBlocks to the serialized
config so per-chain end blocks are visible in serialized/logged output,
mirroring rpcConfigs.
Addresses Copilot and Greptile review feedback on #2176.
|
@greptile review |
…chainId types Address Copilot review on PR #2176: - buildIndexedBlockranges now caps a contract's explicit endBlock at END_BLOCK_<chainId> (min, not fallback), mirroring constrainBlockrange on the Ponder path; add regression test - chainConfigForContract/mergedChainConfigForContracts take ChainId instead of number - add @ensnode/ensnode-sdk changeset for the buildIndexedBlockranges signature change
|
@greptile review |
Replace the global
START_BLOCK/END_BLOCKindexing-range configuration with per-chain end blocks viaEND_BLOCK_<chainId>env vars (e.g.END_BLOCK_1,END_BLOCK_8453), mirroring theRPC_URL_<chainId>convention. They are parsed intoconfig.chainEndBlocks: Map<ChainId, number>; each value constrains its own chain's indexing end block independently and MAY be set across any number of indexed chains.The legacy global blockrange was restricted to single-chain indexing — per-chain end blocks remove that restriction. The new
invariant_chainEndBlocksonly checks that eachEND_BLOCK_<chainId>targets a chain an active plugin actually indexes. Per-chain ranges are resolved viablockrangeForChain()inponder-helpers, andbuildIndexedBlockranges(@ensnode/ensnode-sdk) now takes the per-chain end-block map instead of a single global end block, so indexing-status reachesomnichain-completedfor bounded multichain runs.This enables deterministic, reproducible multichain checkpoints where every indexed chain stops at a block corresponding to a shared timestamp.