Skip to content

Conversation

@DZakh
Copy link
Member

@DZakh DZakh commented Oct 20, 2025

Includes:

Plus:

Summary by CodeRabbit

  • New Features

    • Checkpoint-based reorg tracking, safe-checkpoint optimizations, and multichain checkpoint-aware batching.
  • Improvements

    • Stronger rollback orchestration, multi-chain batch processing, expanded Prometheus metrics (rollbacks, batches, cache invalidations) and improved cache loading/parsing.
    • Hasura-aware storage initialization and CI readiness checks; BIGINT support and schema/table enhancements.
  • Bug Fixes

    • Enforced 63-character entity name limit and hardened UTF‑8/cache error handling.
  • Documentation

    • Added a navigation guide describing project structure and runtime layout.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 20, 2025

Walkthrough

Large, cross-cutting refactor introducing checkpoint-based multichain batching and reorg tracking, replacing legacy end-of-block scan/state with Checkpoints and ReorgDetection, updating persistence/PG schema and Hasura support, adjusting in-memory store and generated templates, adding metrics, and sweeping test/template updates.

Changes

Cohort / File(s) Summary
Docs & CI
\.cursor/rules/navigation.mdc`, `.github/workflows/build_and_test.yml``
Added navigation documentation and a Hasura service plus readiness waits to CI.
Batching & Event Flow
\codegenerator/cli/npm/envio/src/Batch.res`, `codegenerator/cli/templates/static/codegen/src/EventProcessing.res`, `codegenerator/cli/templates/static/codegen/src/IO.res``
Introduced multichain Batch.t with per-chain checkpoints, added prepareUnordered/OrderedBatch paths, threaded checkpointId through handler/preload/executeBatch, and refactored batch iteration to checkpoint-segmented processing.
Fetch State / ChainFetcher / Manager
\codegenerator/cli/npm/envio/src/FetchState.res`, `codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res`, `codegenerator/cli/templates/static/codegen/src/eventFetching/ChainManager.res`, `codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res``
Moved indexingContract into Internal.*, removed dcsToStore exposure, replaced lastBlockScannedHashes with ReorgDetection + SafeCheckpointTracking, added commitedCheckpointId and changed APIs to accept reorgCheckpoints/maxReorgDepth and Batch.t.
Reorg Detection & Safe Checkpoints
\codegenerator/cli/npm/envio/src/ReorgDetection.res`, `codegenerator/cli/npm/envio/src/SafeCheckpointTracking.res``
New ReorgDetection.t (shouldRollbackOnReorg, maxReorgDepth) with register/rollback helpers and SafeCheckpointTracking to compute/preserve safe checkpoint ids and merge/rollback checkpoint sequences.
Persistence, PgStorage & Checkpoints DB
\codegenerator/cli/npm/envio/src/PgStorage.res`, `codegenerator/cli/npm/envio/src/Persistence.res`, `codegenerator/cli/templates/static/codegen/src/Config.res``
Added ~isHasuraEnabled wiring and numeric-array-as-text handling in table SQL, replaced EndOfBlockRangeScannedData with Checkpoints table/module, resumeInitialState now returns reorgCheckpoints and checkpointId, and introduced initialChainState shape.
DB Schema / Entity History / Rollback
\codegenerator/cli/npm/envio/src/db/EntityHistory.res`, `codegenerator/cli/npm/envio/src/db/InternalTable.res`, `codegenerator/cli/templates/static/codegen/src/db/DbFunctions.res`, `codegenerator/cli/templates/static/codegen/src/db/DbFunctionsImplementation.js``
Reworked history to checkpoint-aware entityUpdate/entityUpdateAction, added per-entity entityIndex and history table naming, new checkpoint-based history/rollback queries, removed many legacy DbFunctions exports, and added Checkpoints DB helpers.
In-Memory Store & Load Layer
\codegenerator/cli/templates/static/codegen/src/InMemoryStore.res`, `codegenerator/cli/templates/static/codegen/src/InMemoryTable.res`, `codegenerator/cli/templates/static/codegen/src/LoadLayer.res`, `codegenerator/cli/templates/static/codegen/src/IO.res``
Replaced eventIdentifier with rollbackTargetCheckpointId, added invalidationsCount to effect cache in-mem table, converted per-event DC updates to batch-driven setBatchDcs, adjusted cache schema to outputSchema and added parsing+invalidations handling.
Internal Types & Utilities
\codegenerator/cli/npm/envio/src/Internal.res`, `codegenerator/cli/npm/envio/src/InternalConfig.res`, `codegenerator/cli/npm/envio/src/Utils.res`, `codegenerator/cli/npm/envio/src/EventRegister.res`, `codegenerator/cli/npm/envio/src/Envio.res``
Added Internal.indexingContract, reorgCheckpoint and new in-memory entity variants, changed genericEntityConfig.index visibility, replaced Dict helpers with raw for..in variants (mapValues, forEachWithKey, size, isEmpty), added UnsafeIntOperators, and added registrations.hasEvents flag; changed effect cache meta to outputSchema.
Prometheus / Metrics
\codegenerator/cli/npm/envio/src/Prometheus.res``
Added envio_rollback_events_count, ProgressBatchCount, EffectCacheInvalidationsCount, updated RollbackSuccess.increment signature, and removed ProcessingBlockNumber/ProcessingBatchSize metrics.
Templates & Generated Code
\codegenerator/cli/templates/dynamic/codegen/src/RegisterHandlers.res.hbs`, `codegenerator/cli/templates/dynamic/codegen/src/TestHelpers_MockDb.res.hbs`, `codegenerator/cli/templates/dynamic/codegen/src/Types.res.hbs`, `codegenerator/cli/templates/dynamic/codegen/src/db/Entities.res.hbs`, `codegenerator/cli/templates/dynamic/contract_import_templates//EventHandlers..hbs``
Exposed makeGeneratedConfig, removed several generated in-memory-store types, added per-entity index and pass-through entityIndex into history calls, refactored mock test helpers to batch/multichain flows, and changed event param mapping to use event_key.original.
Codegen/Rust & Table types
\codegenerator/cli/src/config_parsing/entity_parsing.rs`, `codegenerator/cli/npm/envio/src/db/Table.res`, `codegenerator/cli/npm/envio/src/Prometheus.res``
Enforced entity name max-length 63, added BigInt fieldType variant mapping to BIGINT, and updated Prometheus metric signatures.
Tests & Scenarios
\scenarios/**` (many files)`
Wide test updates: removed/rewrote multiple test suites, updated mocks and helpers to use checkpointId, maxReorgDepth, registrationBlock, Internal.* shapes, added effect-cache metric tests, and adjusted many assertions to new checkpoint/checkpointId-based behavior.
Network enum changes (Rust)
\codegenerator/cli/src/config_parsing/chain_helpers.rs``
Added/adjusted network enum variants and attributes used by reorg/threshold logic (ChainwebTestnet22-24, Damon, Plasma, SentientTestnet; attribute adjustments for some networks).

Sequence Diagram(s)

%% High-level checkpoint-aware batch processing and reorg flow
sequenceDiagram
    participant CM as ChainManager
    participant B as Batch
    participant EP as EventProcessing
    participant GS as GlobalState
    participant CF as ChainFetcher
    participant RD as ReorgDetection
    participant SCT as SafeCheckpointTracking
    participant DB as Persistence/PG

    CM->>B: Batch.make(~checkpointIdBeforeBatch, ~chainsBeforeBatch)
    B-->>CM: Batch.t { items, checkpointIds, progressedChainsById, totalBatchSize }

    CM->>EP: processEventBatch(~batch)
    EP->>EP: preloadBatchOrThrow(~batch, checkpointId)
    EP->>EP: runBatchHandlersOrThrow(~batch, checkpointId)
    EP->>GS: update in-memory store with checkpointId
    EP->>DB: executeBatch(~batch, inMemoryStore)

    CF->>RD: registerReorgGuard(reorgGuard, currentBlockHeight)
    alt reorg detected
        RD-->>CF: ReorgDetected
        CF->>GS: submit ReorgDetected
        GS->>GS: FindReorgDepth -> FoundReorgDepth -> RollbackReady
        GS->>DB: query rollbackTargetCheckpointId
        GS->>EP: prepareRollbackDiff(rollbackTargetCheckpointId)
    else no reorg
        RD-->>CF: NoReorg
    end

    note right of SCT: update safe checkpoints on committed batches
    EP->>SCT: updateOnNewBatch(sourceBlockNumber, chainId, batchCheckpointIds)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Large, heterogeneous architectural changes across batching, persistence, rollback, templates, metrics, and tests; careful per-module review and cross-checking of DB migrations, public signatures, and template generation required.

Possibly related PRs

Suggested reviewers

  • JonoPrest

"🐰
I hop through checkpoints, nibble logs of time,
Batches bloom across chains, in tidy rhyme.
I sniff reorgs, stash safe checkpoints in a heap,
Metrics hum and Hasura wakes from sleep.
CodeRabbit twitches whiskers — ready, leap!"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 2 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The PR title "v2.31" is extremely generic and serves only as a version label rather than a meaningful description of the changeset. While the title is technically related to the pull request (it represents a version bump), it fails to convey the substance of the changes to a teammate scanning commit history. The PR contains substantial architectural changes including checkpoint-based batch processing refactoring, reorg detection improvements, entity history restructuring, Prometheus metrics updates, bug fixes for event parameter casing and BigInt array precision, and new network chain support. None of these important aspects are captured in the title. Consider updating the PR title to be more descriptive of the primary changes, such as "Implement checkpoint-based batch processing and reorg handling improvements" or "v2.31: Refactor batch/checkpoint tracking with bug fixes for event casing and BigInt precision". This would make the changeset more discoverable in the project history without requiring reviewers to open the PR to understand its scope.
Out of Scope Changes Check ❓ Inconclusive Clarify whether the entity name length validation and new network chain additions are part of the scope for this release. If these changes are from the included prior PRs, consider documenting that relationship in the PR description. If they are independently introduced, confirm they align with the v2.31 release objectives and have been tested appropriately.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues Check ✅ Passed The PR addresses the key coding requirements from the linked issues: Issue #786 (event parameter casing) is resolved by updating the EventHandlers templates to use event_key.original instead of event_key.uncapitalized, preserving correct casing from contract ABIs [#786]. Issue #788 (BigInt array precision loss) is addressed through the addition of isNumericArrayAsText parameter in PgStorage to handle numeric arrays as text when Hasura is enabled, preventing precision loss in GraphQL responses [#788]. Issue #749 (Effect API console visibility) appears to have changes to Prometheus metrics initialization and effect cache handling, though the connection to console visibility is less explicit in the code changes [#749].
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dz/oct-20-bug-fixes

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eba95ad and 1e010d1.

📒 Files selected for processing (1)
  • codegenerator/cli/src/config_parsing/chain_helpers.rs (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
codegenerator/cli/src/config_parsing/chain_helpers.rs (1)
codegenerator/cli/src/cli_args/interactive_init/evm_prompts.rs (1)
  • HypersyncNetwork (224-232)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (3)
codegenerator/cli/src/config_parsing/chain_helpers.rs (3)

119-126: LGTM! Alphabetical ordering fixed.

The alphabetical ordering issues flagged in the previous review have been resolved. All new network variants are correctly positioned:

  • ChainwebTestnet22/23/24 properly ordered after ChainwebTestnet21
  • Damon correctly positioned before Darwinia
  • Plasma correctly positioned before Plume
  • SentientTestnet correctly positioned before Sepolia

The test networks_are_defined_in_alphabetical_order should now pass.

Also applies to: 148-150, 304-306, 344-346


473-475: LGTM! New networks properly integrated.

All new network variants are consistently integrated across the codebase:

  • Added to get_confirmed_block_threshold with appropriate thresholds
  • Added to get_tier with correct tier classifications (Bronze for Plasma, Stone for others)

Also applies to: 480-480, 519-519, 526-526, 618-618, 627-629


79-79: Confirm these visibility reductions are intentional and document the breaking changes.

Three networks have lost subenum visibility in this change, creating breaking changes:

  1. BerachainBartio (line 79): Removed from HypersyncNetwork subenum
  2. GaladrielDevnet (line 187): Removed from GraphNetwork subenum (retains NetworkWithExplorer)
  3. UnichainSepolia (line 384): Removed from HypersyncNetwork subenum (retains NetworkWithExplorer)

Impact on code that uses these subenum iterators:

  • HypersyncNetwork::iter() at evm_prompts.rs:224 will no longer include BerachainBartio or UnichainSepolia—users cannot select these networks via interactive init
  • GraphNetwork::iter() at chain_helpers.rs:765 will no longer include GaladrielDevnet
  • Integration and unit tests iterating over these enums are affected

Verify these reductions are intentional and align with operational decisions. If intentional, document this breaking change for users who may have previously referenced these networks through the affected subenums.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@DZakh DZakh linked an issue Oct 20, 2025 that may be closed by this pull request
@DZakh DZakh linked an issue Oct 21, 2025 that may be closed by this pull request
Copy link
Collaborator

@JonoPrest JonoPrest left a comment

Choose a reason for hiding this comment

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

🚀

@DZakh DZakh changed the base branch from dz/rollback-v2-fixes to main October 21, 2025 15:25
@DZakh DZakh changed the title Bug fixes and improvements v2.31 Oct 21, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
codegenerator/cli/templates/dynamic/contract_import_templates/typescript/src/EventHandlers.ts.hbs (1)

20-26: Fix ReScript template to use correct event key casing.

The ReScript EventHandlers template still has the casing bug. While TypeScript and JavaScript templates were corrected to use event.params.{{param.event_key.original}}, the ReScript template at codegenerator/cli/templates/dynamic/contract_import_templates/rescript/src/EventHandlers.res.hbs line 11 still incorrectly uses event.params.{{param.event_key.uncapitalized}} and must be updated to use .original instead.

scenarios/test_codegen/test/lib_tests/FetchState_onBlock_test.res (1)

97-107: Comments contradict asserted ordering and priority.

  • The arrays are in earliest→latest order, but comments say “reverse order (latest to earliest)”.
  • Comments claim “block items have higher priority than events at same block”, but expected arrays place the event before the block item (e.g., (5, 0) precedes (5, 16777216)).

Please align comments (or expectations) to avoid confusion. Also, replace the magic 16777216 (1 << 24) with a named constant in the test for readability.

- // Expected in reverse order (latest to earliest): block items have logIndex=16777216, event has logIndex=0
+ // Expected in chronological order (earliest to latest): block items use BLOCK_ITEM_LOG_BASE, event has logIndex=0

+let BLOCK_ITEM_LOG_BASE = 1 lsl 24
...
-  (0, 16777216),
+  (0, BLOCK_ITEM_LOG_BASE),

Also applies to: 144-155, 192-206, 244-263, 299-301

codegenerator/cli/npm/envio/src/Persistence.res (1)

195-223: Critical issues remain: clamping logic not applied, and underlying storage layer not updated for new parameter

The review comment's concerns are valid. Verification found:

  1. Clamping logic missing (Persistence.res:218–219): The calculation still lacks the proposed if next < 0 { 0 } else { next } guard, allowing negative cache counts.

  2. Storage layer incomplete (PgStorage.res): The underlying setEffectCacheOrThrow implementation signature is missing the ~invalidationsCount parameter—it only has ~effect, ~items, and ~initialize.

  3. Test infrastructure outdated:

    • Mock.res: Test mock lacks ~invalidationsCount parameter
    • TestHelpers_MockDb.res.hbs: Template mock lacks ~invalidationsCount parameter

Only the call site (IO.res:301) correctly passes the new parameter; the definition chain is incomplete.

codegenerator/cli/templates/static/codegen/src/Config.res (1)

40-103: Fix pgSchema parameter to use function argument instead of hardcoded storagePgSchema in Hasura calls.

The bug is confirmed: makeStorage accepts a ~pgSchema parameter but ignores it in both Hasura.trackDatabase and Hasura.trackTables calls, using storagePgSchema instead. This breaks support for non-default schemas.

Apply the proposed diff changes at both callsites:

  • Line ~58: Hasura.trackDatabase(..., ~pgSchema=pgSchema, ...)
  • Line ~75: Hasura.trackTables(..., ~pgSchema=pgSchema, ...)
codegenerator/cli/templates/static/codegen/src/IO.res (1)

285-307: Fix type signature mismatch in Persistence.setEffectCacheOrThrow.

The type definition at line 73 declares the function should accept ~initialize: bool, but the actual implementation at line 195 accepts ~invalidationsCount. The function body uses ~invalidationsCount to compute the cache count (line 219), so the type signature must be updated to reflect this. Either update line 76 from ~initialize: bool to ~invalidationsCount: int, or refactor the implementation to use the ~initialize parameter instead.

codegenerator/cli/npm/envio/src/db/InternalTable.res (1)

292-295: Unify ID field usage in progress update query.

makeProgressFieldsUpdateQuery still uses a literal "id" in WHERE while other queries use the dynamic field. Align to avoid future breakage.

-WHERE "id" = $1;`
+WHERE "${(#id: field :> string)}" = $1;`
🧹 Nitpick comments (41)
.github/workflows/build_and_test.yml (1)

40-52: Move hardcoded credentials to GitHub Secrets.

The Hasura service configuration contains credentials visible in the workflow file: postgres:testing in the database URL (line 43) and testing as the admin secret (line 47). While these are ephemeral test credentials for CI, they should be stored as GitHub repository secrets rather than hardcoded in the workflow.

Replace the hardcoded credentials with GitHub secrets:

-      hasura:
-        image: hasura/graphql-engine:v2.43.0
-        env:
-          HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:testing@postgres:5432/envio-dev
-          HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
-          HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
-          HASURA_GRAPHQL_NO_OF_RETRIES: 10
-          HASURA_GRAPHQL_ADMIN_SECRET: testing
-          HASURA_GRAPHQL_STRINGIFY_NUMERIC_TYPES: "true"
-          HASURA_GRAPHQL_UNAUTHORIZED_ROLE: public
-          PORT: 8080
+      hasura:
+        image: hasura/graphql-engine:v2.43.0
+        env:
+          HASURA_GRAPHQL_DATABASE_URL: ${{ secrets.HASURA_DB_URL }}
+          HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
+          HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
+          HASURA_GRAPHQL_NO_OF_RETRIES: 10
+          HASURA_GRAPHQL_ADMIN_SECRET: ${{ secrets.HASURA_ADMIN_SECRET }}
+          HASURA_GRAPHQL_STRINGIFY_NUMERIC_TYPES: "true"
+          HASURA_GRAPHQL_UNAUTHORIZED_ROLE: public
+          PORT: 8080

(Configure these secrets in your GitHub repository settings: Settings → Secrets and variables → Actions)

codegenerator/cli/templates/dynamic/codegen/src/RegisterHandlers.res.hbs (1)

56-56: Remove unused local chain

The binding isn’t used; it adds noise and may trigger warnings.

-      let chain = ChainMap.Chain.makeUnsafe(~chainId={{chain_config.network_config.id}})
scenarios/test_codegen/test/WriteRead_test.res (1)

42-43: Track TODO: Array of booleans support.

The array of booleans is commented out with a TODO. Please ensure this limitation is tracked as a known issue or future enhancement.

Would you like me to search for existing issues tracking boolean array support, or should a new issue be created?

scenarios/test_codegen/test/lib_tests/FetchState_onBlock_test.res (1)

10-13: Unused helper.

getBlockData is defined but unused in this test. Consider removing to reduce noise.

scenarios/test_codegen/test/lib_tests/Persistence_test.res (1)

81-83: Initial state shape update looks correct; reduce duplication.

Adding reorgCheckpoints and checkpointId matches the updated Persistence.initialState. Consider a small factory to avoid duplicating the literals across tests.

+let makeInitialState = (~cleanRun, ~chains=[], ~cache=Js.Dict.empty()) => {
+  cleanRun, chains, cache, reorgCheckpoints: [], checkpointId: 0
+}
...
-    let initialState: Persistence.initialState = {
-      cleanRun: true,
-      chains: [],
-      cache: Js.Dict.empty(),
-      reorgCheckpoints: [],
-      checkpointId: 0,
-    }
+    let initialState: Persistence.initialState = makeInitialState(~cleanRun=true)
...
-    let initialState: Persistence.initialState = {
-      cleanRun: false,
-      chains: [],
-      cache: Js.Dict.empty(),
-      reorgCheckpoints: [],
-      checkpointId: 0,
-    }
+    let initialState: Persistence.initialState = makeInitialState(~cleanRun=false)

Also applies to: 147-149

codegenerator/cli/templates/static/codegen/src/globalState/GlobalStateManager.res (1)

49-61: Promise error piping can be simplified.

You can pipe the promise rejection directly to handleFatalError without the extra resolve wrapper.

-          S.taskReducer(self.state, task, ~dispatchAction=action =>
-            dispatchAction(~stateId, self, action)
-          )
-          ->Promise.catch(e => {
-            e->handleFatalError
-            Promise.resolve()
-          })
-          ->ignore
+          S.taskReducer(self.state, task, ~dispatchAction=action =>
+            dispatchAction(~stateId, self, action)
+          )
+          ->Promise.catch(handleFatalError)
+          ->ignore
codegenerator/cli/npm/envio/src/Batch.res (2)

95-177: Key consistency for per‑chain dicts

You write with setByInt but read via dangerouslyGetNonOption(Int.toString ...). It works in JS, but using the int accessor consistently (dangerouslyGetByIntNonOption) avoids string conversions and reduces edge cases.

- progressBlockNumberPerChain->Utils.Dict.dangerouslyGetNonOption(
-   fetchState.chainId->Int.toString,
- )
+ progressBlockNumberPerChain->Utils.Dict.dangerouslyGetByIntNonOption(
+   fetchState.chainId,
+ )

(Apply similarly for batchSizePerChain.)


179-214: Avoid param shadowing for readability in addReorgCheckpoints

let prevCheckpointId = ref(prevCheckpointId) shadows the param; rename the ref to prevent confusion.

-    let prevCheckpointId = ref(prevCheckpointId)
+    let prevCheckpointIdRef = ref(prevCheckpointId)
...
-        let checkpointId = prevCheckpointId.contents + 1
-        prevCheckpointId := checkpointId
+        let checkpointId = prevCheckpointIdRef.contents + 1
+        prevCheckpointIdRef := checkpointId
...
-    prevCheckpointId.contents
+    prevCheckpointIdRef.contents
codegenerator/cli/templates/dynamic/codegen/src/TestHelpers_MockDb.res.hbs (2)

121-126: Helper writes use checkpointId=0; consider threading a real checkpointId when needed.

Currently both delete/set write payloads set checkpointId: 0. It’s fine with shouldSaveHistory=false, but if history gets enabled later, this will produce misleading history rows. Consider accepting an optional ~checkpointId for these helpers to support tests that assert on history.

Confirm no tests rely on history produced by these helpers.

Also applies to: 138-143


524-528: Nit: comment typo.

“contatin” → “contain”.

codegenerator/cli/npm/envio/src/Prometheus.res (1)

485-489: Naming nit.

Parameter name “rollbackedProcessedEvents” reads awkwardly. Consider “rolledBackProcessedEvents” or “eventsRolledBack”.

scenarios/test_codegen/test/E2E_test.res (1)

9-9: Typo in test name.

“Currectly” → “Correctly”.

scenarios/test_codegen/test/ChainManager_test.res (2)

111-117: ReorgDetection config alignment with chain config.

Hardcoding ~maxReorgDepth=200 while chain configs vary can produce inconsistent rollback thresholds in tests. Prefer sourcing from chainConfig.maxReorgDepth for each fetcher to reflect real behavior.

-      reorgDetection: ReorgDetection.make(
-        ~chainReorgCheckpoints=[],
-        ~maxReorgDepth=200,
-        ~shouldRollbackOnReorg=false,
-      ),
+      reorgDetection: ReorgDetection.make(
+        ~chainReorgCheckpoints=[],
+        ~maxReorgDepth=chainConfig.maxReorgDepth,
+        ~shouldRollbackOnReorg=false,
+      ),

160-176: Avoid deep recursion in long runs.

The recursive testThatCreatedEventsAreOrderedCorrectly(...) can grow deep with large random inputs. Convert to a loop to prevent stack growth in CI.

-        let rec testThatCreatedEventsAreOrderedCorrectly = (chainManager, lastEvent) => {
+        let testThatCreatedEventsAreOrderedCorrectly = (initialChainManager, lastEvent) => {
+          let rec loop = chainManager => {
             let {items, totalBatchSize, progressedChainsById} = ChainManager.createBatch(
               chainManager,
               ~batchSizeTarget=10000,
             )
             // ensure that the events are ordered correctly
-            if totalBatchSize === 0 {
-              chainManager
-            } else {
+            if totalBatchSize === 0 {
+              chainManager
+            } else {
               items->Array.forEach(
                 item => {
                   allEventsRead->Js.Array2.push(item)->ignore
                 },
               )
               numberOfMockEventsReadFromQueues :=
                 numberOfMockEventsReadFromQueues.contents + totalBatchSize
               let firstEventInBlock = items[0]->Option.getExn
               Assert.equal(
                 firstEventInBlock->EventUtils.getOrderedBatchItemComparator >
                   lastEvent->EventUtils.getOrderedBatchItemComparator,
                 true,
                 ~message="Check that first event in this block group is AFTER the last event before this block group",
               )
               let nextChainFetchers = chainManager.chainFetchers->ChainMap.mapWithKey(
                 (chain, fetcher) => {
                   let fetchState = switch progressedChainsById->Utils.Dict.dangerouslyGetByIntNonOption(
                     chain->ChainMap.Chain.toChainId,
                   ) {
                   | Some(chainAfterBatch) => chainAfterBatch.fetchState
                   | None => fetcher.fetchState
                   }
                   {
                     ...fetcher,
                     fetchState,
                   }
                 },
               )
               let nextChainManager: ChainManager.t = {
                 ...chainManager,
                 chainFetchers: nextChainFetchers,
               }
-              testThatCreatedEventsAreOrderedCorrectly(nextChainManager, lastEvent)
-            }
-          }
+              loop(nextChainManager)
+            }
+          }
+          loop(initialChainManager)
         }
codegenerator/cli/templates/static/codegen/src/EventProcessing.res (3)

29-48: Safer JSON conversion without %raw and with BigInt array support.

Avoid %raw {} and handle arrays of bigint if they ever appear. Use Js.Json.object_ and a small recursive toStringBigInts.

-let convertFieldsToJson = (fields: option<dict<unknown>>) => {
+let convertFieldsToJson = (fields: option<dict<unknown>>) => {
   switch fields {
-  | None => %raw(`{}`)
+  | None => Js.Dict.empty()->Js.Json.object_
   | Some(fields) => {
-      let keys = fields->Js.Dict.keys
-      let new = Js.Dict.empty()
-      for i in 0 to keys->Js.Array2.length - 1 {
-        let key = keys->Js.Array2.unsafe_get(i)
-        let value = fields->Js.Dict.unsafeGet(key)
-        // Skip `undefined` values and convert bigint fields to string
-        // There are not fields with nested bigints, so this is safe
-        new->Js.Dict.set(
-          key,
-          Js.typeof(value) === "bigint" ? value->Utils.magic->BigInt.toString->Utils.magic : value,
-        )
-      }
-      new->(Utils.magic: dict<unknown> => Js.Json.t)
+      let rec toJsonSafe = v => {
+        switch Js.typeof(v) {
+        | "bigint" => v->Utils.magic->BigInt.toString->Utils.magic
+        | "object" =>
+          if Js.Array.isArray(v) {
+            v->(Utils.magic: array<unknown>)
+            ->Js.Array2.map(toJsonSafe)
+            ->(Utils.magic: unknown)
+          } else {
+            v
+          }
+        | _ => v
+        }
+      }
+      let keys = fields->Js.Dict.keys
+      let out = Js.Dict.empty()
+      for i in 0 to keys->Js.Array2.length - 1 {
+        let key = keys->Js.Array2.unsafe_get(i)
+        let value = fields->Js.Dict.unsafeGet(key)
+        if Js.Undefined.testAny(value) == false {
+          out->Js.Dict.set(key, toJsonSafe(value))
+        }
+      }
+      out->Js.Json.object_
     }
   }
 }

223-303: Preload: error swallowing is intentional; add a minimal trace on sync throws.

try/catch currently no‑ops on sync throws. Add a debug trace to help users diagnose preload failures without affecting flow.

-          } catch {
-          | _ => ()
-          }
+          } catch {
+          | exn => Logging.debug(`preload error (sync path): ${exn->Utils.prettifyExn}`)
+          }

364-373: Short‑circuit empty batches to skip DB path.

If batch.items is empty, we can return early and avoid IO.executeBatch() overhead.

 let processEventBatch = async (
   ~batch: Batch.t,
@@
 ) => {
   let totalBatchSize = batch.totalBatchSize
   // Compute chains state for this batch
   let chains: Internal.chains = chainFetchers->computeChainsState
@@
-  try {
+  try {
+    if !batch.items->Utils.Array.notEmpty {
+      // Nothing to do; still register zero-cost metrics for visibility
+      registerProcessEventBatchMetrics(~logger=Logging.getLogger(), ~loadDuration=0, ~handlerDuration=0, ~dbWriteDuration=0)
+      return Ok()
+    }
     let timeRef = Hrtime.makeTimer()
 
-    if batch.items->Utils.Array.notEmpty {
+    if batch.items->Utils.Array.notEmpty {
       await batch->preloadBatchOrThrow(

Also applies to: 391-399, 402-411, 416-423

scenarios/test_codegen/test/lib_tests/PgStorage_test.res (1)

746-757: Confirmed: typo in function name makeCommitedCheckpointIdQuery—should be makeCommittedCheckpointIdQuery.

The misspelled function appears in 3 locations requiring updates:

  • Definition: codegenerator/cli/npm/envio/src/db/InternalTable.res:440
  • Usage: codegenerator/cli/npm/envio/src/PgStorage.res:897
  • Test: scenarios/test_codegen/test/lib_tests/PgStorage_test.res:746, 750

Rename the function and update all call sites for correctness and consistency. A temporary alias is optional but not necessary given the internal scope.

codegenerator/cli/npm/envio/src/Internal.res (3)

148-159: Name clarity: consider spelling out dcs.

If dcs means "dynamic contracts," consider a clearer alias or a brief doc-comment, since it’s part of the public surface and appears on item via accessors. This reduces cognitive load for readers.


305-309: Effect cache schema: unsafe cast and JSON constraints.

cacheOutputSchema uses S.json(~validate=false) + Utils.magic to coerce to effectOutput. This bypasses validation and can hide shape drift. Consider validating with the effect’s declared output schema on load (at least in dev) or store plain Js.Json.t and re-decode at read boundaries.

Example (dev-only validation):

-let cacheOutputSchema = S.json(~validate=false)->(Utils.magic: S.t<Js.Json.t> => S.t<effectOutput>)
+let cacheOutputSchema =
+  if Env.isProd {
+    S.json(~validate=false)->(Utils.magic: S.t<Js.Json.t> => S.t<effectOutput>)
+  } else {
+    // decode as Json then validate using the effect's output schema when reading
+    S.json(~validate=true)->(Utils.magic: S.t<Js.Json.t> => S.t<effectOutput>)
+  }

Also, if effectOutput can contain BigInt(s), confirm serialization strategy (e.g., stringify BigInts) to avoid JSON loss; see issue #788 context.

Also applies to: 313-317


337-348: Tiny doc typo.

Stray “b” in the comment (“...present in the diff) b”). Clean it up for clarity.

codegenerator/cli/npm/envio/src/SafeCheckpointTracking.res (4)

12-35: Assume or enforce monotonic ordering of checkpoints.

getSafeCheckpointId relies on checkpointBlockNumbers being ascending. If upstream doesn’t guarantee order, sort once in make or assert ordering to avoid wrong picks.

Possible guard:

 let make = (
   ~maxReorgDepth,
   ~shouldRollbackOnReorg,
   ~chainReorgCheckpoints: array<Internal.reorgCheckpoint>,
 ) => {
   if maxReorgDepth > 0 && shouldRollbackOnReorg {
-    let checkpointIds = Belt.Array.makeUninitializedUnsafe(chainReorgCheckpoints->Array.length)
-    let checkpointBlockNumbers = Belt.Array.makeUninitializedUnsafe(
-      chainReorgCheckpoints->Array.length,
-    )
+    let sorted = chainReorgCheckpoints->Belt.Array.stableSortBy(c => c.blockNumber)
+    let checkpointIds = Belt.Array.makeUninitializedUnsafe(sorted->Array.length)
+    let checkpointBlockNumbers =
+      Belt.Array.makeUninitializedUnsafe(sorted->Array.length)
-    chainReorgCheckpoints->Js.Array2.forEachi((checkpoint, idx) => {
+    sorted->Js.Array2.forEachi((checkpoint, idx) => {
       checkpointIds->Belt.Array.setUnsafe(idx, checkpoint.checkpointId)
       checkpointBlockNumbers->Belt.Array.setUnsafe(idx, checkpoint.blockNumber)
     })

36-67: Edge-case: negative safeBlockNumber.

When sourceBlockNumber < maxReorgDepth, safeBlockNumber becomes negative; current logic returns 0 if first checkpoint is above it, which is fine. Document this behavior to avoid confusion.


69-108: Parallel arrays and getUnsafe: add invariant checks.

batchCheckpoint* arrays must have equal length; otherwise getUnsafe can crash or push undefined. Add a quick length equality check (assert/log) before the loop.

 let updateOnNewBatch = (
   safeCheckpointTracking: t,
   ~sourceBlockNumber: int,
   ~chainId: int,
   ~batchCheckpointIds: array<int>,
   ~batchCheckpointBlockNumbers: array<int>,
   ~batchCheckpointChainIds: array<int>,
 ) => {
+  Js.Array2.length(batchCheckpointIds) === Js.Array2.length(batchCheckpointBlockNumbers)
+  && Js.Array2.length(batchCheckpointIds) === Js.Array2.length(batchCheckpointChainIds)
+  || Js.Exn.raiseError("Mismatched checkpoint arrays in updateOnNewBatch");

110-131: Rollback across chains: clarify intent.

rollback filters by targetBlockNumber without a chain id, affecting all chains. If intended (keeping a global safe frontier), add a brief comment; otherwise consider a chain-aware variant.

codegenerator/cli/templates/static/codegen/src/eventFetching/ChainManager.res (2)

4-4: Typo in public field: commitedCheckpointIdcommittedCheckpointId.

This leaks into generated projects’ public surface. Consider renaming now and (if needed) keeping a transitional alias to avoid churn.

-type t = { commitedCheckpointId: int, ...
+type t = { committedCheckpointId: int, ...
 ...
-    commitedCheckpointId: 0,
+    committedCheckpointId: 0,
 ...
-    commitedCheckpointId: initialState.checkpointId,
+    committedCheckpointId: initialState.checkpointId,

Also applies to: 27-27, 84-85


139-166: Avoid %raw(Infinity) sentinel; prefer option-based min.

Using JS Infinity cast to int is brittle. Use an option accumulator to track min safely.

-let infinity = (%raw(`Infinity`): int)
-let result = ref(infinity)
+let minSafe: ref<option<int>> = ref(None)
 ...
-  for idx in 0 to chainFetchers->Array.length - 1 {
+  for idx in 0 to chainFetchers->Array.length - 1 {
     let chainFetcher = chainFetchers->Array.getUnsafe(idx)
     switch chainFetcher.safeCheckpointTracking {
     | None => ()
     | Some(sct) => {
         let safeCheckpointId =
           sct->SafeCheckpointTracking.getSafeCheckpointId(
             ~sourceBlockNumber=chainFetcher.currentBlockHeight,
           )
-        if safeCheckpointId < result.contents {
-          result := safeCheckpointId
-        }
+        switch minSafe.contents {
+        | None when safeCheckpointId > 0 => minSafe := Some(safeCheckpointId)
+        | Some(curr) when safeCheckpointId > 0 && safeCheckpointId < curr =>
+            minSafe := Some(safeCheckpointId)
+        | _ => ()
+        }
       }
     }
   }
-  if result.contents === infinity || result.contents === 0 {
-    None
-  } else {
-    Some(result.contents)
-  }
+  minSafe.contents
scenarios/test_codegen/test/lib_tests/FetchState_test.res (2)

2131-2131: Remove stray debug log from tests.

Js.log(updatedFetchState) is noisy in CI output and not asserted. Please drop it.

-    Js.log(updatedFetchState)
+    /* no-op */

