Repository: ponder-sh/ponder
Pull request #1403 (2025-01-06 16:49:12)

fix: reorg causing events to be skipped


Patch:
diff --git a/.changeset/dry-hornets-exist.md b/.changeset/dry-hornets-exist.md
new file mode 100644
index 00000000..26fa5fe1
--- /dev/null
+++ b/.changeset/dry-hornets-exist.md
@@ -0,0 +1,5 @@
+---
+"ponder": patch
+---
+
+Fixed a bug where a reorg sometimes caused events to be skipped. This does not affect the rpc cache.
diff --git a/.changeset/itchy-eels-double.md b/.changeset/itchy-eels-double.md
deleted file mode 100644
index f74688c3..00000000
--- a/.changeset/itchy-eels-double.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"ponder": patch
----
-
-Improved the granularity of rpc cache.
diff --git a/packages/core/src/bin/utils/run.ts b/packages/core/src/bin/utils/run.ts
index b0e9a6ba..8f187a3c 100644
--- a/packages/core/src/bin/utils/run.ts
+++ b/packages/core/src/bin/utils/run.ts
@@ -87,7 +87,7 @@ export async function run({
         case "block": {
           // Events must be run block-by-block, so that `database.complete` can accurately
           // update the temporary `checkpoint` value set in the trigger.
-          for (const events of splitEvents(event.events)) {
+          for (const { checkpoint, events } of splitEvents(event.events)) {
             const result = await handleEvents(
               decodeEvents(common, indexingBuild.sources, events),
               event.checkpoint,
@@ -96,7 +96,7 @@ export async function run({
             if (result.status === "error") onReloadableError(result.error);
 
             // Set reorg table `checkpoint` column for newly inserted rows.
-            await database.complete({ checkpoint: event.checkpoint });
+            await database.complete({ checkpoint });
           }
 
           await metadataStore.setStatus(event.status);
diff --git a/packages/core/src/sync-historical/index.ts b/packages/core/src/sync-historical/index.ts
index 7bdabb62..38111b52 100644
--- a/packages/core/src/sync-historical/index.ts
+++ b/packages/core/src/sync-historical/index.ts
@@ -6,12 +6,10 @@ import {
   isTransferFilterMatched,
 } from "@/sync-realtime/filter.js";
 import type { SyncStore } from "@/sync-store/index.js";
-import { type Fragment, recoverFilter } from "@/sync/fragments.js";
 import {
   type BlockFilter,
   type Factory,
   type Filter,
-  type FilterWithoutBlocks,
   type LogFactory,
   type LogFilter,
   type TraceFilter,
@@ -29,10 +27,10 @@ import type {
 import {
   type Interval,
   getChunks,
-  intervalBounds,
   intervalDifference,
   intervalRange,
 } from "@/utils/interval.js";
+import { never } from "@/utils/never.js";
 import type { RequestQueue } from "@/utils/requestQueue.js";
 import {
   _debug_traceBlockByNumber,
@@ -52,7 +50,7 @@ import {
 } from "viem";
 
 export type HistoricalSync = {
-  intervalsCache: Map<Filter, { fragment: Fragment; intervals: Interval[] }[]>;
+  intervalsCache: Map<Filter, Interval[]>;
   /**
    * Extract raw data for `interval` and return the closest-to-tip block
    * that is synced.
@@ -125,10 +123,7 @@ export const createHistoricalSync = async (
    *
    * Note: `intervalsCache` is not updated after a new interval is synced.
    */
-  let intervalsCache: Map<
-    Filter,
-    { fragment: Fragment; intervals: Interval[] }[]
-  >;
+  let intervalsCache: Map<Filter, Interval[]>;
   if (args.network.disableCache) {
     intervalsCache = new Map();
     for (const { filter } of args.sources) {
@@ -724,102 +719,71 @@ export const createHistoricalSync = async (
   return {
     intervalsCache,
     async sync(_interval) {
-      const syncedIntervals: {
-        interval: Interval;
-        filter: FilterWithoutBlocks;
-      }[] = [];
+      const syncedIntervals: { filter: Filter; interval: Interval }[] = [];
 
-      // Determine the requests that need to be made, and which intervals need to be inserted.
-      // Fragments are used to create a minimal filter, to avoid refetching data even if a filter
-      // is only partially synced.
-
-      for (const { filter } of args.sources) {
-        if (
-          (filter.fromBlock !== undefined && filter.fromBlock > _interval[1]) ||
-          (filter.toBlock !== undefined && filter.toBlock < _interval[0])
-        ) {
-          continue;
-        }
-
-        const interval: Interval = [
-          Math.max(filter.fromBlock ?? 0, _interval[0]),
-          Math.min(filter.toBlock ?? Number.POSITIVE_INFINITY, _interval[1]),
-        ];
-
-        const completedIntervals = intervalsCache.get(filter)!;
-        const requiredIntervals: {
-          fragment: Fragment;
-          intervals: Interval[];
-        }[] = [];
-
-        for (const {
-          fragment,
-          intervals: fragmentIntervals,
-        } of completedIntervals) {
-          const requiredFragmentIntervals = intervalDifference(
-            [interval],
-            fragmentIntervals,
-          );
-
-          if (requiredFragmentIntervals.length > 0) {
-            requiredIntervals.push({
-              fragment,
-              intervals: requiredFragmentIntervals,
-            });
+      await Promise.all(
+        args.sources.map(async (source) => {
+          const filter = source.filter;
+
+          // Compute the required interval to sync, accounting for cached
+          // intervals and start + end block.
+
+          // Skip sync if the interval is after the `toBlock` or before
+          // the `fromBlock`.
+          if (
+            (filter.fromBlock !== undefined &&
+              filter.fromBlock > _interval[1]) ||
+            (filter.toBlock !== undefined && filter.toBlock < _interval[0])
+          ) {
+            return;
           }
-        }
-
-        if (requiredIntervals.length > 0) {
-          const requiredInterval = intervalBounds(
-            requiredIntervals.flatMap(({ intervals }) => intervals),
-          );
-
-          const requiredFilter = recoverFilter(
-            filter,
-            requiredIntervals.map(({ fragment }) => fragment),
+          const interval: Interval = [
+            Math.max(filter.fromBlock ?? 0, _interval[0]),
+            Math.min(filter.toBlock ?? Number.POSITIVE_INFINITY, _interval[1]),
+          ];
+          const completedIntervals = intervalsCache.get(filter)!;
+          const requiredIntervals = intervalDifference(
+            [interval],
+            completedIntervals,
           );
 
-          syncedIntervals.push({
-            filter: requiredFilter,
-            interval: requiredInterval,
-          });
-        }
-      }
+          // Skip sync if the interval is already complete.
+          if (requiredIntervals.length === 0) return;
 
-      await Promise.all(
-        syncedIntervals.map(async ({ filter, interval }) => {
           // Request last block of interval
           const blockPromise = syncBlock(interval[1]);
 
           try {
-            switch (filter.type) {
-              case "log": {
-                await syncLogFilter(filter as LogFilter, interval);
-                break;
-              }
-
-              case "block": {
-                await syncBlockFilter(filter as BlockFilter, interval);
-                break;
-              }
-
-              case "transaction": {
-                await syncTransactionFilter(
-                  filter as TransactionFilter,
-                  interval,
-                );
-                break;
-              }
-
-              case "trace":
-              case "transfer": {
-                await syncTraceOrTransferFilter(
-                  filter as TraceFilter | TransferFilter,
-                  interval,
-                );
-                break;
-              }
-            }
+            // sync required intervals, account for chunk sizes
+            await Promise.all(
+              requiredIntervals.map(async (interval) => {
+                switch (filter.type) {
+                  case "log": {
+                    await syncLogFilter(filter, interval);
+                    break;
+                  }
+
+                  case "block": {
+                    await syncBlockFilter(filter, interval);
+                    break;
+                  }
+
+                  case "transaction": {
+                    await syncTransactionFilter(filter, interval);
+                    break;
+                  }
+
+                  case "trace":
+                  case "transfer": {
+                    await syncTraceOrTransferFilter(filter, interval);
+                    break;
+                  }
+
+                  default:
+                    never(filter);
+                }
+              }),
+            );
           } catch (_error) {
             const error = _error as Error;
 
@@ -837,11 +801,11 @@ export const createHistoricalSync = async (
           if (isKilled) return;
 
           await blockPromise;
+
+          syncedIntervals.push({ filter, interval });
         }),
       );
 
-      if (isKilled) return;
-
       const blocks = await Promise.all(blockCache.values());
 
       await Promise.all([
@@ -859,8 +823,6 @@ export const createHistoricalSync = async (
         }),
       ]);
 
-      if (isKilled) return;
-
       // Add corresponding intervals to the sync-store
       // Note: this should happen after so the database doesn't become corrupted
       if (args.network.disableCache === false) {
diff --git a/packages/core/src/sync-store/index.ts b/packages/core/src/sync-store/index.ts
index 2670c1fe..7485e670 100644
--- a/packages/core/src/sync-store/index.ts
+++ b/packages/core/src/sync-store/index.ts
@@ -1,17 +1,11 @@
 import type { Common } from "@/common/common.js";
 import type { HeadlessKysely } from "@/database/kysely.js";
 import type { RawEvent } from "@/sync/events.js";
-import {
-  type Fragment,
-  type FragmentId,
-  fragmentToId,
-  getFragments,
-} from "@/sync/fragments.js";
+import { type FragmentId, getFragmentIds } from "@/sync/fragments.js";
 import {
   type BlockFilter,
   type Factory,
   type Filter,
-  type FilterWithoutBlocks,
   type LogFactory,
   type LogFilter,
   type TraceFilter,
@@ -30,7 +24,7 @@ import type {
   SyncTransactionReceipt,
 } from "@/types/sync.js";
 import type { NonNull } from "@/types/utils.js";
-import type { Interval } from "@/utils/interval.js";
+import { type Interval, intervalIntersectionMany } from "@/utils/interval.js";
 import { type Kysely, type SelectQueryBuilder, sql as ksql } from "kysely";
 import type { InsertObject } from "kysely";
 import {
@@ -53,14 +47,14 @@ import {
 export type SyncStore = {
   insertIntervals(args: {
     intervals: {
-      filter: FilterWithoutBlocks;
+      filter: Filter;
       interval: Interval;
     }[];
     chainId: number;
   }): Promise<void>;
   getIntervals(args: {
     filters: Filter[];
-  }): Promise<Map<Filter, { fragment: Fragment; intervals: Interval[] }[]>>;
+  }): Promise<Map<Filter, Interval[]>>;
   getChildAddresses(args: {
     filter: Factory;
     limit?: number;
@@ -174,13 +168,12 @@ export const createSyncStore = ({
       // dedupe and merge matching fragments
 
       for (const { filter, interval } of intervals) {
-        for (const fragment of getFragments(filter)) {
-          const fragmentId = fragmentToId(fragment.fragment);
-          if (perFragmentIntervals.has(fragmentId) === false) {
-            perFragmentIntervals.set(fragmentId, []);
+        for (const fragment of getFragmentIds(filter)) {
+          if (perFragmentIntervals.has(fragment.id) === false) {
+            perFragmentIntervals.set(fragment.id, []);
           }
 
-          perFragmentIntervals.get(fragmentId)!.push(interval);
+          perFragmentIntervals.get(fragment.id)!.push(interval);
         }
       }
 
@@ -220,27 +213,25 @@ export const createSyncStore = ({
         | SelectQueryBuilder<
             PonderSyncSchema,
             "intervals",
-            { merged_blocks: string | null; filter: string; fragment: string }
+            { merged_blocks: string | null; filter: string }
           >
         | undefined;
 
       for (let i = 0; i < filters.length; i++) {
         const filter = filters[i]!;
-        const fragments = getFragments(filter);
-        for (let j = 0; j < fragments.length; j++) {
-          const fragment = fragments[j]!;
+        const fragments = getFragmentIds(filter);
+        for (const fragment of fragments) {
           const _query = db
             .selectFrom(
               db
                 .selectFrom("intervals")
                 .select(ksql`unnest(blocks)`.as("blocks"))
-                .where("fragment_id", "in", fragment.adjacentIds)
+                .where("fragment_id", "in", fragment.adjacent)
                 .as("unnested"),
             )
             .select([
               ksql<string>`range_agg(unnested.blocks)`.as("merged_blocks"),
               ksql.raw(`'${i}'`).as("filter"),
-              ksql.raw(`'${j}'`).as("fragment"),
             ]);
           // @ts-ignore
           query = query === undefined ? _query : query.unionAll(_query);
@@ -249,34 +240,28 @@ export const createSyncStore = ({
 
       const rows = await query!.execute();
 
-      const result = new Map<
-        Filter,
-        { fragment: Fragment; intervals: Interval[] }[]
-      >();
+      const result: Map<Filter, Interval[]> = new Map();
+
+      // intervals use "union" for the same fragment, and
+      // "intersection" for the same filter
 
       // NOTE: `interval[1]` must be rounded down in order to offset the previous
       // rounding.
 
       for (let i = 0; i < filters.length; i++) {
         const filter = filters[i]!;
-        const fragments = getFragments(filter);
-        result.set(filter, []);
-        for (let j = 0; j < fragments.length; j++) {
-          const fragment = fragments[j]!;
-          const intervals = rows
-            .filter((row) => row.filter === `${i}`)
-            .filter((row) => row.fragment === `${j}`)
-            .map((row) =>
-              (row.merged_blocks
-                ? (JSON.parse(
-                    `[${row.merged_blocks.slice(1, -1)}]`,
-                  ) as Interval[])
-                : []
-              ).map((interval) => [interval[0], interval[1] - 1] as Interval),
-            )[0]!;
+        const intervals = rows
+          .filter((row) => row.filter === `${i}`)
+          .map((row) =>
+            (row.merged_blocks
+              ? (JSON.parse(
+                  `[${row.merged_blocks.slice(1, -1)}]`,
+                ) as Interval[])
+              : []
+            ).map((interval) => [interval[0], interval[1] - 1] as Interval),
+          );
 
-          result.get(filter)!.push({ fragment: fragment.fragment, intervals });
-        }
+        result.set(filter, intervalIntersectionMany(intervals));
       }
 
       return result;
diff --git a/packages/core/src/sync/fragments.ts b/packages/core/src/sync/fragments.ts
index 4c051e2e..953a6ac9 100644
--- a/packages/core/src/sync/fragments.ts
+++ b/packages/core/src/sync/fragments.ts
@@ -1,10 +1,8 @@
-import { dedupe } from "@ponder/common";
 import type { Address, Hex } from "viem";
 import {
   type BlockFilter,
   type Factory,
-  type FilterAddress,
-  type FilterWithoutBlocks,
+  type Filter,
   type LogFilter,
   type TraceFilter,
   type TransactionFilter,
@@ -14,127 +12,66 @@ import {
 } from "./source.js";
 
 type FragmentAddress =
-  | Address
-  | {
-      address: Address;
-      eventSelector: Factory["eventSelector"];
-      childAddressLocation: Factory["childAddressLocation"];
-    }
-  | null;
-
-type FragmentAddressId =
   | Address
   | `${Address}_${Factory["eventSelector"]}_${Factory["childAddressLocation"]}`
   | null;
 type FragmentTopic = Hex | null;
 
-export type Fragment =
-  | {
-      type: "block";
-      chainId: number;
-      interval: number;
-      offset: number;
-    }
-  | {
-      type: "transaction";
-      chainId: number;
-      fromAddress: FragmentAddress;
-      toAddress: FragmentAddress;
-    }
-  | {
-      type: "trace";
-      chainId: number;
-      fromAddress: FragmentAddress;
-      toAddress: FragmentAddress;
-      functionSelector: Hex | null;
-      includeTransactionReceipts: boolean;
-    }
-  | {
-      type: "log";
-      chainId: number;
-      address: FragmentAddress;
-      topic0: FragmentTopic;
-      topic1: FragmentTopic;
-      topic2: FragmentTopic;
-      topic3: FragmentTopic;
-      includeTransactionReceipts: boolean;
-    }
-  | {
-      type: "transfer";
-      chainId: number;
-      fromAddress: FragmentAddress;
-      toAddress: FragmentAddress;
-      includeTransactionReceipts: boolean;
-    };
-
 export type FragmentId =
   /** block_{chainId}_{interval}_{offset} */
   | `block_${number}_${number}_${number}`
   /** transaction_{chainId}_{fromAddress}_{toAddress} */
-  | `transaction_${number}_${FragmentAddressId}_${FragmentAddressId}`
+  | `transaction_${number}_${FragmentAddress}_${FragmentAddress}`
   /** trace_{chainId}_{fromAddress}_{toAddress}_{functionSelector}_{includeReceipts} */
-  | `trace_${number}_${FragmentAddressId}_${FragmentAddressId}_${Hex | null}_${0 | 1}`
+  | `trace_${number}_${FragmentAddress}_${FragmentAddress}_${Hex | null}_${0 | 1}`
   /** log_{chainId}_{address}_{topic0}_{topic1}_{topic2}_{topic3}_{includeReceipts} */
-  | `log_${number}_${FragmentAddressId}_${FragmentTopic}_${FragmentTopic}_${FragmentTopic}_${FragmentTopic}_${0 | 1}`
+  | `log_${number}_${FragmentAddress}_${FragmentTopic}_${FragmentTopic}_${FragmentTopic}_${FragmentTopic}_${0 | 1}`
   /** transfer_{chainId}_{fromAddress}_{toAddress}_{includeReceipts} */
-  | `transfer_${number}_${FragmentAddressId}_${FragmentAddressId}_${0 | 1}`;
+  | `transfer_${number}_${FragmentAddress}_${FragmentAddress}_${0 | 1}`;
 
-export const getFragments = (
-  filter: FilterWithoutBlocks,
+export const getFragmentIds = (
+  filter: Omit<Filter, "startBlock" | "endBlock">,
 ): FragmentReturnType => {
   switch (filter.type) {
     case "block":
-      return getBlockFilterFragment(filter as BlockFilter);
+      return getBlockFilterFragmentId(filter as BlockFilter);
     case "transaction":
-      return getTransactionFilterFragments(filter as TransactionFilter);
+      return getTransactionFilterFragmentIds(filter as TransactionFilter);
     case "trace":
-      return getTraceFilterFragments(filter as TraceFilter);
+      return getTraceFilterFragmentIds(filter as TraceFilter);
     case "log":
-      return getLogFilterFragments(filter as LogFilter);
+      return getLogFilterFragmentIds(filter as LogFilter);
     case "transfer":
-      return getTransferFilterFragments(filter as TransferFilter);
+      return getTransferFilterFragmentIds(filter as TransferFilter);
   }
 };
 
 type FragmentReturnType = {
-  fragment: Fragment;
-  adjacentIds: FragmentId[];
+  id: FragmentId;
+  adjacent: FragmentId[];
 }[];
 
-const getAddressFragments = (
+const getAddressFragmentIds = (
   address: Address | Address[] | Factory | undefined,
 ) => {
-  const fragments: {
-    fragment: FragmentAddress;
-    adjacentIds: FragmentAddressId[];
-  }[] = [];
+  const fragments: { id: FragmentAddress; adjacent: FragmentAddress[] }[] = [];
 
   if (isAddressFactory(address)) {
     for (const fragmentAddress of Array.isArray(address.address)
       ? address.address
       : [address.address]) {
-      const fragment = {
-        address: fragmentAddress,
-        eventSelector: address.eventSelector,
-        childAddressLocation: address.childAddressLocation,
-      } satisfies FragmentAddress;
+      const id =
+        `${fragmentAddress}_${address.eventSelector}_${address.childAddressLocation}` as const;
 
-      fragments.push({
-        fragment,
-        adjacentIds: [
-          `${fragmentAddress}_${address.eventSelector}_${address.childAddressLocation}` as const,
-        ],
-      });
+      fragments.push({ id, adjacent: [id] });
     }
   } else {
     for (const fragmentAddress of Array.isArray(address)
       ? address
       : [address ?? null]) {
       fragments.push({
-        fragment: fragmentAddress,
-        adjacentIds: fragmentAddress
-          ? [fragmentAddress, null]
-          : [fragmentAddress],
+        id: fragmentAddress,
+        adjacent: fragmentAddress ? [fragmentAddress, null] : [fragmentAddress],
       });
     }
   }
@@ -142,25 +79,20 @@ const getAddressFragments = (
   return fragments;
 };
 
-export const getBlockFilterFragment = ({
+export const getBlockFilterFragmentId = ({
   chainId,
   interval,
   offset,
 }: Omit<BlockFilter, "fromBlock" | "toBlock">): FragmentReturnType => {
   return [
     {
-      fragment: {
-        type: "block",
-        chainId,
-        interval,
-        offset,
-      },
-      adjacentIds: [`block_${chainId}_${interval}_${offset}`],
+      id: `block_${chainId}_${interval}_${offset}`,
+      adjacent: [`block_${chainId}_${interval}_${offset}`],
     },
   ];
 };
 
-export const getTransactionFilterFragments = ({
+export const getTransactionFilterFragmentIds = ({
   chainId,
   fromAddress,
   toAddress,
@@ -168,36 +100,32 @@ export const getTransactionFilterFragments = ({
   chainId: number;
 }): FragmentReturnType => {
   const fragments: FragmentReturnType = [];
-  const fromAddressFragments = getAddressFragments(fromAddress);
-  const toAddressFragments = getAddressFragments(toAddress);
-
-  for (const fromAddressFragment of fromAddressFragments) {
-    for (const toAddressFragment of toAddressFragments) {
-      const fragment = {
-        type: "transaction",
-        chainId,
-        fromAddress: fromAddressFragment.fragment,
-        toAddress: toAddressFragment.fragment,
-      } satisfies Fragment;
-
-      const adjacentIds: FragmentId[] = [];
-
-      for (const fromAddressAdjacentId of fromAddressFragment.adjacentIds) {
-        for (const toAddressAdjacentId of toAddressFragment.adjacentIds) {
-          adjacentIds.push(
-            `transaction_${chainId}_${fromAddressAdjacentId}_${toAddressAdjacentId}`,
+  const fromAddressFragmentIds = getAddressFragmentIds(fromAddress);
+  const toAddressFragmentIds = getAddressFragmentIds(toAddress);
+
+  for (const fragmentFromAddress of fromAddressFragmentIds) {
+    for (const fragmentToAddress of toAddressFragmentIds) {
+      const id =
+        `transaction_${chainId}_${fragmentFromAddress.id}_${fragmentToAddress.id}` as const;
+
+      const adjacent: FragmentId[] = [];
+
+      for (const adjacentFromAddress of fragmentFromAddress.adjacent) {
+        for (const adjacentToAddress of fragmentToAddress.adjacent) {
+          adjacent.push(
+            `transaction_${chainId}_${adjacentFromAddress}_${adjacentToAddress}`,
           );
         }
       }
 
-      fragments.push({ fragment, adjacentIds });
+      fragments.push({ id, adjacent });
     }
   }
 
   return fragments;
 };
 
-export const getTraceFilterFragments = ({
+export const getTraceFilterFragmentIds = ({
   chainId,
   fromAddress,
   toAddress,
@@ -208,43 +136,37 @@ export const getTraceFilterFragments = ({
   chainId: number;
 }): FragmentReturnType => {
   const fragments: FragmentReturnType = [];
-  const fromAddressFragments = getAddressFragments(fromAddress);
-  const toAddressFragments = getAddressFragments(toAddress);
+  const fromAddressFragmentIds = getAddressFragmentIds(fromAddress);
+  const toAddressFragmentIds = getAddressFragmentIds(toAddress);
   const includeTransactionReceipts = shouldGetTransactionReceipt(filter);
 
-  for (const fromAddressFragment of fromAddressFragments) {
-    for (const toAddressFragment of toAddressFragments) {
+  for (const fragmentFromAddress of fromAddressFragmentIds) {
+    for (const fragmentToAddress of toAddressFragmentIds) {
       for (const fragmentFunctionSelector of Array.isArray(functionSelector)
         ? functionSelector
         : [functionSelector]) {
-        const fragment = {
-          type: "trace",
-          chainId,
-          fromAddress: fromAddressFragment.fragment,
-          toAddress: toAddressFragment.fragment,
-          functionSelector: fragmentFunctionSelector ?? null,
-          includeTransactionReceipts,
-        } satisfies Fragment;
-
-        const adjacentIds: FragmentId[] = [];
-
-        for (const fromAddressAdjacentId of fromAddressFragment.adjacentIds) {
-          for (const toAddressAdjacentId of toAddressFragment.adjacentIds) {
+        const id =
+          `trace_${chainId}_${fragmentFromAddress.id}_${fragmentToAddress.id}_${fragmentFunctionSelector ?? null}_${includeTransactionReceipts ? 1 : 0}` as const;
+
+        const adjacent: FragmentId[] = [];
+
+        for (const adjacentFromAddress of fragmentFromAddress.adjacent) {
+          for (const adjacentToAddress of fragmentToAddress.adjacent) {
             for (const adjacentFunctionSelector of fragmentFunctionSelector
               ? [fragmentFunctionSelector, null]
               : [null]) {
               for (const adjacentTxr of includeTransactionReceipts
                 ? [1]
                 : [0, 1]) {
-                adjacentIds.push(
-                  `trace_${chainId}_${fromAddressAdjacentId}_${toAddressAdjacentId}_${adjacentFunctionSelector}_${adjacentTxr as 0 | 1}`,
+                adjacent.push(
+                  `trace_${chainId}_${adjacentFromAddress}_${adjacentToAddress}_${adjacentFunctionSelector}_${adjacentTxr as 0 | 1}`,
                 );
               }
             }
           }
         }
 
-        fragments.push({ fragment, adjacentIds });
+        fragments.push({ id, adjacent });
       }
     }
   }
@@ -252,7 +174,7 @@ export const getTraceFilterFragments = ({
   return fragments;
 };
 
-export const getLogFilterFragments = ({
+export const getLogFilterFragmentIds = ({
   chainId,
   address,
   topic0,
@@ -262,10 +184,10 @@ export const getLogFilterFragments = ({
   ...filter
 }: Omit<LogFilter, "fromBlock" | "toBlock">): FragmentReturnType => {
   const fragments: FragmentReturnType = [];
-  const addressFragments = getAddressFragments(address);
+  const addressFragmentIds = getAddressFragmentIds(address);
   const includeTransactionReceipts = shouldGetTransactionReceipt(filter);
 
-  for (const addressFragment of addressFragments) {
+  for (const fragmentAddress of addressFragmentIds) {
     for (const fragmentTopic0 of Array.isArray(topic0) ? topic0 : [topic0]) {
       for (const fragmentTopic1 of Array.isArray(topic1) ? topic1 : [topic1]) {
         for (const fragmentTopic2 of Array.isArray(topic2)
@@ -274,20 +196,12 @@ export const getLogFilterFragments = ({
           for (const fragmentTopic3 of Array.isArray(topic3)
             ? topic3
             : [topic3]) {
-            const fragment = {
-              type: "log",
-              chainId,
-              address: addressFragment.fragment,
-              topic0: fragmentTopic0 ?? null,
-              topic1: fragmentTopic1 ?? null,
-              topic2: fragmentTopic2 ?? null,
-              topic3: fragmentTopic3 ?? null,
-              includeTransactionReceipts,
-            } satisfies Fragment;
-
-            const adjacentIds: FragmentId[] = [];
-
-            for (const addressAdjacentId of addressFragment.adjacentIds) {
+            const id =
+              `log_${chainId}_${fragmentAddress.id}_${fragmentTopic0 ?? null}_${fragmentTopic1 ?? null}_${fragmentTopic2 ?? null}_${fragmentTopic3 ?? null}_${includeTransactionReceipts ? 1 : 0}` as const;
+
+            const adjacent: FragmentId[] = [];
+
+            for (const adjacentAddress of fragmentAddress.adjacent) {
               for (const adjacentTopic0 of fragmentTopic0
                 ? [fragmentTopic0, null]
                 : [null]) {
@@ -303,8 +217,8 @@ export const getLogFilterFragments = ({
                       for (const adjacentTxr of includeTransactionReceipts
                         ? [1]
                         : [0, 1]) {
-                        adjacentIds.push(
-                          `log_${chainId}_${addressAdjacentId}_${adjacentTopic0}_${adjacentTopic1}_${adjacentTopic2}_${adjacentTopic3}_${adjacentTxr as 0 | 1}`,
+                        adjacent.push(
+                          `log_${chainId}_${adjacentAddress}_${adjacentTopic0}_${adjacentTopic1}_${adjacentTopic2}_${adjacentTopic3}_${adjacentTxr as 0 | 1}`,
                         );
                       }
                     }
@@ -313,7 +227,7 @@ export const getLogFilterFragments = ({
               }
             }
 
-            fragments.push({ fragment, adjacentIds });
+            fragments.push({ id, adjacent });
           }
         }
       }
@@ -323,7 +237,7 @@ export const getLogFilterFragments = ({
   return fragments;
 };
 
-export const getTransferFilterFragments = ({
+export const getTransferFilterFragmentIds = ({
   chainId,
   fromAddress,
   toAddress,
@@ -332,211 +246,30 @@ export const getTransferFilterFragments = ({
   chainId: number;
 }): FragmentReturnType => {
   const fragments: FragmentReturnType = [];
-  const fromAddressFragments = getAddressFragments(fromAddress);
-  const toAddressFragments = getAddressFragments(toAddress);
+  const fromAddressFragmentIds = getAddressFragmentIds(fromAddress);
+  const toAddressFragmentIds = getAddressFragmentIds(toAddress);
   const includeTransactionReceipts = shouldGetTransactionReceipt(filter);
 
-  for (const fromAddressFragment of fromAddressFragments) {
-    for (const toAddressFragment of toAddressFragments) {
-      const fragment = {
-        type: "transfer",
-        chainId,
-        fromAddress: fromAddressFragment.fragment,
-        toAddress: toAddressFragment.fragment,
-        includeTransactionReceipts,
-      } satisfies Fragment;
+  for (const fragmentFromAddress of fromAddressFragmentIds) {
+    for (const fragmentToAddress of toAddressFragmentIds) {
+      const id =
+        `transfer_${chainId}_${fragmentFromAddress.id}_${fragmentToAddress.id}_${includeTransactionReceipts ? 1 : 0}` as const;
 
-      const adjacentIds: FragmentId[] = [];
+      const adjacent: FragmentId[] = [];
 
-      for (const fromAddressAdjacentId of fromAddressFragment.adjacentIds) {
-        for (const toAddressAdjacentId of toAddressFragment.adjacentIds) {
+      for (const adjacentFromAddress of fragmentFromAddress.adjacent) {
+        for (const adjacentToAddress of fragmentToAddress.adjacent) {
           for (const adjacentTxr of includeTransactionReceipts ? [1] : [0, 1]) {
-            adjacentIds.push(
-              `transfer_${chainId}_${fromAddressAdjacentId}_${toAddressAdjacentId}_${adjacentTxr as 0 | 1}`,
+            adjacent.push(
+              `transfer_${chainId}_${adjacentFromAddress}_${adjacentToAddress}_${adjacentTxr as 0 | 1}`,
             );
           }
         }
       }
 
-      fragments.push({ fragment, adjacentIds });
+      fragments.push({ id, adjacent });
     }
   }
 
   return fragments;
 };
-
-const fragmentAddressToId = (
-  fragmentAddress: FragmentAddress,
-): FragmentAddressId => {
-  if (fragmentAddress === null) return null;
-  if (typeof fragmentAddress === "string") return fragmentAddress;
-  return `${fragmentAddress.address}_${fragmentAddress.eventSelector}_${fragmentAddress.childAddressLocation}`;
-};
-
-export const fragmentToId = (fragment: Fragment): FragmentId => {
-  switch (fragment.type) {
-    case "block":
-      return `block_${fragment.chainId}_${fragment.interval}_${fragment.offset}`;
-    case "transaction":
-      return `transaction_${fragment.chainId}_${fragmentAddressToId(fragment.fromAddress)}_${fragmentAddressToId(fragment.toAddress)}`;
-    case "trace":
-      return `trace_${fragment.chainId}_${fragmentAddressToId(fragment.fromAddress)}_${fragmentAddressToId(fragment.toAddress)}_${fragment.functionSelector}_${fragment.includeTransactionReceipts ? 1 : 0}`;
-    case "log":
-      return `log_${fragment.chainId}_${fragmentAddressToId(fragment.address)}_${fragment.topic0}_${fragment.topic1}_${fragment.topic2}_${fragment.topic3}_${fragment.includeTransactionReceipts ? 1 : 0}`;
-    case "transfer":
-      return `transfer_${fragment.chainId}_${fragmentAddressToId(fragment.fromAddress)}_${fragmentAddressToId(fragment.toAddress)}_${fragment.includeTransactionReceipts ? 1 : 0}`;
-  }
-};
-
-const recoverAddress = (
-  baseAddress: FilterAddress,
-  fragmentAddresses: FragmentAddress[],
-): FilterAddress => {
-  if (baseAddress === undefined) return undefined;
-  if (typeof baseAddress === "string") return baseAddress;
-  if (Array.isArray(baseAddress)) return dedupe(fragmentAddresses) as Address[];
-  if (typeof baseAddress.address === "string") return baseAddress;
-
-  const address = {
-    type: "log",
-    chainId: baseAddress.chainId,
-    address: [] as Address[],
-    eventSelector: baseAddress.eventSelector,
-    childAddressLocation: baseAddress.childAddressLocation,
-  } satisfies Factory;
-
-  address.address = dedupe(
-    (fragmentAddresses as Extract<FragmentAddress, { address: Address }>[]).map(
-      ({ address }) => address,
-    ),
-  );
-
-  return address;
-};
-
-const recoverSelector = (
-  base: Hex | Hex[] | undefined,
-  fragments: (Hex | null)[],
-): Hex | Hex[] | undefined => {
-  if (base === undefined) return undefined;
-  if (typeof base === "string") return base;
-  return dedupe(fragments) as Hex[];
-};
-
-const recoverTopic = (
-  base: Hex | Hex[] | null,
-  fragments: (Hex | null)[],
-): Hex | Hex[] | null => {
-  if (base === null) return null;
-  if (typeof base === "string") return base;
-  return dedupe(fragments) as Hex[];
-};
-
-export const recoverFilter = (
-  baseFilter: FilterWithoutBlocks,
-  fragments: Fragment[],
-): FilterWithoutBlocks => {
-  switch (baseFilter.type) {
-    case "block": {
-      return baseFilter;
-    }
-    case "transaction": {
-      return {
-        ...baseFilter,
-        fromAddress: recoverAddress(
-          baseFilter.fromAddress,
-          (fragments as Extract<Fragment, { type: "transaction" }>[]).map(
-            (fragment) => fragment.fromAddress,
-          ),
-        ),
-        toAddress: recoverAddress(
-          baseFilter.toAddress,
-          (fragments as Extract<Fragment, { type: "transaction" }>[]).map(
-            (fragment) => fragment.toAddress,
-          ),
-        ),
-      };
-    }
-    case "trace": {
-      return {
-        ...baseFilter,
-        fromAddress: recoverAddress(
-          baseFilter.fromAddress,
-          (fragments as Extract<Fragment, { type: "transaction" }>[]).map(
-            (fragment) => fragment.fromAddress,
-          ),
-        ),
-        toAddress: recoverAddress(
-          baseFilter.toAddress,
-          (fragments as Extract<Fragment, { type: "transaction" }>[]).map(
-            (fragment) => fragment.toAddress,
-          ),
-        ),
-        functionSelector: recoverSelector(
-          baseFilter.functionSelector,
-          fragments.map(
-            (fragment) =>
-              (fragment as Extract<Fragment, { type: "trace" }>)
-                .functionSelector,
-          ),
-        ),
-      };
-    }
-    case "log": {
-      return {
-        ...baseFilter,
-        address: recoverAddress(
-          baseFilter.address,
-          (fragments as Extract<Fragment, { type: "log" }>[]).map(
-            (fragment) => fragment.address,
-          ),
-        ),
-        topic0: recoverTopic(
-          baseFilter.topic0,
-          fragments.map(
-            (fragment) =>
-              (fragment as Extract<Fragment, { type: "log" }>).topic0,
-          ),
-        ),
-        topic1: recoverTopic(
-          baseFilter.topic1,
-          fragments.map(
-            (fragment) =>
-              (fragment as Extract<Fragment, { type: "log" }>).topic1,
-          ),
-        ),
-        topic2: recoverTopic(
-          baseFilter.topic2,
-          fragments.map(
-            (fragment) =>
-              (fragment as Extract<Fragment, { type: "log" }>).topic2,
-          ),
-        ),
-        topic3: recoverTopic(
-          baseFilter.topic3,
-          fragments.map(
-            (fragment) =>
-              (fragment as Extract<Fragment, { type: "log" }>).topic3,
-          ),
-        ),
-      };
-    }
-    case "transfer": {
-      return {
-        ...baseFilter,
-        fromAddress: recoverAddress(
-          baseFilter.fromAddress,
-          (fragments as Extract<Fragment, { type: "transaction" }>[]).map(
-            (fragment) => fragment.fromAddress,
-          ),
-        ),
-        toAddress: recoverAddress(
-          baseFilter.toAddress,
-          (fragments as Extract<Fragment, { type: "transaction" }>[]).map(
-            (fragment) => fragment.toAddress,
-          ),
-        ),
-      };
-    }
-  }
-};
diff --git a/packages/core/src/sync/index.ts b/packages/core/src/sync/index.ts
index 1e1fce91..b184ca1f 100644
--- a/packages/core/src/sync/index.ts
+++ b/packages/core/src/sync/index.ts
@@ -25,7 +25,6 @@ import {
   type Interval,
   intervalDifference,
   intervalIntersection,
-  intervalIntersectionMany,
   intervalSum,
   sortIntervals,
 } from "@/utils/interval.js";
@@ -162,17 +161,27 @@ const min = (...checkpoints: (string | undefined)[]) => {
   })!;
 };
 
-export const splitEvents = (events: RawEvent[]): RawEvent[][] => {
+export const splitEvents = (
+  events: RawEvent[],
+): { checkpoint: string; events: RawEvent[] }[] => {
   let prevHash: Hash | undefined;
-  const result: RawEvent[][] = [];
+  const result: { checkpoint: string; events: RawEvent[] }[] = [];
 
   for (const event of events) {
     if (prevHash === undefined || prevHash !== event.block.hash) {
-      result.push([]);
+      result.push({
+        checkpoint: encodeCheckpoint({
+          ...maxCheckpoint,
+          blockTimestamp: Number(event.block.timestamp),
+          chainId: BigInt(event.chainId),
+          blockNumber: event.block.number,
+        }),
+        events: [],
+      });
       prevHash = event.block.hash;
     }
 
-    result[result.length - 1]!.push(event);
+    result[result.length - 1]!.events.push(event);
   }
 
   return result;
@@ -987,15 +996,10 @@ export const getCachedBlock = ({
       filter.fromBlock ?? 0,
       filter.toBlock ?? Number.POSITIVE_INFINITY,
     ] satisfies Interval;
-    const fragmentIntervals = historicalSync.intervalsCache.get(filter)!;
+    const cachedIntervals = historicalSync.intervalsCache.get(filter)!;
 
     const completedIntervals = sortIntervals(
-      intervalIntersection(
-        [requiredInterval],
-        intervalIntersectionMany(
-          fragmentIntervals.map(({ intervals }) => intervals),
-        ),
-      ),
+      intervalIntersection([requiredInterval], cachedIntervals),
     );
 
     if (completedIntervals.length === 0) return undefined;
@@ -1086,7 +1090,7 @@ export async function* localHistoricalSyncGenerator({
 
   const requiredIntervals = Array.from(
     historicalSync.intervalsCache.entries(),
-  ).flatMap(([filter, fragmentIntervals]) =>
+  ).flatMap(([filter, interval]) =>
     intervalDifference(
       [
         [
@@ -1097,9 +1101,7 @@ export async function* localHistoricalSyncGenerator({
           ),
         ],
       ],
-      intervalIntersectionMany(
-        fragmentIntervals.map(({ intervals }) => intervals),
-      ),
+      interval,
     ),
   );
 
diff --git a/packages/core/src/sync/source.ts b/packages/core/src/sync/source.ts
index 01c04fe2..ed067d4b 100644
--- a/packages/core/src/sync/source.ts
+++ b/packages/core/src/sync/source.ts
@@ -40,16 +40,7 @@ export type Filter =
   | TransferFilter
   | TransactionFilter
   | TraceFilter;
-export type FilterWithoutBlocks =
-  | Omit<BlockFilter, "fromBlock" | "toBlock">
-  | Omit<TransactionFilter, "fromBlock" | "toBlock">
-  | Omit<TraceFilter, "fromBlock" | "toBlock">
-  | Omit<LogFilter, "fromBlock" | "toBlock">
-  | Omit<TransferFilter, "fromBlock" | "toBlock">;
 export type Factory = LogFactory;
-export type FilterAddress<
-  factory extends Factory | undefined = Factory | undefined,
-> = factory extends Factory ? factory : Address | Address[] | undefined;
 
 export type ContractMetadata = {
   type: "contract";
@@ -75,11 +66,11 @@ export type LogFilter<
 > = {
   type: "log";
   chainId: number;
-  address: FilterAddress<factory>;
-  topic0: LogTopic;
-  topic1: LogTopic;
-  topic2: LogTopic;
-  topic3: LogTopic;
+  address: factory extends Factory ? factory : Address | Address[] | undefined;
+  topic0: LogTopic | undefined;
+  topic1: LogTopic | undefined;
+  topic2: LogTopic | undefined;
+  topic3: LogTopic | undefined;
   fromBlock: number | undefined;
   toBlock: number | undefined;
   include:
@@ -108,8 +99,12 @@ export type TransferFilter<
 > = {
   type: "transfer";
   chainId: number;
-  fromAddress: FilterAddress<fromFactory>;
-  toAddress: FilterAddress<toFactory>;
+  fromAddress: fromFactory extends Factory
+    ? fromFactory
+    : Address | Address[] | undefined;
+  toAddress: toFactory extends Factory
+    ? fromFactory
+    : Address | Address[] | undefined;
   includeReverted: boolean;
   fromBlock: number | undefined;
   toBlock: number | undefined;
@@ -129,8 +124,12 @@ export type TransactionFilter<
 > = {
   type: "transaction";
   chainId: number;
-  fromAddress: FilterAddress<fromFactory>;
-  toAddress: FilterAddress<toFactory>;
+  fromAddress: fromFactory extends Factory
+    ? fromFactory
+    : Address | Address[] | undefined;
+  toAddress: toFactory extends Factory
+    ? toFactory
+    : Address | Address[] | undefined;
   includeReverted: boolean;
   fromBlock: number | undefined;
   toBlock: number | undefined;
@@ -149,8 +148,12 @@ export type TraceFilter<
 > = {
   type: "trace";
   chainId: number;
-  fromAddress: FilterAddress<fromFactory>;
-  toAddress: FilterAddress<toFactory>;
+  fromAddress: fromFactory extends Factory
+    ? fromFactory
+    : Address | Address[] | undefined;
+  toAddress: toFactory extends Factory
+    ? toFactory
+    : Address | Address[] | undefined;
   functionSelector: Hex | Hex[] | undefined;
   callType: Trace["result"]["type"] | undefined;
   includeReverted: boolean;
diff --git a/packages/core/src/utils/interval.ts b/packages/core/src/utils/interval.ts
index ff1722cc..62508a50 100644
--- a/packages/core/src/utils/interval.ts
+++ b/packages/core/src/utils/interval.ts
@@ -166,19 +166,6 @@ export function intervalDifference(
   return result;
 }
 
-/**
- * Return an interval that encompasses all the intervals in the list.
- *
- * @param intervals List of numeric intervals to find the bounds of.
- * @returns Bounds of the intervals.
- */
-export function intervalBounds(intervals: Interval[]): Interval {
-  const start = Math.min(...intervals.map((interval) => interval[0]));
-  const end = Math.max(...intervals.map((interval) => interval[1]));
-
-  return [start, end];
-}
-
 export function sortIntervals(intervals: Interval[]) {
   return intervals.sort((a, b) => (a[0] < b[0] ? -1 : 1));
 }


Test patch:
diff --git a/packages/core/src/sync-historical/index.test.ts b/packages/core/src/sync-historical/index.test.ts
index 6e0876cb..bb4d8645 100644
--- a/packages/core/src/sync-historical/index.test.ts
+++ b/packages/core/src/sync-historical/index.test.ts
@@ -25,12 +25,7 @@ import {
 } from "@/_test/utils.js";
 import { buildConfigAndIndexingFunctions } from "@/build/configAndIndexingFunctions.js";
 import { createRequestQueue } from "@/utils/requestQueue.js";
-import {
-  encodeFunctionData,
-  encodeFunctionResult,
-  toHex,
-  zeroAddress,
-} from "viem";
+import { encodeFunctionData, encodeFunctionResult, toHex } from "viem";
 import { parseEther } from "viem/utils";
 import { beforeEach, expect, test, vi } from "vitest";
 import { createHistoricalSync } from "./index.js";
@@ -573,7 +568,7 @@ test("sync() with many filters", async (context) => {
   await cleanup();
 });
 
-test("sync() with cache", async (context) => {
+test("sync() with cache hit", async (context) => {
   const { cleanup, syncStore } = await setupDatabaseServices(context);
 
   const network = getNetwork();
@@ -628,80 +623,6 @@ test("sync() with cache", async (context) => {
   await cleanup();
 });
 
-test("sync() with partial cache", async (context) => {
-  const { cleanup, syncStore } = await setupDatabaseServices(context);
-
-  const network = getNetwork();
-  const requestQueue = createRequestQueue({
-    network,
-    common: context.common,
-  });
-
-  const { address } = await deployErc20({ sender: ALICE });
-  await mintErc20({
-    erc20: address,
-    to: ALICE,
-    amount: parseEther("1"),
-    sender: ALICE,
-  });
-
-  const { config, rawIndexingFunctions } = getErc20ConfigAndIndexingFunctions({
-    address,
-  });
-  const { sources } = await buildConfigAndIndexingFunctions({
-    config,
-    rawIndexingFunctions,
-  });
-
-  let historicalSync = await createHistoricalSync({
-    common: context.common,
-    network,
-    sources,
-    syncStore,
-    requestQueue,
-    onFatalError: () => {},
-  });
-
-  await historicalSync.sync([1, 2]);
-
-  // re-instantiate `historicalSync` to reset the cached intervals
-
-  const spy = vi.spyOn(requestQueue, "request");
-
-  // @ts-ignore
-  sources[0]!.filter.address = [sources[0]!.filter.address, zeroAddress];
-
-  historicalSync = await createHistoricalSync({
-    common: context.common,
-    network,
-    sources,
-    syncStore,
-    requestQueue,
-    onFatalError: () => {},
-  });
-
-  await historicalSync.sync([1, 2]);
-  expect(spy).toHaveBeenCalledTimes(2);
-
-  expect(spy).toHaveBeenCalledWith({
-    method: "eth_getLogs",
-    params: [
-      {
-        address: [zeroAddress],
-        fromBlock: "0x1",
-        toBlock: "0x2",
-        topics: [
-          [
-            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
-          ],
-        ],
-      },
-    ],
-  });
-
-  await cleanup();
-});
-
 test("syncBlock() with cache", async (context) => {
   const { cleanup, syncStore } = await setupDatabaseServices(context);
 
diff --git a/packages/core/src/sync-store/index.test.ts b/packages/core/src/sync-store/index.test.ts
index 0cde7691..c2708874 100644
--- a/packages/core/src/sync-store/index.test.ts
+++ b/packages/core/src/sync-store/index.test.ts
@@ -89,29 +89,7 @@ test("getIntervals() empty", async (context) => {
     filters: [filter],
   });
 
-  expect(intervals).toMatchInlineSnapshot(`
-    Map {
-      {
-        "chainId": 1,
-        "fromBlock": undefined,
-        "include": [],
-        "interval": 1,
-        "offset": 0,
-        "toBlock": undefined,
-        "type": "block",
-      } => [
-        {
-          "fragment": {
-            "chainId": 1,
-            "interval": 1,
-            "offset": 0,
-            "type": "block",
-          },
-          "intervals": [],
-        },
-      ],
-    }
-  `);
+  expect(Array.from(intervals.values())[0]).toHaveLength(0);
 
   await cleanup();
 });
@@ -143,34 +121,8 @@ test("getIntervals() returns intervals", async (context) => {
     filters: [filter],
   });
 
-  expect(intervals).toMatchInlineSnapshot(`
-    Map {
-      {
-        "chainId": 1,
-        "fromBlock": undefined,
-        "include": [],
-        "interval": 1,
-        "offset": 0,
-        "toBlock": undefined,
-        "type": "block",
-      } => [
-        {
-          "fragment": {
-            "chainId": 1,
-            "interval": 1,
-            "offset": 0,
-            "type": "block",
-          },
-          "intervals": [
-            [
-              0,
-              4,
-            ],
-          ],
-        },
-      ],
-    }
-  `);
+  expect(Array.from(intervals.values())[0]).toHaveLength(1);
+  expect(Array.from(intervals.values())[0]![0]).toStrictEqual([0, 4]);
 
   await cleanup();
 });
@@ -211,34 +163,8 @@ test("getIntervals() merges intervals", async (context) => {
     filters: [filter],
   });
 
-  expect(intervals).toMatchInlineSnapshot(`
-    Map {
-      {
-        "chainId": 1,
-        "fromBlock": undefined,
-        "include": [],
-        "interval": 1,
-        "offset": 0,
-        "toBlock": undefined,
-        "type": "block",
-      } => [
-        {
-          "fragment": {
-            "chainId": 1,
-            "interval": 1,
-            "offset": 0,
-            "type": "block",
-          },
-          "intervals": [
-            [
-              0,
-              8,
-            ],
-          ],
-        },
-      ],
-    }
-  `);
+  expect(Array.from(intervals.values())[0]).toHaveLength(1);
+  expect(Array.from(intervals.values())[0]![0]).toStrictEqual([0, 8]);
 
   await cleanup();
 });
@@ -272,7 +198,6 @@ test("getIntervals() adjacent intervals", async (context) => {
   await syncStore.insertIntervals({
     intervals: [
       {
-        // @ts-ignore
         filter: { ...filter, address: undefined },
         interval: [5, 8],
       },
@@ -283,43 +208,8 @@ test("getIntervals() adjacent intervals", async (context) => {
     filters: [filter],
   });
 
-  expect(intervals).toMatchInlineSnapshot(`
-    Map {
-      {
-        "address": [
-          "0x0000000000000000000000000000000000000000",
-        ],
-        "chainId": 1,
-        "fromBlock": undefined,
-        "include": [],
-        "toBlock": undefined,
-        "topic0": null,
-        "topic1": null,
-        "topic2": null,
-        "topic3": null,
-        "type": "log",
-      } => [
-        {
-          "fragment": {
-            "address": "0x0000000000000000000000000000000000000000",
-            "chainId": 1,
-            "includeTransactionReceipts": false,
-            "topic0": null,
-            "topic1": null,
-            "topic2": null,
-            "topic3": null,
-            "type": "log",
-          },
-          "intervals": [
-            [
-              0,
-              8,
-            ],
-          ],
-        },
-      ],
-    }
-  `);
+  expect(Array.from(intervals.values())[0]).toHaveLength(1);
+  expect(Array.from(intervals.values())[0]![0]).toStrictEqual([0, 8]);
 
   await cleanup();
 });
@@ -365,124 +255,8 @@ test("insertIntervals() merges duplicates", async (context) => {
     filters: [filter],
   });
 
-  expect(intervals).toMatchInlineSnapshot(`
-    Map {
-      {
-        "chainId": 1,
-        "fromBlock": undefined,
-        "include": [],
-        "interval": 1,
-        "offset": 0,
-        "toBlock": undefined,
-        "type": "block",
-      } => [
-        {
-          "fragment": {
-            "chainId": 1,
-            "interval": 1,
-            "offset": 0,
-            "type": "block",
-          },
-          "intervals": [
-            [
-              0,
-              8,
-            ],
-          ],
-        },
-      ],
-    }
-  `);
-
-  await cleanup();
-});
-
-test("insertIntervals() preserves fragments", async (context) => {
-  const { cleanup, syncStore } = await setupDatabaseServices(context);
-
-  const filter = {
-    type: "log",
-    chainId: 1,
-    topic0: null,
-    topic1: null,
-    topic2: null,
-    topic3: null,
-    address: [zeroAddress, ALICE],
-    fromBlock: undefined,
-    toBlock: undefined,
-    include: [],
-  } satisfies LogFilter;
-
-  await syncStore.insertIntervals({
-    intervals: [
-      {
-        filter,
-        interval: [0, 4],
-      },
-    ],
-    chainId: 1,
-  });
-
-  const intervals = await syncStore.getIntervals({
-    filters: [filter],
-  });
-
-  expect(intervals).toMatchInlineSnapshot(`
-    Map {
-      {
-        "address": [
-          "0x0000000000000000000000000000000000000000",
-          "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
-        ],
-        "chainId": 1,
-        "fromBlock": undefined,
-        "include": [],
-        "toBlock": undefined,
-        "topic0": null,
-        "topic1": null,
-        "topic2": null,
-        "topic3": null,
-        "type": "log",
-      } => [
-        {
-          "fragment": {
-            "address": "0x0000000000000000000000000000000000000000",
-            "chainId": 1,
-            "includeTransactionReceipts": false,
-            "topic0": null,
-            "topic1": null,
-            "topic2": null,
-            "topic3": null,
-            "type": "log",
-          },
-          "intervals": [
-            [
-              0,
-              4,
-            ],
-          ],
-        },
-        {
-          "fragment": {
-            "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
-            "chainId": 1,
-            "includeTransactionReceipts": false,
-            "topic0": null,
-            "topic1": null,
-            "topic2": null,
-            "topic3": null,
-            "type": "log",
-          },
-          "intervals": [
-            [
-              0,
-              4,
-            ],
-          ],
-        },
-      ],
-    }
-  `);
+  expect(Array.from(intervals.values())[0]).toHaveLength(1);
+  expect(Array.from(intervals.values())[0]![0]).toStrictEqual([0, 8]);
 
   await cleanup();
 });
diff --git a/packages/core/src/sync/fragments.test.ts b/packages/core/src/sync/fragments.test.ts
index cfa9db32..da910817 100644
--- a/packages/core/src/sync/fragments.test.ts
+++ b/packages/core/src/sync/fragments.test.ts
@@ -1,14 +1,14 @@
 import { buildLogFactory } from "@/build/factory.js";
 import { parseAbiItem } from "viem";
 import { expect, test } from "vitest";
-import { getLogFilterFragments } from "./fragments.js";
+import { getLogFilterFragmentIds } from "./fragments.js";
 
 const llamaFactoryEventAbiItem = parseAbiItem(
   "event LlamaInstanceCreated(address indexed deployer, string indexed name, address llamaCore, address llamaExecutor, address llamaPolicy, uint256 chainId)",
 );
 
-test("getLogFilterFragments generates 1 log filter fragment for null filter", () => {
-  const logFilterFragments = getLogFilterFragments({
+test("getLogFilterFragmentIds generates 1 log filter fragment for null filter", () => {
+  const logFilterFragments = getLogFilterFragmentIds({
     type: "log",
     chainId: 1,
     address: undefined,
@@ -19,30 +19,11 @@ test("getLogFilterFragments generates 1 log filter fragment for null filter", ()
     include: [],
   });
 
-  expect(logFilterFragments).toMatchInlineSnapshot(`
-    [
-      {
-        "adjacentIds": [
-          "log_1_null_null_null_null_null_0",
-          "log_1_null_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": null,
-          "chainId": 1,
-          "includeTransactionReceipts": false,
-          "topic0": null,
-          "topic1": null,
-          "topic2": null,
-          "topic3": null,
-          "type": "log",
-        },
-      },
-    ]
-  `);
+  expect(logFilterFragments[0]!.id).toBe("log_1_null_null_null_null_null_0");
 });
 
-test("getLogFilterFragments generates 1 log filter fragment for simple filter", () => {
-  const logFilterFragments = getLogFilterFragments({
+test("getLogFilterFragmentIds generates 1 log filter fragment for simple filter", () => {
+  const logFilterFragments = getLogFilterFragmentIds({
     type: "log",
     chainId: 1,
     address: "0xa",
@@ -53,32 +34,11 @@ test("getLogFilterFragments generates 1 log filter fragment for simple filter",
     include: [],
   });
 
-  expect(logFilterFragments).toMatchInlineSnapshot(`
-    [
-      {
-        "adjacentIds": [
-          "log_1_0xa_null_null_null_null_0",
-          "log_1_0xa_null_null_null_null_1",
-          "log_1_null_null_null_null_null_0",
-          "log_1_null_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": "0xa",
-          "chainId": 1,
-          "includeTransactionReceipts": false,
-          "topic0": null,
-          "topic1": null,
-          "topic2": null,
-          "topic3": null,
-          "type": "log",
-        },
-      },
-    ]
-  `);
+  expect(logFilterFragments[0]!.id).toBe("log_1_0xa_null_null_null_null_0");
 });
 
-test("getLogFilterFragments generates 4 log filter fragment for 2x2 filter", () => {
-  const logFilterFragments = getLogFilterFragments({
+test("getLogFilterFragmentIds generates 4 log filter fragment for 2x2 filter", () => {
+  const logFilterFragments = getLogFilterFragmentIds({
     type: "log",
     chainId: 115511,
     address: ["0xa", "0xb"],
@@ -89,134 +49,14 @@ test("getLogFilterFragments generates 4 log filter fragment for 2x2 filter", ()
     include: [],
   });
 
-  expect(logFilterFragments).toMatchInlineSnapshot(`
-    [
-      {
-        "adjacentIds": [
-          "log_115511_0xa_0xc_null_0xe_null_0",
-          "log_115511_0xa_0xc_null_0xe_null_1",
-          "log_115511_0xa_0xc_null_null_null_0",
-          "log_115511_0xa_0xc_null_null_null_1",
-          "log_115511_0xa_null_null_0xe_null_0",
-          "log_115511_0xa_null_null_0xe_null_1",
-          "log_115511_0xa_null_null_null_null_0",
-          "log_115511_0xa_null_null_null_null_1",
-          "log_115511_null_0xc_null_0xe_null_0",
-          "log_115511_null_0xc_null_0xe_null_1",
-          "log_115511_null_0xc_null_null_null_0",
-          "log_115511_null_0xc_null_null_null_1",
-          "log_115511_null_null_null_0xe_null_0",
-          "log_115511_null_null_null_0xe_null_1",
-          "log_115511_null_null_null_null_null_0",
-          "log_115511_null_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": "0xa",
-          "chainId": 115511,
-          "includeTransactionReceipts": false,
-          "topic0": "0xc",
-          "topic1": null,
-          "topic2": "0xe",
-          "topic3": null,
-          "type": "log",
-        },
-      },
-      {
-        "adjacentIds": [
-          "log_115511_0xa_0xd_null_0xe_null_0",
-          "log_115511_0xa_0xd_null_0xe_null_1",
-          "log_115511_0xa_0xd_null_null_null_0",
-          "log_115511_0xa_0xd_null_null_null_1",
-          "log_115511_0xa_null_null_0xe_null_0",
-          "log_115511_0xa_null_null_0xe_null_1",
-          "log_115511_0xa_null_null_null_null_0",
-          "log_115511_0xa_null_null_null_null_1",
-          "log_115511_null_0xd_null_0xe_null_0",
-          "log_115511_null_0xd_null_0xe_null_1",
-          "log_115511_null_0xd_null_null_null_0",
-          "log_115511_null_0xd_null_null_null_1",
-          "log_115511_null_null_null_0xe_null_0",
-          "log_115511_null_null_null_0xe_null_1",
-          "log_115511_null_null_null_null_null_0",
-          "log_115511_null_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": "0xa",
-          "chainId": 115511,
-          "includeTransactionReceipts": false,
-          "topic0": "0xd",
-          "topic1": null,
-          "topic2": "0xe",
-          "topic3": null,
-          "type": "log",
-        },
-      },
-      {
-        "adjacentIds": [
-          "log_115511_0xb_0xc_null_0xe_null_0",
-          "log_115511_0xb_0xc_null_0xe_null_1",
-          "log_115511_0xb_0xc_null_null_null_0",
-          "log_115511_0xb_0xc_null_null_null_1",
-          "log_115511_0xb_null_null_0xe_null_0",
-          "log_115511_0xb_null_null_0xe_null_1",
-          "log_115511_0xb_null_null_null_null_0",
-          "log_115511_0xb_null_null_null_null_1",
-          "log_115511_null_0xc_null_0xe_null_0",
-          "log_115511_null_0xc_null_0xe_null_1",
-          "log_115511_null_0xc_null_null_null_0",
-          "log_115511_null_0xc_null_null_null_1",
-          "log_115511_null_null_null_0xe_null_0",
-          "log_115511_null_null_null_0xe_null_1",
-          "log_115511_null_null_null_null_null_0",
-          "log_115511_null_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": "0xb",
-          "chainId": 115511,
-          "includeTransactionReceipts": false,
-          "topic0": "0xc",
-          "topic1": null,
-          "topic2": "0xe",
-          "topic3": null,
-          "type": "log",
-        },
-      },
-      {
-        "adjacentIds": [
-          "log_115511_0xb_0xd_null_0xe_null_0",
-          "log_115511_0xb_0xd_null_0xe_null_1",
-          "log_115511_0xb_0xd_null_null_null_0",
-          "log_115511_0xb_0xd_null_null_null_1",
-          "log_115511_0xb_null_null_0xe_null_0",
-          "log_115511_0xb_null_null_0xe_null_1",
-          "log_115511_0xb_null_null_null_null_0",
-          "log_115511_0xb_null_null_null_null_1",
-          "log_115511_null_0xd_null_0xe_null_0",
-          "log_115511_null_0xd_null_0xe_null_1",
-          "log_115511_null_0xd_null_null_null_0",
-          "log_115511_null_0xd_null_null_null_1",
-          "log_115511_null_null_null_0xe_null_0",
-          "log_115511_null_null_null_0xe_null_1",
-          "log_115511_null_null_null_null_null_0",
-          "log_115511_null_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": "0xb",
-          "chainId": 115511,
-          "includeTransactionReceipts": false,
-          "topic0": "0xd",
-          "topic1": null,
-          "topic2": "0xe",
-          "topic3": null,
-          "type": "log",
-        },
-      },
-    ]
-  `);
+  expect(logFilterFragments[0]!.id).toBe("log_115511_0xa_0xc_null_0xe_null_0");
+  expect(logFilterFragments[1]!.id).toBe("log_115511_0xa_0xd_null_0xe_null_0");
+  expect(logFilterFragments[2]!.id).toBe("log_115511_0xb_0xc_null_0xe_null_0");
+  expect(logFilterFragments[3]!.id).toBe("log_115511_0xb_0xd_null_0xe_null_0");
 });
 
-test("getLogFilterFragments generates 12 log filter fragment for 2x2x3 filter", () => {
-  const logFilterFragments = getLogFilterFragments({
+test("getLogFilterFragmentIds generates 12 log filter fragment for 2x2x3 filter", () => {
+  const logFilterFragments = getLogFilterFragmentIds({
     type: "log",
     chainId: 1,
     address: ["0xa", "0xb"],
@@ -230,8 +70,8 @@ test("getLogFilterFragments generates 12 log filter fragment for 2x2x3 filter",
   expect(logFilterFragments.length).toBe(12);
 });
 
-test("getLogFilterFragments includeTransactionReceipts", () => {
-  const logFilterFragments = getLogFilterFragments({
+test("getLogFilterFragmentIds includeTransactionReceipts", () => {
+  const logFilterFragments = getLogFilterFragmentIds({
     type: "log",
     chainId: 1,
     address: undefined,
@@ -242,28 +82,10 @@ test("getLogFilterFragments includeTransactionReceipts", () => {
     include: ["transactionReceipt.status"],
   });
 
-  expect(logFilterFragments).toMatchInlineSnapshot(`
-    [
-      {
-        "adjacentIds": [
-          "log_1_null_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": null,
-          "chainId": 1,
-          "includeTransactionReceipts": true,
-          "topic0": null,
-          "topic1": null,
-          "topic2": null,
-          "topic3": null,
-          "type": "log",
-        },
-      },
-    ]
-  `);
+  expect(logFilterFragments[0]!.id).toBe("log_1_null_null_null_null_null_1");
 });
 
-test("getLogFilterFragments builds id containing factory topic", () => {
+test("getLogFilterFragmentIds builds id containing factory topic", () => {
   const factory = buildLogFactory({
     address: "0xa",
     event: llamaFactoryEventAbiItem,
@@ -271,7 +93,7 @@ test("getLogFilterFragments builds id containing factory topic", () => {
     chainId: 1,
   });
 
-  const fragments = getLogFilterFragments({
+  const fragments = getLogFilterFragmentIds({
     type: "log",
     chainId: 1,
     topic0: null,
@@ -282,33 +104,14 @@ test("getLogFilterFragments builds id containing factory topic", () => {
     include: [],
   });
 
-  expect(fragments).toMatchInlineSnapshot(`
-    [
-      {
-        "adjacentIds": [
-          "log_1_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_topic1_null_null_null_null_0",
-          "log_1_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_topic1_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": {
-            "address": "0xa",
-            "childAddressLocation": "topic1",
-            "eventSelector": "0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599",
-          },
-          "chainId": 1,
-          "includeTransactionReceipts": false,
-          "topic0": null,
-          "topic1": null,
-          "topic2": null,
-          "topic3": null,
-          "type": "log",
-        },
-      },
-    ]
-  `);
+  expect(fragments).toHaveLength(1);
+
+  expect(fragments[0]!.id).toBe(
+    "log_1_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_topic1_null_null_null_null_0",
+  );
 });
 
-test("getLogFilterFragments builds id containing factory offset", () => {
+test("getLogFilterFragmentIds builds id containing factory offset", () => {
   const factory = buildLogFactory({
     address: "0xa",
     event: llamaFactoryEventAbiItem,
@@ -316,7 +119,7 @@ test("getLogFilterFragments builds id containing factory offset", () => {
     chainId: 1,
   });
 
-  const fragments = getLogFilterFragments({
+  const fragments = getLogFilterFragmentIds({
     type: "log",
     chainId: 115511,
     topic0: null,
@@ -327,33 +130,14 @@ test("getLogFilterFragments builds id containing factory offset", () => {
     include: [],
   });
 
-  expect(fragments).toMatchInlineSnapshot(`
-    [
-      {
-        "adjacentIds": [
-          "log_115511_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64_null_null_null_null_0",
-          "log_115511_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": {
-            "address": "0xa",
-            "childAddressLocation": "offset64",
-            "eventSelector": "0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599",
-          },
-          "chainId": 115511,
-          "includeTransactionReceipts": false,
-          "topic0": null,
-          "topic1": null,
-          "topic2": null,
-          "topic3": null,
-          "type": "log",
-        },
-      },
-    ]
-  `);
+  expect(fragments).toHaveLength(1);
+
+  expect(fragments[0]!.id).toBe(
+    "log_115511_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64_null_null_null_null_0",
+  );
 });
 
-test("getLogFilterFragments builds id with multiple factories", () => {
+test("getLogFilterFragmentIds builds id with multiple factories", () => {
   const factory = buildLogFactory({
     address: ["0xa", "0xb"],
     event: llamaFactoryEventAbiItem,
@@ -361,7 +145,7 @@ test("getLogFilterFragments builds id with multiple factories", () => {
     chainId: 1,
   });
 
-  const fragments = getLogFilterFragments({
+  const fragments = getLogFilterFragmentIds({
     type: "log",
     chainId: 1,
     topic0: null,
@@ -372,48 +156,12 @@ test("getLogFilterFragments builds id with multiple factories", () => {
     include: [],
   });
 
-  expect(fragments).toMatchInlineSnapshot(`
-    [
-      {
-        "adjacentIds": [
-          "log_1_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64_null_null_null_null_0",
-          "log_1_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": {
-            "address": "0xa",
-            "childAddressLocation": "offset64",
-            "eventSelector": "0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599",
-          },
-          "chainId": 1,
-          "includeTransactionReceipts": false,
-          "topic0": null,
-          "topic1": null,
-          "topic2": null,
-          "topic3": null,
-          "type": "log",
-        },
-      },
-      {
-        "adjacentIds": [
-          "log_1_0xb_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64_null_null_null_null_0",
-          "log_1_0xb_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64_null_null_null_null_1",
-        ],
-        "fragment": {
-          "address": {
-            "address": "0xb",
-            "childAddressLocation": "offset64",
-            "eventSelector": "0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599",
-          },
-          "chainId": 1,
-          "includeTransactionReceipts": false,
-          "topic0": null,
-          "topic1": null,
-          "topic2": null,
-          "topic3": null,
-          "type": "log",
-        },
-      },
-    ]
-  `);
+  expect(fragments).toHaveLength(2);
+
+  expect(fragments[0]!.id).toBe(
+    "log_1_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64_null_null_null_null_0",
+  );
+  expect(fragments[1]!.id).toBe(
+    "log_1_0xb_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64_null_null_null_null_0",
+  );
 });