Skip to content

Move Fuel & SVM selection/routing into Rust; unify HyperSync source naming - #1462

Merged
DZakh merged 13 commits into
mainfrom
claude/fuel-svm-performance-6dta8l
Jul 22, 2026
Merged

Move Fuel & SVM selection/routing into Rust; unify HyperSync source naming#1462
DZakh merged 13 commits into
mainfrom
claude/fuel-svm-performance-6dta8l

Conversation

@DZakh

@DZakh DZakh commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

Brings the Fuel and Solana (SVM) HyperSync sources up to the same architecture the EVM source already uses (merged in #1446): the full per-(event, chain) registration set crosses the napi boundary once at client construction, and each query sends only {block range, registration indexes, addresses}. Selection-building, routing, discriminator/receipt dispatch, and (SVM) Borsh decoding now run in Rust instead of crossing the boundary per-item in JS. Also unifies the naming across all three ecosystems.

Because the EVM fan-out PR is already on main, this PR's diff is only the Fuel/SVM work plus the naming cleanup.

What changed

Fuel → Rust (packages/cli/src/fuel_hypersync_source/)

  • FuelHyperSyncClient::new(cfg, userAgent, registrations) takes registrations (index, contractName, kind, logId, isWildcard); a new selection.rs ports the receipt-selection grouping from the old getSelectionConfig and requests only the receipt columns the selection's kinds read.
  • get_event_items routes receipts by (receiptType | rb) + contract ownership, fans each out to every matching registration (wildcards + owning contract), flattens kind-specific columns onto items (normalising TransferOut's toAddress into to), and validates kind-required columns / block joins via the shared MissingFields protocol. LogData param decoding stays in JS against the contract ABI.

SVM → Rust (packages/cli/src/svm_hypersync_source/)

  • SvmHyperSyncClient::new absorbs the old programSchemas config: each registration carries its Borsh schema pieces (accounts/args/definedTypes), grouped per program in Rust.
  • get_event_items ports buildInstructionSelections (per-AND-group fan-out, deduplicated) and the block/transaction/log/tokenBalance field unions, then routes instructions by discriminator longest-first (d8→d4→d2→d1, program-wide fallback) with fan-out and per-registration re-application of account filters and the isInner constraint. Borsh decoding, instruction-scoped log attachment, and the failed-transaction (isCommitted) filter run inline.

Shared

  • EventRouter.res is deleted; routing now lives in Rust and its duplicate / wildcard-collision validation is inlined into HandlerRegister.finishRegistration (identical error messages).
  • The ReScript sources shrink to index-mapped item building.

Naming — unified to {Evm|Fuel|Svm}HyperSync{Client|Source}:

  • napi classes: EvmHypersyncClientEvmHyperSyncClient, SvmHypersyncClientSvmHyperSyncClient, HyperfuelClientFuelHyperSyncClient.
  • ReScript modules: HyperSyncSourceEvmHyperSyncSource, HyperFuelSourceFuelHyperSyncSource, HyperFuelClientFuelHyperSyncClient, HyperFuelFuelHyperSync.
  • Only code identifiers changed; the "HyperFuel"/"HyperSync" product-name strings in error messages, URLs, and source name fields are left as the real service names.

Behavior change

Fan-out routing (matches the EVM change already on main): a returned receipt/instruction now routes to every matching registration rather than one. If both a wildcard handler and a contract-specific handler are registered for the same signature, both now fire for a matching item from that contract. The common case (no overlapping registrations) is unchanged.

End-user impact

  • Performance: selection-building, routing, and decoding move off the napi boundary into Rust, and raw transactions/blocks stay in Rust (materialised field-by-field only when a handler reads them) — the same optimization EVM already got, raising Fuel/Solana indexing throughput.
  • No config or handler API changes; existing indexers need no edits.

Testing

  • Rust: cargo test --lib green (Fuel 21, SVM 21, EVM 30; 401 total). New unit tests cover grouping, scoping, longest-first routing, filter re-application, missing-field errors, isCommitted drops, and log attachment.
  • ReScript compiles across envio, test_codegen, fuel_test, svm_test (which enforces warnings.error = "+a", so no unused bindings).
  • Vitest: rewritten SvmHyperSyncSource_test, the Fuel suite, and the addon-boundary suites (BlockStore_test/TransactionStore_test/EventFilters_test, which exercise the renamed napi classes) all pass. CI runs the full suite.
  • Dead-code pass: the six Rust "never used" warnings are all #[napi] exports consumed from ReScript, i.e. live cross-boundary API — nothing to remove.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Myo6vsGNF9ACsAbBV6PXSk


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added registration-driven event item retrieval for Fuel and SVM sources (typed queries, routing, pagination, decoded params, optional logs, and block metadata).
    • Added event-item and height client APIs to support the new retrieval flow.
  • Breaking Changes

    • Renamed legacy HyperSync/HyperFuel client surfaces to consistently named HyperSync variants.
    • Removed legacy generic query and event-routing interfaces.
  • Bug Fixes

    • Improved handling for missing required fields and unauthorized access.
    • Fixed/normalized event data mapping (including Fuel TransferOut recipient mapping) and strengthened routing/selection validation.

claude added 8 commits July 17, 2026 10:04
Routing in the Rust DecoderCore previously picked a single registration per
log (owning contract, else wildcard fallback) and rejected duplicate
signatures per contract and multiple wildcards at construction. Now every
matching registration gets its own item: wildcards always match, contract-
bound registrations match iff the log's address is owned by the contract,
with no fallback tier. The log is decoded once and each registration's own
param names are applied.

Since one registration's broader selection can now fetch logs for a
narrower sibling, routing is scoped to the query's registration indexes
(threaded into process_response and the RPC page fetch) and re-applies each
registration's static topic filters and startBlock gate; ContractAddresses
markers stay match-any in Rust — the JS clientAddressFilter owns that
check. startBlock now crosses the napi boundary on the registration.

The RPC page dedup key gains the registration index so fan-out items
survive, and selection compression dedupes repeated topic0s and identical
filtered selections that multiple same-signature registrations produce.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WN93NjbyYQhuiNgdpjQFFu
A log can only route to registrations belonging to the query selection that
fetched it — registration_indexes scoping plus the re-applied static topic
filters already guarantee that, and the JS side's partitioning by start
block keeps a query from ranging below its registrations' start blocks. The
per-log startBlock check was redundant, so the field no longer crosses the
napi boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WN93NjbyYQhuiNgdpjQFFu
Registrations sharing (topic0, topic count) were forced onto one positional
decoder, so a collision with a different indexed/body split was rejected at
construction. Now each distinct layout in the group gets its own decoder
(same-layout variants still share one) and a log decodes lazily, once per
matched layout, under each registration's own declaration.

A layout the log's bytes don't decode under contributes no items; the
failure only surfaces as an error when no matched layout decodes, so
genuinely malformed data still can't disappear silently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WN93NjbyYQhuiNgdpjQFFu
The decoder's MetaKey lookup table (signature map, per-contract variant
index, wildcard index, per-query active set) existed to answer one
question per log: which of the query's registrations match it. Answer it
literally instead: DecoderCore now just holds all registrations by index,
a query resolves its selection once into a SelectionDecoder, and routing
is a straight scan over that selection — match on signature, emitter
(wildcard or owning contract), and static topic filters, then decode
under the matching registration's own declaration.

This removes the MetaKey/RegisteredEvent/EventVariant structure, the
decoder-sharing bookkeeping (each registration owns its decoder), and
the per-log HashSet membership checks. A selection is one partition's
events, small enough that the scan is also cheaper than the hashed
lookups it replaces.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WN93NjbyYQhuiNgdpjQFFu
OnEventRegistration (the napi input DTO) becomes OnEventRegistrationInput,
freeing the name for the decoder's parsed form (was EventRegistration) —
matching the JS side's onEventRegistration naming for the same concept.

The static topic filter DNF moves behind a TopicFilters type owning its
parse and matches, so the registration struct and matching logic no longer
carry the raw nested shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WN93NjbyYQhuiNgdpjQFFu
Match the EVM client's shape on both remaining HyperSync sources: the whole
per-(event, chain) registration set crosses the napi boundary once at client
construction, and a query names only its block range, registration indexes,
and current addresses.

Fuel:
- HyperfuelClient::new takes registrations (index, contractName, kind,
  logId, isWildcard); a new selection builder ports the receipt-selection
  grouping from HyperFuelSource.getSelectionConfig, requesting only the
  receipt columns the selection's kinds read.
- get_event_items routes receipts by (rb | receiptType) with fan-out to
  every matching registration (wildcards plus the owning contract), flattens
  kind-specific columns onto items (normalising TransferOut's toAddress into
  to), validates kind-required columns and the block join via the
  MissingFields protocol, and drops receipts that route nowhere. LogData
  param decoding stays in JS against the contract ABI.

SVM:
- SvmHypersyncClient::new takes registrations carrying discriminator,
  account filters, isInner, includeLogs, field selections, and the Borsh
  schema pieces — absorbing the programSchemas config option.
- get_event_items ports buildInstructionSelections (per-AND-group fan-out,
  deduplicated) and the per-selection block/transaction/log/tokenBalance
  field unions, then routes instructions by discriminator longest-first
  (d8/d4/d2/d1, program-wide fallback) with fan-out and per-registration
  re-application of account filters and the isInner constraint. Borsh
  decoding and instruction-scoped log attachment happen inline.

Both sources shrink to index-mapped item building, EventRouter.res is
deleted, and its duplicate/wildcard-collision validation is inlined into
HandlerRegister.finishRegistration. The JS selection/routing tests move to
Rust unit tests alongside the new builders.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Myo6vsGNF9ACsAbBV6PXSk
…rmance-6dta8l

# Conflicts:
#	packages/cli/src/evm_hypersync_source/decode.rs
#	packages/cli/src/evm_hypersync_source/mod.rs
#	packages/cli/src/evm_rpc_source/mod.rs
…aming

Unify the naming across all three ecosystems to `{Evm|Fuel|Svm}HyperSync{Client|Source}`,
fixing the casing drift left after the Fuel/SVM port:

- napi client classes: EvmHypersyncClient -> EvmHyperSyncClient,
  SvmHypersyncClient -> SvmHyperSyncClient, HyperfuelClient -> FuelHyperSyncClient
  (Rust structs + Core.res @as bindings/field/ctor names + test mock keys).
- ReScript modules: HyperSyncSource -> EvmHyperSyncSource,
  HyperFuelSource -> FuelHyperSyncSource, HyperFuelClient -> FuelHyperSyncClient,
  HyperFuel -> FuelHyperSync (with .resi).

Only code identifiers are renamed; the "HyperFuel"/"HyperSync" product-name strings
in error messages, URLs, and source `name` fields are left as the real service names.

Dead-code pass: the six Rust "never used" warnings (evm/svm block+transaction
field-name fns, encode_indexed_topic, the shared field_names helper) are all
`#[napi]` exports consumed from ReScript (drift-protection tests, EventConfigBuilder),
so they are live cross-boundary API, not dead code — left in place. ReScript's
`warnings.error = "+a"` makes the compiler reject any unused binding, so the
ReScript side is dead-code-free by construction. No removals were warranted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Myo6vsGNF9ACsAbBV6PXSk
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 09c17cae-81c4-4cda-97d3-df9d3e9de5db

📥 Commits

Reviewing files that changed from the base of the PR and between 8b1c124 and 4f6a790.

📒 Files selected for processing (4)
  • packages/cli/src/evm_hypersync_source/decode.rs
  • packages/cli/src/evm_hypersync_source/mod.rs
  • packages/cli/src/fuel_hypersync_source/mod.rs
  • packages/envio-tests/test/HandlerRegisterValidation_test.res
💤 Files with no reviewable changes (1)
  • packages/envio-tests/test/HandlerRegisterValidation_test.res
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/cli/src/evm_hypersync_source/mod.rs
  • packages/cli/src/fuel_hypersync_source/mod.rs

📝 Walkthrough

Walkthrough

HyperSync event retrieval is migrated to registration-driven routing across EVM, SVM, and Fuel. New client contracts, Rust-side selection and routing, event-item APIs, source integrations, schema handling, and registration validation replace older query and EventRouter paths.

Changes

HyperSync routing migration

Layer / File(s) Summary
Client contracts and selection inputs
packages/cli/src/*_hypersync_source/*, packages/envio/src/sources/{SvmHyperSyncClient.res, HyperSyncClient.res}
Fuel and SVM registration inputs, event-item response types, receipt/instruction selections, schema inputs, and internal response representations are introduced or updated.
Fuel event-item pipeline
packages/cli/src/fuel_hypersync_source/*, packages/envio/src/sources/FuelHyperSync*.res*, packages/envio/src/ChainState.res
Fuel receipts are selected, queried, converted, routed, flattened into event items, and consumed by the new Fuel source with pagination and error handling.
SVM event-item pipeline
packages/cli/src/svm_hypersync_source/*, packages/envio/src/sources/SvmHyperSync*.res, packages/envio-tests/test/SvmHyperSyncSource_test.res
SVM registrations drive schema construction, query selection, Rust-side routing, optional decoding and log attachment, store materialization, and source event construction.
Source wiring and client naming
packages/envio/src/{Core.res,sources/*}, packages/envio-tests/test/HyperSyncClient_test.res, scenarios/*
EVM, SVM, and Fuel client names and factories are updated, authorization handling is wired into sources, and related tests use the renamed source APIs.
Registration validation cleanup
packages/envio/src/HandlerRegister*, packages/envio-tests/test/HandlerRegisterValidation_test.res, packages/envio/src/{FetchState.res,Internal.res}
Per-chain event ID and wildcard collision validation replaces EventRouter-based registration validation, with supporting comments and tests updated.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EventRegistration
  participant HyperSyncSource
  participant HyperSyncClient
  participant RustRouter
  participant HyperSyncAPI
  EventRegistration->>HyperSyncSource: configure registrations
  HyperSyncSource->>HyperSyncClient: request event items
  HyperSyncClient->>RustRouter: build selections and route results
  RustRouter->>HyperSyncAPI: execute chain query
  HyperSyncAPI-->>RustRouter: raw chain data
  RustRouter-->>HyperSyncClient: routed event items
  HyperSyncClient-->>HyperSyncSource: paginated response
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: moving Fuel/SVM selection and routing into Rust and renaming HyperSync sources consistently.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

…rmance-6dta8l

# Conflicts:
#	packages/envio-tests/test/EventRouter_svm_test.res

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a097ab335f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/envio/src/HandlerRegister.res Outdated
Comment on lines +653 to +654
validateEventIdOrThrow(
~eventId=eventConfig.id,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include program id when validating SVM wildcard handlers

For SVM configs, eventConfig.id is only the discriminator value (or "none" for program-wide handlers), not the program id. This means two wildcard SVM handlers on different programs with the same discriminator/program-wide id are rejected here as a wildcard collision even though the old router keyed SVM dispatch by (programId, discriminator) and the new Rust routing still scopes matches by program_id. Include the program id in the validation key for SVM so valid multi-program wildcard configs can start.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/envio/src/Internal.res (1)

182-182: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove caller-specific wording from this comment.

Keep the nullable-schema invariant; RpcSource and EvmHyperSyncSource are implementation consumers, not useful durable context.

Proposed fix
-// Static sets of nullable field names — used by RpcSource and EvmHyperSyncSource to wrap schemas with S.nullable
+// Static sets of field names whose source schemas must allow null.

As per coding guidelines, don't write comments that state “which callers use a value.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/Internal.res` at line 182, Update the comment above the
nullable field-name sets to describe only the invariant that these static sets
identify nullable fields used to wrap schemas with S.nullable. Remove the
caller-specific references to RpcSource and EvmHyperSyncSource.

Source: Coding guidelines

packages/envio/src/sources/FuelHyperSync.res (1)

71-71: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Address the archiveHeight default TODO. Defaulting res.archiveHeight to 0 when the source omits it flows into FuelHyperSyncSource (Line 105) as knownHeight, so a genuine None from HyperFuel would report a chain height of 0 rather than surfacing the missing value. Consider treating an absent archiveHeight as an explicit error or retry rather than silently defaulting.

Want me to open an issue to track removing this default and defining the absent-archiveHeight behavior?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/sources/FuelHyperSync.res` at line 71, Remove the
`Option.getOr(0)` fallback when constructing `archiveHeight` in the source
response flow, and define explicit handling for an absent value—either return an
error or trigger the established retry path. Ensure `FuelHyperSyncSource` does
not receive `knownHeight` as `0` when HyperFuel omits `archiveHeight`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/envio/src/HandlerRegister.res`:
- Around line 626-629: In the wildcard event validation error within the
HandlerRegister logic, correct the message text from “interfer” to “interfere”
while preserving the existing error construction and context.

In `@packages/envio/src/sources/SvmHyperSyncSource.res`:
- Around line 17-20: Update synthLogIndex so its per-transaction stride exceeds
every possible addrSum, preventing nested instruction paths from colliding with
later transactions; retain the existing addrSum encoding while correcting the
outer-only mapping so [i] produces transactionIndex times the stride plus i + 1.

---

Nitpick comments:
In `@packages/envio/src/Internal.res`:
- Line 182: Update the comment above the nullable field-name sets to describe
only the invariant that these static sets identify nullable fields used to wrap
schemas with S.nullable. Remove the caller-specific references to RpcSource and
EvmHyperSyncSource.

In `@packages/envio/src/sources/FuelHyperSync.res`:
- Line 71: Remove the `Option.getOr(0)` fallback when constructing
`archiveHeight` in the source response flow, and define explicit handling for an
absent value—either return an error or trigger the established retry path.
Ensure `FuelHyperSyncSource` does not receive `knownHeight` as `0` when
HyperFuel omits `archiveHeight`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7b72d0f1-d548-4b01-994a-59606ec79cd0

📥 Commits

Reviewing files that changed from the base of the PR and between b344321 and 5fe48ca.

📒 Files selected for processing (38)
  • packages/cli/src/evm_hypersync_source/mod.rs
  • packages/cli/src/fuel_hypersync_source/mod.rs
  • packages/cli/src/fuel_hypersync_source/query.rs
  • packages/cli/src/fuel_hypersync_source/selection.rs
  • packages/cli/src/fuel_hypersync_source/types.rs
  • packages/cli/src/svm_hypersync_source/borsh_decoder.rs
  • packages/cli/src/svm_hypersync_source/config.rs
  • packages/cli/src/svm_hypersync_source/mod.rs
  • packages/cli/src/svm_hypersync_source/selection.rs
  • packages/cli/src/svm_hypersync_source/types.rs
  • packages/envio-tests/test/EventRouter_svm_test.res
  • packages/envio-tests/test/HyperSyncClient_test.res
  • packages/envio-tests/test/SvmHyperSyncSource_test.res
  • packages/envio/src/ChainState.res
  • packages/envio/src/Core.res
  • packages/envio/src/FetchState.res
  • packages/envio/src/HandlerRegister.res
  • packages/envio/src/Internal.res
  • packages/envio/src/sources/EventRouter.res
  • packages/envio/src/sources/EvmChain.res
  • packages/envio/src/sources/EvmHyperSyncSource.res
  • packages/envio/src/sources/FuelHyperSync.res
  • packages/envio/src/sources/FuelHyperSync.resi
  • packages/envio/src/sources/FuelHyperSyncClient.res
  • packages/envio/src/sources/FuelHyperSyncSource.res
  • packages/envio/src/sources/HyperFuel.res
  • packages/envio/src/sources/HyperFuel.resi
  • packages/envio/src/sources/HyperFuelClient.res
  • packages/envio/src/sources/HyperFuelSource.res
  • packages/envio/src/sources/HyperSyncClient.res
  • packages/envio/src/sources/SvmHyperSyncClient.res
  • packages/envio/src/sources/SvmHyperSyncSource.res
  • scenarios/fuel_test/test/HyperFuelHeight_test.res
  • scenarios/fuel_test/test/HyperFuelSource_test.res
  • scenarios/svm_flow_xray/src/indexer.test.ts
  • scenarios/svm_metaplex_demo/src/indexer.test.ts
  • scenarios/test_codegen/test/SourceBlockHashes_test.res
  • scenarios/test_codegen/test/lib_tests/EventRouter_test.res
💤 Files with no reviewable changes (10)
  • packages/envio/src/sources/HyperFuel.resi
  • scenarios/test_codegen/test/lib_tests/EventRouter_test.res
  • scenarios/fuel_test/test/HyperFuelSource_test.res
  • packages/envio-tests/test/EventRouter_svm_test.res
  • packages/cli/src/svm_hypersync_source/config.rs
  • packages/cli/src/fuel_hypersync_source/query.rs
  • packages/envio/src/sources/EventRouter.res
  • packages/envio/src/sources/HyperFuelSource.res
  • packages/envio/src/sources/HyperFuelClient.res
  • packages/envio/src/sources/HyperFuel.res

Comment thread packages/envio/src/HandlerRegister.res Outdated
Comment on lines +17 to 20
let synthLogIndex = (~transactionIndex, ~instructionAddress) => {
let addrSum = instructionAddress->Array.reduce(0, (acc, n) => acc * 1024 + n + 1)
transactionIndex * 65536 + addrSum
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how logIndex participates in FetchState ordering/dedup.
rg -nP -C4 '\blogIndex\b' packages/envio/src/FetchState.res

Repository: enviodev/hyperindex

Length of output: 2153


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- SvmHyperSyncSource.res around synthLogIndex ---'
sed -n '1,80p' packages/envio/src/sources/SvmHyperSyncSource.res

echo
echo '--- FetchState ordering around compareBufferItem ---'
sed -n '690,740p' packages/envio/src/FetchState.res

echo
echo '--- FetchState block item logIndex generation ---'
sed -n '820,870p' packages/envio/src/FetchState.res

Repository: enviodev/hyperindex

Length of output: 6859


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
def synth(tx, instr):
    addr_sum = 0
    for n in instr:
        addr_sum = addr_sum * 1024 + n + 1
    return tx * 65536 + addr_sum

samples = [
    (0, [0]),
    (0, [63]),
    (0, [63,0]),
    (0, [5,10]),
    (1, [0]),
]
for tx, instr in samples:
    print(tx, instr, synth(tx, instr))

# Explore the max addrSum for bounded values length <= 10 and each n <= 1000.
# This is just a growth probe, not an exhaustive search over 1001^10.
addr_sum = 0
for i in range(10):
    addr_sum = addr_sum * 1024 + 1000 + 1
    print(i+1, addr_sum)
PY

Repository: enviodev/hyperindex

Length of output: 424


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -nP -C3 '\bblockNumberAndLogIndex\b|\bcompareBufferItem\b|same log routed to the same registration|logIndex:' packages/envio/src/FetchState.res

Repository: enviodev/hyperindex

Length of output: 2365


synthLogIndex needs a wider per-transaction stride. addrSum can exceed 65536 for nested paths, so distinct instructions can collide on logIndex across transactions (for example, [63,0] and the next transaction’s [0] both land on 65537). FetchState treats equal (blockNumber, logIndex, registration index) as a duplicate, so that can silently drop an event. The outer-only example is also off: a single [i] maps to tx * 65536 + i + 1.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/sources/SvmHyperSyncSource.res` around lines 17 - 20,
Update synthLogIndex so its per-transaction stride exceeds every possible
addrSum, preventing nested instruction paths from colliding with later
transactions; retain the existing addrSum encoding while correcting the
outer-only mapping so [i] produces transactionIndex times the stride plus i + 1.

- Scope the SVM wildcard/duplicate validation key by (programId, discriminator)
  instead of the discriminator alone. SVM eventConfig.id is only the
  discriminator (or "none"), so two wildcard handlers on different programs
  sharing a discriminator were falsely rejected as a wildcard collision even
  though Rust routing scopes matches by program_id (Codex P2).
- Drop the caller list from the nullable-field-names comment in Internal.res.
- Fix "interfer" -> "interfere" in the wildcard-collision error message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Myo6vsGNF9ACsAbBV6PXSk
- Fuel: replace `Registration.kind: FuelEventKind` + `rb: Option<u64>` with
  an internal `RegistrationKind` enum whose `LogData` variant carries `rb`,
  making the invalid states unrepresentable and dropping the `unwrap_or_default`.
- Guard integer overflow on the inclusive→exclusive `to_block`/`to_slot`
  conversion (checked_add), and make SVM `hex_to_bytes` slice on char
  boundaries so a malformed discriminator returns an error instead of panicking.
- Extract the per-chain dispatch-id validation out of `finishRegistration`
  into `makeEventIdValidator`/`validateEventIdOrThrow` and restore unit tests
  for duplicate-event and wildcard-collision detection (replacing the deleted
  EventRouter tests), including program-scoped SVM ids.
- Restore `tx_status = [1]` coverage in the Fuel selection tests and add a
  focused test asserting every built selection filters to successful txs.
- Rename the stale HyperFuelHeight test file to FuelHyperSyncSourceHeight.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E887S2x7EfB1hcVvvVLVz3

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/envio-tests/test/HandlerRegisterValidation_test.res`:
- Line 7: Remove the refactor-history comment near the top of the test file,
while preserving the existing comments on lines 3–6 that document the collision
risk.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ccc427ff-2914-4e4b-a183-8213e43cf5c0

📥 Commits

Reviewing files that changed from the base of the PR and between e40f49e and 8b1c124.

📒 Files selected for processing (7)
  • packages/cli/src/fuel_hypersync_source/mod.rs
  • packages/cli/src/fuel_hypersync_source/selection.rs
  • packages/cli/src/svm_hypersync_source/mod.rs
  • packages/envio-tests/test/HandlerRegisterValidation_test.res
  • packages/envio/src/HandlerRegister.res
  • packages/envio/src/HandlerRegister.resi
  • scenarios/fuel_test/test/FuelHyperSyncSourceHeight_test.res
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/cli/src/fuel_hypersync_source/selection.rs
  • packages/envio/src/HandlerRegister.res
  • packages/cli/src/fuel_hypersync_source/mod.rs
  • packages/cli/src/svm_hypersync_source/mod.rs

Comment thread packages/envio-tests/test/HandlerRegisterValidation_test.res Outdated
- fuel_hypersync_source/mod.rs: FuelEventKind is only used by the #[cfg(test)]
  module now that non-test routing uses RegistrationKind, so move it out of the
  crate-level import into the test module. Fixes `cargo clippy -- -D warnings`.
- HandlerRegisterValidation_test.res: drop the refactor-history line per the
  no-narrate-the-refactor guideline (keep the collision-risk rationale).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Myo6vsGNF9ACsAbBV6PXSk
@DZakh
DZakh merged commit f43e543 into main Jul 22, 2026
8 checks passed
@DZakh
DZakh deleted the claude/fuel-svm-performance-6dta8l branch July 22, 2026 11:38

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f6a79053d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +343 to +345
let mut selection = BuiltInstructionSelection {
program_id: reg.program_id.clone(),
discriminator_hex: reg.discriminator_hex.clone(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Scope SVM selections to the current partition

When an SVM chain has multiple non-wildcard programs split across FetchState partitions, normalSelection still contains all address-dependent registrations, but each partition's addressesByContractName only contains that partition's programs. This builder emits an InstructionSelection for every selected registration from its static program_id, so every partition fetches every normal SVM program and then drops the rows whose program is not in that partition during routing; on noisy programs this can burn the maxNumInstructions page budget on unusable instructions and make the intended partition crawl or starve. Pass the partition address map into the SVM selection builder and skip non-wildcard registrations whose contract has no address in the current partition, as the Fuel builder does.

Useful? React with 👍 / 👎.

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.

2 participants