140-140: Avoid %raw once ReScript v12 is baseline.

The %raw(\undefined`)` workaround is fine for v11, but please gate/plan removal when v12 is required to avoid raw escapes in tests.

codegenerator/cli/npm/envio/src/PgStorage.res (1)

35-39: Quote custom type names to avoid edge cases.

Custom(name) currently renders as "${pgSchema}".${name}. If a custom type contains capitals or special chars, lack of quotes can break. Quote when safe (no parentheses), otherwise keep as-is.

-          | Custom(name) if !(name->Js.String2.startsWith("NUMERIC(")) => `"${pgSchema}".${name}`
+          | Custom(name) if !(name->Js.String2.includes("(")) => `"${pgSchema}"."${name}"`
+          | Custom(name) => name /* e.g., parameterized like NUMERIC(38,0) handled above */
codegenerator/cli/npm/envio/src/ReorgDetection.res (1)

93-109: Avoid relying on JS object key ordering for correctness.

Comments assume numeric object keys are ordered ASC. While generally true for integer-like keys, prefer explicit sort for robustness.

-  // Js engine automatically orders numeric object keys
-  let ascBlockNumberKeys = dataByBlockNumber->Js.Dict.keys
+  let ascBlockNumberKeys =
+    dataByBlockNumber
+    ->Js.Dict.keys
+    ->Js.Array2.map(Int.fromStringUnsafe) /* or safe parse with filter */
+    ->Js.Array2.sortInPlaceWith((a,b)=>a-b)
+    ->Js.Array2.map(Int.toString)
codegenerator/cli/npm/envio/src/FetchState.res (2)

1229-1234: Deterministic tie‑break for Block items (avoid random).

Js.Math.random_int introduces nondeterminism in production ordering and tests. Use a stable field (e.g., chainId) to break ties.

-        | (Some(Block(_)), _)
-        | (_, Some(Block(_))) =>
-          // Currently block items don't have a timestamp,
-          // so we sort chains with them in a random order
-          Js.Math.random_int(-1, 1)
+        | (Some(Block(_)), _)
+        | (_, Some(Block(_))) =>
+          a.chainId - b.chainId

1166-1179: Rollback: don’t advance onBlock pointer beyond prior value.

Setting latestOnBlockBlockNumber = targetBlockNumber may jump forward if it was behind. Safer to cap at min(previous, target).

-  {
-    ...fetchState,
-    latestOnBlockBlockNumber: targetBlockNumber, // TODO: This is not tested. I assume there might be a possible issue of it skipping some blocks
-  }->updateInternal(
+  {
+    ...fetchState,
+    latestOnBlockBlockNumber:
+      Pervasives.min(fetchState.latestOnBlockBlockNumber, targetBlockNumber),
+  }->updateInternal(
codegenerator/cli/templates/static/codegen/src/IO.res (2)

181-196: Race in error capture can overwrite the first failure.

Multiple entity operations run concurrently; specificError may be overwritten by later handlers. Capture the first error only.

-            | `invalid byte sequence for encoding "UTF8": 0x00` =>
+            | `invalid byte sequence for encoding "UTF8": 0x00` =>
               ...
-              specificError.contents = Some(PgStorage.PgEncodingError({table: entityConfig.table}))
-            | _ => specificError.contents = Some(exn->Utils.prettifyExn)
+              if specificError.contents == None {
+                specificError.contents = Some(PgStorage.PgEncodingError({table: entityConfig.table}))
+              }
+            | _ =>
+              if specificError.contents == None {
+                specificError.contents = Some(exn->Utils.prettifyExn)
+              }

63-142: History batching is solid; minor polish.

Good backfill-before-updates sequencing and UTF‑8 cleanup. Consider moving shouldRemoveInvalidUtf8 detection out of the inner loop and keep per‑entity to avoid repeated checks.

codegenerator/cli/npm/envio/src/db/InternalTable.res (1)

441-443: Typo: “Commited” → “Committed”.

Rename makeCommitedCheckpointIdQuery for clarity and grep-ability.

codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res (2)

554-578: Reorg threshold entry gate is sound; minor nit: reuse helper.

This duplicates readiness logic found in FinishWaitingForNewBlock. Consider extracting a shared predicate to avoid drift.


195-233: Metrics updates tied to batch are good; ensure findFirstEventBlockNumber is O(1).

If Batch.findFirstEventBlockNumber scans, consider caching per chain in Batch.t.

codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res (1)

412-415: Threshold block computation comment nit.

Doc has “higehest”. Consider a quick typo fix when touching next.

codegenerator/cli/npm/envio/src/db/EntityHistory.res (1)

192-212: Pruning query is well reasoned; consider adding supporting index.

For large tables, add (id, checkpoint_id) index on history tables to accelerate anchors and delete USING joins.

scenarios/test_codegen/test/rollback/Rollback_test.res (1)

166-177: Consider removing commented-out assertions.

Multiple blocks of commented test assertions for UpdateEndOfBlockRangeScannedData expectations appear throughout the tests. If these expectations no longer apply to the new architecture, consider removing them entirely rather than leaving them commented.

Apply cleanup to remove obsolete assertions:

-    // Assert.deepEqual(
-    //   tasks.contents->Js.Array2.unsafe_get(0),
-    //   UpdateEndOfBlockRangeScannedData({
-    //     blockNumberThreshold: -198,
-    //     chain: MockConfig.chain1337,
-    //     nextEndOfBlockRangeScannedData: {
-    //       blockHash: block2.blockHash,
-    //       blockNumber: block2.blockNumber,
-    //       chainId: 1337,
-    //     },
-    //   }),
-    // )

(Apply to all similar commented blocks throughout the file.)

Also applies to: 224-235, 319-330, 352-363

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d906825 and cd01da8.

⛔ Files ignored due to path filters (2)
  • scenarios/fuel_test/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • scenarios/test_codegen/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (75)
  • .cursor/rules/navigation.mdc (1 hunks)
  • .github/workflows/build_and_test.yml (2 hunks)
  • codegenerator/cli/npm/envio/src/Batch.res (4 hunks)
  • codegenerator/cli/npm/envio/src/Envio.res (1 hunks)
  • codegenerator/cli/npm/envio/src/EventRegister.res (4 hunks)
  • codegenerator/cli/npm/envio/src/EventRegister.resi (1 hunks)
  • codegenerator/cli/npm/envio/src/FetchState.res (14 hunks)
  • codegenerator/cli/npm/envio/src/Internal.res (5 hunks)
  • codegenerator/cli/npm/envio/src/InternalConfig.res (1 hunks)
  • codegenerator/cli/npm/envio/src/Persistence.res (4 hunks)
  • codegenerator/cli/npm/envio/src/PgStorage.res (16 hunks)
  • codegenerator/cli/npm/envio/src/Prometheus.res (3 hunks)
  • codegenerator/cli/npm/envio/src/ReorgDetection.res (2 hunks)
  • codegenerator/cli/npm/envio/src/SafeCheckpointTracking.res (1 hunks)
  • codegenerator/cli/npm/envio/src/Utils.res (2 hunks)
  • codegenerator/cli/npm/envio/src/db/EntityHistory.res (3 hunks)
  • codegenerator/cli/npm/envio/src/db/InternalTable.gen.ts (1 hunks)
  • codegenerator/cli/npm/envio/src/db/InternalTable.res (11 hunks)
  • codegenerator/cli/npm/envio/src/db/Table.res (1 hunks)
  • codegenerator/cli/npm/envio/src/sources/EventRouter.res (1 hunks)
  • codegenerator/cli/npm/envio/src/sources/Source.res (1 hunks)
  • codegenerator/cli/src/config_parsing/entity_parsing.rs (1 hunks)
  • codegenerator/cli/templates/dynamic/codegen/src/RegisterHandlers.res.hbs (1 hunks)
  • codegenerator/cli/templates/dynamic/codegen/src/TestHelpers_MockDb.res.hbs (3 hunks)
  • codegenerator/cli/templates/dynamic/codegen/src/Types.res.hbs (0 hunks)
  • codegenerator/cli/templates/dynamic/codegen/src/db/Entities.res.hbs (3 hunks)
  • codegenerator/cli/templates/dynamic/contract_import_templates/javascript/src/EventHandlers.js.hbs (1 hunks)
  • codegenerator/cli/templates/dynamic/contract_import_templates/typescript/src/EventHandlers.ts.hbs (1 hunks)
  • codegenerator/cli/templates/static/codegen/src/Config.res (4 hunks)
  • codegenerator/cli/templates/static/codegen/src/EventProcessing.res (13 hunks)
  • codegenerator/cli/templates/static/codegen/src/IO.res (6 hunks)
  • codegenerator/cli/templates/static/codegen/src/InMemoryStore.res (4 hunks)
  • codegenerator/cli/templates/static/codegen/src/InMemoryTable.res (5 hunks)
  • codegenerator/cli/templates/static/codegen/src/LoadLayer.res (1 hunks)
  • codegenerator/cli/templates/static/codegen/src/UserContext.res (4 hunks)
  • codegenerator/cli/templates/static/codegen/src/db/DbFunctions.res (0 hunks)
  • codegenerator/cli/templates/static/codegen/src/db/DbFunctionsImplementation.js (0 hunks)
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res (14 hunks)
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainManager.res (6 hunks)
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res (29 hunks)
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalStateManager.res (3 hunks)
  • scenarios/erc20_multichain_factory/test/MockChainData.res (1 hunks)
  • scenarios/erc20_multichain_factory/test/RollbackDynamicContract_test.res (0 hunks)
  • scenarios/erc20_multichain_factory/test/RollbackMultichain_test.res (0 hunks)
  • scenarios/erc20_multichain_factory/test/TestDeleteEntity.res (0 hunks)
  • scenarios/erc20_multichain_factory/test/TestWhereQuery.res (0 hunks)
  • scenarios/helpers/src/ChainMocking.res (1 hunks)
  • scenarios/helpers/src/Indexer.res (0 hunks)
  • scenarios/test_codegen/schema.graphql (1 hunks)
  • scenarios/test_codegen/test/ChainFetcher_test.res (0 hunks)
  • scenarios/test_codegen/test/ChainManager_test.res (5 hunks)
  • scenarios/test_codegen/test/ChainsStateComputation_test.res (0 hunks)
  • scenarios/test_codegen/test/E2EEthNode_test.res (1 hunks)
  • scenarios/test_codegen/test/E2E_test.res (4 hunks)
  • scenarios/test_codegen/test/EventHandler_test.ts (4 hunks)
  • scenarios/test_codegen/test/EventOrigin_test.res (1 hunks)
  • scenarios/test_codegen/test/Integration_ts_helpers.res (2 hunks)
  • scenarios/test_codegen/test/Mock_test.res (0 hunks)
  • scenarios/test_codegen/test/ReorgDetection_test.res (23 hunks)
  • scenarios/test_codegen/test/SerDe_Test.res (0 hunks)
  • scenarios/test_codegen/test/WriteRead_test.res (1 hunks)
  • scenarios/test_codegen/test/__mocks__/DbStub.res (0 hunks)
  • scenarios/test_codegen/test/__mocks__/MockConfig.res (1 hunks)
  • scenarios/test_codegen/test/helpers/Mock.res (14 hunks)
  • scenarios/test_codegen/test/lib_tests/EntityHistory_test.res (1 hunks)
  • scenarios/test_codegen/test/lib_tests/EventRouter_test.res (1 hunks)
  • scenarios/test_codegen/test/lib_tests/FetchState_onBlock_test.res (1 hunks)
  • scenarios/test_codegen/test/lib_tests/FetchState_test.res (41 hunks)
  • scenarios/test_codegen/test/lib_tests/Persistence_test.res (2 hunks)
  • scenarios/test_codegen/test/lib_tests/PgStorage_test.res (20 hunks)
  • scenarios/test_codegen/test/lib_tests/SourceManager_test.res (1 hunks)
  • scenarios/test_codegen/test/rollback/MockChainData.res (1 hunks)
  • scenarios/test_codegen/test/rollback/Rollback_test.res (41 hunks)
  • scenarios/test_codegen/test/schema_types/BigDecimal_test.res (1 hunks)
  • scenarios/test_codegen/test/schema_types/Timestamp_test.res (1 hunks)
💤 Files with no reviewable changes (13)
  • scenarios/erc20_multichain_factory/test/TestDeleteEntity.res
  • scenarios/test_codegen/test/ChainFetcher_test.res
  • scenarios/erc20_multichain_factory/test/TestWhereQuery.res
  • scenarios/test_codegen/test/Mock_test.res
  • scenarios/erc20_multichain_factory/test/RollbackDynamicContract_test.res
  • scenarios/erc20_multichain_factory/test/RollbackMultichain_test.res
  • scenarios/test_codegen/test/SerDe_Test.res
  • scenarios/test_codegen/test/ChainsStateComputation_test.res
  • codegenerator/cli/templates/static/codegen/src/db/DbFunctionsImplementation.js
  • scenarios/test_codegen/test/mocks/DbStub.res
  • scenarios/helpers/src/Indexer.res
  • codegenerator/cli/templates/static/codegen/src/db/DbFunctions.res
  • codegenerator/cli/templates/dynamic/codegen/src/Types.res.hbs
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{res,resi}

📄 CodeRabbit inference engine (.cursor/rules/rescript.mdc)

**/*.{res,resi}: Never use [| item |] to create an array. Use [ item ] instead.
Must always use = for setting value to a field. Use := only for ref values created using ref function.
ReScript has record types which require a type definition before hand. You can access record fields by dot like foo.myField.
It's also possible to define an inline object, it'll have quoted fields in this case.
Use records when working with structured data, and objects to conveniently pass payload data between functions.
Never use %raw to access object fields if you know the type.

Files:

  • codegenerator/cli/npm/envio/src/EventRegister.resi
  • codegenerator/cli/npm/envio/src/sources/Source.res
  • scenarios/test_codegen/test/lib_tests/FetchState_onBlock_test.res
  • scenarios/test_codegen/test/WriteRead_test.res
  • codegenerator/cli/npm/envio/src/EventRegister.res
  • scenarios/test_codegen/test/EventOrigin_test.res
  • scenarios/test_codegen/test/lib_tests/EventRouter_test.res
  • codegenerator/cli/templates/static/codegen/src/LoadLayer.res
  • codegenerator/cli/npm/envio/src/SafeCheckpointTracking.res
  • scenarios/test_codegen/test/lib_tests/SourceManager_test.res
  • scenarios/test_codegen/test/lib_tests/Persistence_test.res
  • codegenerator/cli/npm/envio/src/Envio.res
  • scenarios/test_codegen/test/rollback/MockChainData.res
  • scenarios/test_codegen/test/schema_types/BigDecimal_test.res
  • scenarios/test_codegen/test/E2E_test.res
  • codegenerator/cli/npm/envio/src/db/Table.res
  • codegenerator/cli/npm/envio/src/InternalConfig.res
  • scenarios/test_codegen/test/schema_types/Timestamp_test.res
  • scenarios/test_codegen/test/E2EEthNode_test.res
  • scenarios/erc20_multichain_factory/test/MockChainData.res
  • codegenerator/cli/templates/static/codegen/src/EventProcessing.res
  • codegenerator/cli/npm/envio/src/sources/EventRouter.res
  • codegenerator/cli/templates/static/codegen/src/Config.res
  • codegenerator/cli/templates/static/codegen/src/InMemoryTable.res
  • scenarios/test_codegen/test/__mocks__/MockConfig.res
  • codegenerator/cli/templates/static/codegen/src/InMemoryStore.res
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainManager.res
  • scenarios/helpers/src/ChainMocking.res
  • scenarios/test_codegen/test/rollback/Rollback_test.res
  • scenarios/test_codegen/test/lib_tests/PgStorage_test.res
  • codegenerator/cli/npm/envio/src/PgStorage.res
  • codegenerator/cli/templates/static/codegen/src/UserContext.res
  • codegenerator/cli/templates/static/codegen/src/IO.res
  • scenarios/test_codegen/test/ReorgDetection_test.res
  • scenarios/test_codegen/test/Integration_ts_helpers.res
  • scenarios/test_codegen/test/helpers/Mock.res
  • codegenerator/cli/npm/envio/src/Batch.res
  • codegenerator/cli/npm/envio/src/Persistence.res
  • scenarios/test_codegen/test/lib_tests/EntityHistory_test.res
  • scenarios/test_codegen/test/ChainManager_test.res
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalStateManager.res
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res
  • codegenerator/cli/npm/envio/src/db/EntityHistory.res
  • codegenerator/cli/npm/envio/src/Prometheus.res
  • codegenerator/cli/npm/envio/src/ReorgDetection.res
  • codegenerator/cli/npm/envio/src/Internal.res
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res
  • scenarios/test_codegen/test/lib_tests/FetchState_test.res
  • codegenerator/cli/npm/envio/src/FetchState.res
  • codegenerator/cli/npm/envio/src/Utils.res
  • codegenerator/cli/npm/envio/src/db/InternalTable.res
**/*.res

📄 CodeRabbit inference engine (.cursor/rules/navigation.mdc)

Prefer reading ReScript .res modules directly; compiled .js artifacts can be ignored

Files:

  • codegenerator/cli/npm/envio/src/sources/Source.res
  • scenarios/test_codegen/test/lib_tests/FetchState_onBlock_test.res
  • scenarios/test_codegen/test/WriteRead_test.res
  • codegenerator/cli/npm/envio/src/EventRegister.res
  • scenarios/test_codegen/test/EventOrigin_test.res
  • scenarios/test_codegen/test/lib_tests/EventRouter_test.res
  • codegenerator/cli/templates/static/codegen/src/LoadLayer.res
  • codegenerator/cli/npm/envio/src/SafeCheckpointTracking.res
  • scenarios/test_codegen/test/lib_tests/SourceManager_test.res
  • scenarios/test_codegen/test/lib_tests/Persistence_test.res
  • codegenerator/cli/npm/envio/src/Envio.res
  • scenarios/test_codegen/test/rollback/MockChainData.res
  • scenarios/test_codegen/test/schema_types/BigDecimal_test.res
  • scenarios/test_codegen/test/E2E_test.res
  • codegenerator/cli/npm/envio/src/db/Table.res
  • codegenerator/cli/npm/envio/src/InternalConfig.res
  • scenarios/test_codegen/test/schema_types/Timestamp_test.res
  • scenarios/test_codegen/test/E2EEthNode_test.res
  • scenarios/erc20_multichain_factory/test/MockChainData.res
  • codegenerator/cli/templates/static/codegen/src/EventProcessing.res
  • codegenerator/cli/npm/envio/src/sources/EventRouter.res
  • codegenerator/cli/templates/static/codegen/src/Config.res
  • codegenerator/cli/templates/static/codegen/src/InMemoryTable.res
  • scenarios/test_codegen/test/__mocks__/MockConfig.res
  • codegenerator/cli/templates/static/codegen/src/InMemoryStore.res
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainManager.res
  • scenarios/helpers/src/ChainMocking.res
  • scenarios/test_codegen/test/rollback/Rollback_test.res
  • scenarios/test_codegen/test/lib_tests/PgStorage_test.res
  • codegenerator/cli/npm/envio/src/PgStorage.res
  • codegenerator/cli/templates/static/codegen/src/UserContext.res
  • codegenerator/cli/templates/static/codegen/src/IO.res
  • scenarios/test_codegen/test/ReorgDetection_test.res
  • scenarios/test_codegen/test/Integration_ts_helpers.res
  • scenarios/test_codegen/test/helpers/Mock.res
  • codegenerator/cli/npm/envio/src/Batch.res
  • codegenerator/cli/npm/envio/src/Persistence.res
  • scenarios/test_codegen/test/lib_tests/EntityHistory_test.res
  • scenarios/test_codegen/test/ChainManager_test.res
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalStateManager.res
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res
  • codegenerator/cli/npm/envio/src/db/EntityHistory.res
  • codegenerator/cli/npm/envio/src/Prometheus.res
  • codegenerator/cli/npm/envio/src/ReorgDetection.res
  • codegenerator/cli/npm/envio/src/Internal.res
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res
  • scenarios/test_codegen/test/lib_tests/FetchState_test.res
  • codegenerator/cli/npm/envio/src/FetchState.res
  • codegenerator/cli/npm/envio/src/Utils.res
  • codegenerator/cli/npm/envio/src/db/InternalTable.res
codegenerator/cli/templates/{static,dynamic}/codegen/src/**/*

📄 CodeRabbit inference engine (.cursor/rules/navigation.mdc)

Edit template versions under codegenerator/cli/templates/static/codegen/src or codegenerator/cli/templates/dynamic/codegen/src instead of editing generated/src

Files:

  • codegenerator/cli/templates/static/codegen/src/LoadLayer.res
  • codegenerator/cli/templates/dynamic/codegen/src/TestHelpers_MockDb.res.hbs
  • codegenerator/cli/templates/static/codegen/src/EventProcessing.res
  • codegenerator/cli/templates/static/codegen/src/Config.res
  • codegenerator/cli/templates/static/codegen/src/InMemoryTable.res
  • codegenerator/cli/templates/static/codegen/src/InMemoryStore.res
  • codegenerator/cli/templates/dynamic/codegen/src/db/Entities.res.hbs
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainManager.res
  • codegenerator/cli/templates/static/codegen/src/UserContext.res
  • codegenerator/cli/templates/static/codegen/src/IO.res
  • codegenerator/cli/templates/dynamic/codegen/src/RegisterHandlers.res.hbs
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalStateManager.res
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res
scenarios/test_codegen/**/*.ts

📄 CodeRabbit inference engine (scenarios/test_codegen/.cursor/rules/hyperindex.mdc)

scenarios/test_codegen/**/*.ts: After changing any TypeScript files, run: pnpm tsc --noEmit to ensure successful compilation
When updating existing entities in handlers, always use the spread operator to create updated objects before persisting
For any external call (e.g., fetch), wrap it in an Effect via experimental_createEffect and consume via context.effect
Use !context.isPreload to skip logic that should not run during preload
In TypeScript, set relationship fields using *_id properties (e.g., token_id) rather than object references
Always cast timestamps from events to BigInt (e.g., BigInt(event.block.timestamp))
When matching addresses in configuration objects within code, ensure keys are lowercase and compare using address.toLowerCase()
Use string | undefined for optional string fields instead of string | null
Always normalize token amounts to a standard decimal (e.g., 18) before addition across tokens; use helpers like normalizeAmountToUSD()

Files:

  • scenarios/test_codegen/test/EventHandler_test.ts
scenarios/test_codegen/**/schema.graphql

📄 CodeRabbit inference engine (scenarios/test_codegen/.cursor/rules/hyperindex.mdc)

scenarios/test_codegen/**/schema.graphql: After changing schema.graphql, run: pnpm codegen
Do not add the @entity decorator to GraphQL schema types
Avoid time-series aggregate fields (e.g., dailyVolume) in the GraphQL schema
Never use arrays of entities in the schema (e.g., [User!]!)
Model relationships using *_id fields in the schema (e.g., user_id: String!) instead of entity references

Files:

  • scenarios/test_codegen/schema.graphql
🧠 Learnings (11)
📚 Learning: 2025-10-13T12:26:45.351Z
Learnt from: CR
PR: enviodev/hyperindex#0
File: .cursor/rules/subgraph-migration.mdc:0-0
Timestamp: 2025-10-13T12:26:45.351Z
Learning: Applies to src/**/*.ts : Register dynamically created contracts using `contractRegister` above the corresponding factory event handler

Applied to files:

  • codegenerator/cli/npm/envio/src/EventRegister.res
📚 Learning: 2025-10-13T12:27:30.803Z
Learnt from: CR
PR: enviodev/hyperindex#0
File: codegenerator/cli/templates/static/shared/.cursor/rules/subgraph-migration.mdc:0-0
Timestamp: 2025-10-13T12:27:30.803Z
Learning: Applies to codegenerator/cli/templates/static/shared/src/**/*.ts : Register dynamic contracts using contractRegister above the creating event’s handler (e.g., context.addPair(event.params.pair))

Applied to files:

  • codegenerator/cli/npm/envio/src/EventRegister.res
📚 Learning: 2025-10-13T12:27:30.803Z
Learnt from: CR
PR: enviodev/hyperindex#0
File: codegenerator/cli/templates/static/shared/.cursor/rules/subgraph-migration.mdc:0-0
Timestamp: 2025-10-13T12:27:30.803Z
Learning: Applies to codegenerator/cli/templates/static/shared/src/**/*.ts : Prefix all entity IDs with event.chainId and never hardcode chainId=1; accept chainId in helper functions; use chain-specific Bundle IDs like `${chainId}-1`

Applied to files:

  • codegenerator/cli/templates/dynamic/codegen/src/TestHelpers_MockDb.res.hbs
  • scenarios/test_codegen/test/EventHandler_test.ts
📚 Learning: 2025-08-11T08:49:57.399Z
Learnt from: CR
PR: enviodev/hyperindex#0
File: scenarios/test_codegen/.cursor/rules/hyperindex.mdc:0-0
Timestamp: 2025-08-11T08:49:57.399Z
Learning: Applies to scenarios/test_codegen/**/schema.graphql : Do not add the entity decorator to GraphQL schema types

Applied to files:

  • scenarios/test_codegen/schema.graphql
📚 Learning: 2025-08-11T08:49:57.399Z
Learnt from: CR
PR: enviodev/hyperindex#0
File: scenarios/test_codegen/.cursor/rules/hyperindex.mdc:0-0
Timestamp: 2025-08-11T08:49:57.399Z
Learning: Applies to scenarios/test_codegen/**/schema.graphql : Model relationships using *_id fields in the schema (e.g., user_id: String!) instead of entity references

Applied to files:

  • scenarios/test_codegen/schema.graphql
📚 Learning: 2025-10-13T12:27:30.803Z
Learnt from: CR
PR: enviodev/hyperindex#0
File: codegenerator/cli/templates/static/shared/.cursor/rules/subgraph-migration.mdc:0-0
Timestamp: 2025-10-13T12:27:30.803Z
Learning: Applies to codegenerator/cli/templates/static/shared/src/EventHandlers.ts : Remove the original src/EventHandlers.ts after moving handlers into contract-specific files

Applied to files:

  • codegenerator/cli/templates/dynamic/contract_import_templates/typescript/src/EventHandlers.ts.hbs
📚 Learning: 2025-10-13T12:27:30.803Z
Learnt from: CR
PR: enviodev/hyperindex#0
File: codegenerator/cli/templates/static/shared/.cursor/rules/subgraph-migration.mdc:0-0
Timestamp: 2025-10-13T12:27:30.803Z
Learning: Applies to codegenerator/cli/templates/static/shared/**/config.yaml : Refactor to contract-specific handler files in config.yaml and remove the single EventHandlers.ts reference

Applied to files:

  • codegenerator/cli/templates/dynamic/contract_import_templates/typescript/src/EventHandlers.ts.hbs
  • codegenerator/cli/templates/dynamic/codegen/src/RegisterHandlers.res.hbs
📚 Learning: 2025-10-13T12:27:30.803Z
Learnt from: CR
PR: enviodev/hyperindex#0
File: codegenerator/cli/templates/static/shared/.cursor/rules/subgraph-migration.mdc:0-0
Timestamp: 2025-10-13T12:27:30.803Z
Learning: Applies to codegenerator/cli/templates/static/shared/src/EventHandlers.ts : Clear all boilerplate logic from src/EventHandlers.ts and replace handlers with empty skeletons before implementing business logic

Applied to files:

  • codegenerator/cli/templates/dynamic/contract_import_templates/typescript/src/EventHandlers.ts.hbs
📚 Learning: 2025-10-13T12:26:45.351Z
Learnt from: CR
PR: enviodev/hyperindex#0
File: .cursor/rules/subgraph-migration.mdc:0-0
Timestamp: 2025-10-13T12:26:45.351Z
Learning: Applies to src/EventHandlers.ts : Remove the monolithic EventHandlers.ts file after moving handlers to contract-specific files

Applied to files:

  • codegenerator/cli/templates/dynamic/contract_import_templates/typescript/src/EventHandlers.ts.hbs
📚 Learning: 2025-08-11T08:42:57.311Z
Learnt from: CR
PR: enviodev/hyperindex#0
File: codegenerator/cli/templates/static/shared/.cursor/rules/hyperindex.mdc:0-0
Timestamp: 2025-08-11T08:42:57.311Z
Learning: Applies to codegenerator/cli/templates/static/shared/**/*.ts : When updating existing entities in handlers, always use the spread operator because returned objects are read-only

Applied to files:

  • codegenerator/cli/templates/dynamic/contract_import_templates/typescript/src/EventHandlers.ts.hbs
📚 Learning: 2025-05-27T17:07:12.878Z
Learnt from: JonoPrest
PR: enviodev/hyperindex#555
File: scenarios/test_codegen/test/lib_tests/EntityHistory_test.res:631-659
Timestamp: 2025-05-27T17:07:12.878Z
Learning: In isolated tests for EntityHistory rollbacks with controlled mock data setup, the ordering of getRollbackDiff results is deterministic and doesn't require sorting before comparison with deepStrictEqual.

Applied to files:

  • scenarios/test_codegen/test/rollback/Rollback_test.res
  • scenarios/test_codegen/test/lib_tests/EntityHistory_test.res
🪛 Checkov (3.2.334)
.github/workflows/build_and_test.yml

[medium] 43-44: Basic Auth Credentials

(CKV_SECRET_4)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test

Comment on lines +230 to +244
let historyTableRef = `"${pgSchema}"."${historyTableName(~entityName, ~entityIndex)}"`
`WITH target_ids AS (
SELECT UNNEST($1::${(Text: Table.fieldType :> string)}[]) AS id
),
missing_history AS (
SELECT e.*
FROM "${pgSchema}"."${entityName}" e
JOIN target_ids t ON e.id = t.id
LEFT JOIN ${historyTableRef} h ON h.id = e.id
WHERE h.id IS NULL
)
INSERT INTO ${historyTableRef}
SELECT *, 0 AS ${checkpointIdFieldName}, '${(RowAction.SET :> string)}' as ${changeFieldName}
FROM missing_history;`
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Backfill INSERT uses SELECT * without an explicit column list — brittle and risky.

If entity columns change or order differs, backfill will misalign. Specify columns explicitly.

-INSERT INTO ${historyTableRef}
-SELECT *, 0 AS ${checkpointIdFieldName}, '${(RowAction.SET :> string)}' as ${changeFieldName}
-FROM missing_history;`
+INSERT INTO ${historyTableRef} (
+  ${/* data fields first, then checkpoint and change */""}
+  ${table.fields
+    ->Belt.Array.map(field => field->getFieldName)
+    ->Belt.Array.keep(name => name != checkpointIdFieldName && name != changeFieldName)
+    ->Belt.Array.map(name => "\"" ++ name ++ "\"")
+    ->Js.Array2.joinWith(", ")
+  }, "${checkpointIdFieldName}", "${changeFieldName}"
+)
+SELECT
+  ${table.fields
+    ->Belt.Array.map(field => field->getFieldName)
+    ->Belt.Array.keep(name => name != checkpointIdFieldName && name != changeFieldName)
+    ->Belt.Array.map(name => "e.\"" ++ name ++ "\"")
+    ->Js.Array2.joinWith(", ")
+  },
+  0 AS ${checkpointIdFieldName},
+  '${(RowAction.SET :> string)}' as ${changeFieldName}
+FROM missing_history e;`

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In codegenerator/cli/npm/envio/src/db/EntityHistory.res around lines 230 to 244,
the backfill INSERT currently uses SELECT * which is brittle; replace it with an
explicit column list for the history table and an explicit SELECT column list
from the entity table to guarantee correct ordering and mapping. Concretely,
construct the target column list for ${historyTableName(~entityName,
~entityIndex)} including all history columns (e.g. the entity fields in the
exact order the history table expects plus ${checkpointIdFieldName} and
${changeFieldName}), and change the SELECT to list the corresponding entity
columns (matching types/order) followed by the literal 0 for
${checkpointIdFieldName} and '${(RowAction.SET :> string)}' for
${changeFieldName}; ensure any computed or default columns are handled
explicitly and the generated SQL uses these explicit column lists instead of
SELECT *.

Comment on lines +414 to +438
let makeGetReorgCheckpointsQuery = (~pgSchema): string => {
// Use CTE to pre-filter chains and compute safe_block once per chain
// This is faster because:
// 1. Chains table is small, so filtering it first is cheap
// 2. safe_block is computed once per chain, not per checkpoint
// 3. Query planner can materialize the small CTE result before joining
`WITH reorg_chains AS (
SELECT
"${(#id: Chains.field :> string)}" as id,
"${(#source_block: Chains.field :> string)}" - "${(#max_reorg_depth: Chains.field :> string)}" AS safe_block
FROM "${pgSchema}"."${Chains.table.tableName}"
WHERE "${(#max_reorg_depth: Chains.field :> string)}" > 0
AND "${(#progress_block: Chains.field :> string)}" > "${(#source_block: Chains.field :> string)}" - "${(#max_reorg_depth: Chains.field :> string)}"
)
SELECT
cp."${(#id: field :> string)}",
cp."${(#chain_id: field :> string)}",
cp."${(#block_number: field :> string)}",
cp."${(#block_hash: field :> string)}"
FROM "${pgSchema}"."${table.tableName}" cp
INNER JOIN reorg_chains rc
ON cp."${(#chain_id: field :> string)}" = rc.id
WHERE cp."${(#block_hash: field :> string)}" IS NOT NULL
AND cp."${(#block_number: field :> string)}" >= rc.safe_block;` // Include safe_block checkpoint to use it for safe checkpoint tracking
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Add supporting indexes for checkpoint lookups.

Queries filter/join by chain_id and block_number, and order by id. Add a composite index like (chain_id, block_number, id DESC) to speed up getRollbackTargetCheckpoint and the reorg CTE join.


🏁 Script executed:

#!/bin/bash
# Check if indexes exist on envio_checkpoints
rg -nP 'CREATE\s+INDEX.*envio_checkpoints' -C2

Length of output: 0


I need to explore the codebase more broadly to understand how indexes are defined and check for existing indexes on the checkpoints table.

Let me search the codebase more comprehensively for how database indexes are defined and managed.




---

🏁 Script executed:

```shell
#!/bin/bash
# Search for migration files or schema definitions
fd -e sql -e ts -e res -e js | head -20

Length of output: 891


🏁 Script executed:

#!/bin/bash
# Look for any index definitions more broadly
rg -nP '(index|INDEX)' --type=sql -C1 | head -50

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Search for envio_checkpoints mentions
rg -n 'envio_checkpoints|checkpoints' --type=res -C2 | head -40

Length of output: 27







🏁 Script executed:

#!/bin/bash
# Find InternalTable.res file
find . -name "InternalTable.res" -type f

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Search for checkpoints table definition in .res files
rg -n 'checkpoints|Checkpoints' -C3 | head -60

Length of output: 9892


🏁 Script executed:

#!/bin/bash
# Look for migration or schema directories
fd -t d 'migrations|schema|db' | head -20

Length of output: 212


🏁 Script executed:

#!/bin/bash
# Search for CREATE statements or index patterns in any file type
rg -n 'CREATE.*INDEX|create.*index' -C2 | head -50

Length of output: 5995







🏁 Script executed:

#!/bin/bash
# Read InternalTable.res to find Checkpoints table definition and index configuration
head -500 codegenerator/cli/npm/envio/src/db/InternalTable.res | tail -200

Length of output: 6558


🏁 Script executed:

#!/bin/bash
# Search for Checkpoints module in InternalTable.res
rg -n 'module Checkpoints|table.*=.*Checkpoints' codegenerator/cli/npm/envio/src/db/InternalTable.res -A10

Length of output: 202


🏁 Script executed:

#!/bin/bash
# Search for getRollbackTargetCheckpoint function mentioned in review
rg -n 'getRollbackTargetCheckpoint|rollback.*checkpoint' codegenerator/cli/npm/envio/src/db/InternalTable.res -B2 -A5

Length of output: 168


🏁 Script executed:

#!/bin/bash
# Check what indexes are defined for tables in InternalTable.res
rg -n 'indexes\s*=|makeCreateIndexQuery' codegenerator/cli/npm/envio/src/db/InternalTable.res -B2 -A2

Length of output: 0





---

🏁 Script executed:

```shell
#!/bin/bash
# Get getRollbackTargetCheckpoint implementation
rg -n 'makeGetRollbackTargetCheckpointQuery|getRollbackTargetCheckpoint' codegenerator/cli/npm/envio/src/db/InternalTable.res -A15

Length of output: 1184


🏁 Script executed:

#!/bin/bash
# Check other table definitions for index patterns
rg -n 'let table = mkTable' codegenerator/cli/npm/envio/src/db/InternalTable.res -A15 | head -80

Length of output: 3820


🏁 Script executed:

#!/bin/bash
# Look for any indexes field in table definitions across the file
cat codegenerator/cli/npm/envio/src/db/InternalTable.res | grep -n 'indexes' -B5 -A5

Length of output: 0





---

🏁 Script executed:

```shell
#!/bin/bash
# Search for where indexes are created for Checkpoints or internal tables
rg -n 'makeCreateIndexQuery.*[Cc]heckpoint' --type=res -C3

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for PgStorage index creation calls
rg -n 'makeCreateTableIndicesQuery|makeCreateIndexQuery' --type=res -C2 | head -50

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Check PgStorage module to understand index creation
find . -name "PgStorage.res" -type f

Length of output: 47







🏁 Script executed:

#!/bin/bash
# Search for index creation calls without type filter
rg -n 'makeCreateTableIndicesQuery|makeCreateIndexQuery' -C2 | head -60

Length of output: 5019


🏁 Script executed:

#!/bin/bash
# Search where indexes might be created for Checkpoints or internal tables
rg -n 'Checkpoints.*index|createIndex.*Checkpoint' -C2

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look at PgStorage.res to understand how indexes are created
head -200 codegenerator/cli/npm/envio/src/PgStorage.res | tail -100

Length of output: 3446


🏁 Script executed:

#!/bin/bash
# Search for where internal tables get indexes created
rg -n 'InternalTable\.(Checkpoints|Chains|RawEvents)' codegenerator/cli/npm/envio/src/PgStorage.res -B2 -A5

Length of output: 2976





🏁 Script executed:

#!/bin/bash
# Look for tables with indexes defined to understand the pattern
rg -n 'indexes\s*:' codegenerator/cli -B3 -A3 | head -40

Length of output: 3535


🏁 Script executed:

#!/bin/bash
# Search for Table.table type definition to understand indexes field
rg -n 'type table\s*=|indexes' codegenerator/cli/npm/envio/src/Table.res -B2 -A5 | head -60

Length of output: 81





---

🏁 Script executed:

```shell
#!/bin/bash
# Find Table.res file
find . -name "Table.res" -type f

Length of output: 46


🏁 Script executed:

#!/bin/bash
# Search for table type definition
rg -n 'type table\s*=' codegenerator/cli/npm/envio/src -A10

Length of output: 750


🏁 Script executed:

#!/bin/bash
# Search in makeCreateTableIndicesQuery to understand how indexes are accessed
rg -n 'makeCreateTableIndicesQuery' -A20 codegenerator/cli/npm/envio/src/PgStorage.res

Length of output: 1569



---

🏁 Script executed:

```shell
#!/bin/bash
# Verify the exact Checkpoints.table definition
sed -n '403,412p' codegenerator/cli/npm/envio/src/db/InternalTable.res

Length of output: 486


Add supporting indexes for checkpoint queries.

The envio_checkpoints table at lines 403-412 lacks composite indexes despite multiple queries filtering and joining on chain_id and block_number. Both makeGetRollbackTargetCheckpointQuery (lines 501-506) and makeGetReorgCheckpointsQuery (lines 414-438) perform these operations without index support, causing full table scans.

Add ~compositeIndices=[["chain_id", "block_number"]] to the mkTable call for the Checkpoints table to enable the index generation mechanism (via makeCreateTableIndicesQuery in PgStorage.res) to create the supporting indexes.

🤖 Prompt for AI Agents
In codegenerator/cli/npm/envio/src/db/InternalTable.res around lines 403 to 412
and relevant query usage at 414 to 438, the Checkpoints table definition
(mkTable call) lacks a composite index on chain_id and block_number causing
full-table scans for queries like makeGetRollbackTargetCheckpointQuery and
makeGetReorgCheckpointsQuery; fix by adding the composite index configuration to
the mkTable call for the Checkpoints table: include
~compositeIndices=[["chain_id","block_number"]] in the mkTable parameters so the
existing makeCreateTableIndicesQuery will generate the supporting composite
index.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd01da8 and eba95ad.

📒 Files selected for processing (1)
  • codegenerator/cli/src/config_parsing/chain_helpers.rs (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
codegenerator/cli/src/config_parsing/chain_helpers.rs (1)
codegenerator/cli/src/cli_args/interactive_init/evm_prompts.rs (1)
  • HypersyncNetwork (224-232)
🪛 GitHub Actions: Build & Test Scenario
codegenerator/cli/src/config_parsing/chain_helpers.rs

[error] 689-689: config_parsing::chain_helpers::test::networks_are_defined_in_alphabetical_order failed: Networks are not sorted alphabetically (see assertion at chain_helpers.rs:689).

🔇 Additional comments (4)
codegenerator/cli/src/config_parsing/chain_helpers.rs (4)

186-187: Confirm intent: removed from HypersyncNetwork.

  • GaladrielDevnet now only NetworkWithExplorer.
  • UnichainSepolia now only NetworkWithExplorer.

This affects HypersyncNetwork::iter() consumers (e.g., interactive selection). Confirm this removal is intentional.

Also applies to: 383-385


473-476: Threshold mappings for new networks look consistent; validate policy.

New networks mapped to DEFAULT_CONFIRMED_BLOCK_THRESHOLD. If any of these have known reorg profiles requiring 0 or custom thresholds, adjust accordingly.

Also applies to: 480-480, 519-519, 526-526


618-619: Tier assignments added; looks coherent with existing scheme.

Plasma → Bronze; ChainwebTestnet22/23/24, Damon, SentientTestnet → Stone. Confirm these align with product tier policy.

Also applies to: 622-630


119-126: Alphabetical ordering violation detected; verify scope and whether pre-existing or introduced by this PR.

The verification script found a constraint violation: the enum is not in alphabetical order by kebab-case—"Darwinia" appears before "Damon" (darwinia > damon alphabetically). The ChainwebTestnet22-24 additions themselves are correctly ordered and have unique IDs with no duplicates.

Determine whether this ordering issue is pre-existing or introduced by these changes. If introduced, all out-of-order variants need repositioning to maintain alphabetical ordering.

@DZakh DZakh enabled auto-merge (squash) October 21, 2025 16:28
@DZakh DZakh merged commit 130db80 into main Oct 21, 2025
2 checks passed
@DZakh DZakh deleted the dz/oct-20-bug-fixes branch October 21, 2025 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants