envio 3.5.0-alpha.1 → 3.5.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/index.d.ts +10 -6
  2. package/package.json +6 -6
  3. package/src/BatchProcessing.res +27 -0
  4. package/src/BatchProcessing.res.mjs +12 -0
  5. package/src/ChainFetching.res +5 -5
  6. package/src/ChainFetching.res.mjs +3 -4
  7. package/src/ChainState.res +48 -25
  8. package/src/ChainState.res.mjs +23 -29
  9. package/src/ChainState.resi +2 -6
  10. package/src/Change.res +3 -3
  11. package/src/Config.res +2 -6
  12. package/src/Config.res.mjs +2 -12
  13. package/src/Core.res +3 -0
  14. package/src/CrossChainState.res +33 -10
  15. package/src/CrossChainState.res.mjs +21 -6
  16. package/src/CrossChainState.resi +3 -0
  17. package/src/EntityId.res +15 -0
  18. package/src/EntityId.res.mjs +9 -0
  19. package/src/EventConfigBuilder.res +1 -58
  20. package/src/EventConfigBuilder.res.mjs +1 -33
  21. package/src/FetchState.res +510 -460
  22. package/src/FetchState.res.mjs +413 -428
  23. package/src/FinalizeBackfill.res +34 -0
  24. package/src/FinalizeBackfill.res.mjs +26 -0
  25. package/src/HandlerRegister.res +45 -0
  26. package/src/HandlerRegister.res.mjs +43 -0
  27. package/src/InMemoryStore.res +11 -4
  28. package/src/InMemoryTable.res +26 -24
  29. package/src/InMemoryTable.res.mjs +19 -18
  30. package/src/IndexerState.res +71 -2
  31. package/src/IndexerState.res.mjs +67 -3
  32. package/src/IndexerState.resi +5 -0
  33. package/src/Internal.res +17 -8
  34. package/src/LoadLayer.res +9 -1
  35. package/src/LoadLayer.res.mjs +1 -0
  36. package/src/LogSelection.res +2 -1
  37. package/src/Metrics.res +37 -6
  38. package/src/Metrics.res.mjs +5 -1
  39. package/src/Persistence.res +12 -1
  40. package/src/PgStorage.res +411 -57
  41. package/src/PgStorage.res.mjs +262 -41
  42. package/src/SimulateItems.res +2 -2
  43. package/src/TestIndexer.res +9 -5
  44. package/src/TestIndexer.res.mjs +5 -2
  45. package/src/UserContext.res +3 -3
  46. package/src/Writing.res +12 -2
  47. package/src/Writing.res.mjs +13 -2
  48. package/src/bindings/ClickHouse.res +5 -3
  49. package/src/bindings/ClickHouse.res.mjs +2 -5
  50. package/src/bindings/Vitest.res +22 -1
  51. package/src/bindings/Vitest.res.mjs +15 -0
  52. package/src/db/EntityFilter.res +0 -4
  53. package/src/db/EntityFilter.res.mjs +1 -8
  54. package/src/db/EntityHistory.res +16 -7
  55. package/src/db/EntityHistory.res.mjs +7 -6
  56. package/src/db/IndexRegistry.res +219 -0
  57. package/src/db/IndexRegistry.res.mjs +195 -0
  58. package/src/db/InternalTable.res +7 -0
  59. package/src/db/InternalTable.res.mjs +7 -0
  60. package/src/db/Table.res +53 -22
  61. package/src/db/Table.res.mjs +56 -18
  62. package/src/sources/AddressSet.res +48 -0
  63. package/src/sources/AddressSet.res.mjs +11 -0
  64. package/src/sources/AddressStore.res +152 -0
  65. package/src/sources/AddressStore.res.mjs +97 -0
  66. package/src/sources/EvmChain.res +3 -0
  67. package/src/sources/EvmChain.res.mjs +4 -2
  68. package/src/sources/EvmHyperSyncSource.res +6 -3
  69. package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
  70. package/src/sources/EvmRpcClient.res +6 -3
  71. package/src/sources/EvmRpcClient.res.mjs +3 -3
  72. package/src/sources/FuelHyperSync.res +4 -3
  73. package/src/sources/FuelHyperSync.res.mjs +3 -3
  74. package/src/sources/FuelHyperSync.resi +2 -1
  75. package/src/sources/FuelHyperSyncClient.res +12 -6
  76. package/src/sources/FuelHyperSyncClient.res.mjs +2 -2
  77. package/src/sources/FuelHyperSyncSource.res +7 -4
  78. package/src/sources/FuelHyperSyncSource.res.mjs +3 -3
  79. package/src/sources/HyperSync.res +6 -4
  80. package/src/sources/HyperSync.res.mjs +2 -3
  81. package/src/sources/HyperSync.resi +1 -1
  82. package/src/sources/HyperSyncClient.res +18 -9
  83. package/src/sources/HyperSyncClient.res.mjs +4 -4
  84. package/src/sources/RpcSource.res +15 -10
  85. package/src/sources/RpcSource.res.mjs +3 -4
  86. package/src/sources/SimulateSource.res +26 -11
  87. package/src/sources/SimulateSource.res.mjs +13 -5
  88. package/src/sources/Source.res +4 -2
  89. package/src/sources/SourceManager.res +2 -3
  90. package/src/sources/SourceManager.res.mjs +2 -3
  91. package/src/sources/Svm.res +1 -2
  92. package/src/sources/Svm.res.mjs +1 -1
  93. package/src/sources/SvmHyperSyncClient.res +12 -3
  94. package/src/sources/SvmHyperSyncClient.res.mjs +2 -2
  95. package/src/sources/SvmHyperSyncSource.res +10 -5
  96. package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
  97. package/src/IndexingAddresses.res +0 -151
  98. package/src/IndexingAddresses.res.mjs +0 -130
  99. package/src/IndexingAddresses.resi +0 -39
package/index.d.ts CHANGED
@@ -585,14 +585,18 @@ export type SvmOnSlotContext<Config extends IndexerConfigTypes = GlobalConfig> =
585
585
  BaseHandlerContext<Config, SvmChainIds<Config>>
586
586
  >;
587
587
 
588
+ /** The entity's `id` type. `ID!`/`String!` ids are `string`; `Int!` is `number`
589
+ * and `BigInt!` is `bigint`, so id-keyed operations accept the real scalar. */
590
+ type EntityId<Entity> = Entity extends { readonly id: infer Id } ? Id : string;
591
+
588
592
  /** Entity operations available in handler contexts. */
589
593
  type EntityOperations<Entity> = {
590
- readonly get: (id: string) => Promise<Entity | undefined>;
591
- readonly getOrThrow: (id: string, message?: string) => Promise<Entity>;
594
+ readonly get: (id: EntityId<Entity>) => Promise<Entity | undefined>;
595
+ readonly getOrThrow: (id: EntityId<Entity>, message?: string) => Promise<Entity>;
592
596
  readonly getWhere: (filter: GetWhereFilter<Entity>) => Promise<Entity[]>;
593
597
  readonly getOrCreate: (entity: Entity) => Promise<Entity>;
594
598
  readonly set: (entity: Entity) => void;
595
- readonly deleteUnsafe: (id: string) => void;
599
+ readonly deleteUnsafe: (id: EntityId<Entity>) => void;
596
600
  };
597
601
 
598
602
  /** Contract registration handle. */
@@ -1510,7 +1514,7 @@ type EntityChangeValue<Entity> = {
1510
1514
  /** Entities that were created or updated. */
1511
1515
  readonly sets?: readonly Entity[];
1512
1516
  /** IDs of entities that were deleted. */
1513
- readonly deleted?: readonly string[];
1517
+ readonly deleted?: readonly EntityId<Entity>[];
1514
1518
  };
1515
1519
 
1516
1520
  /** A dynamic contract address registration. */
@@ -1528,9 +1532,9 @@ type ConfigEntities<Config extends IndexerConfigTypes = GlobalConfig> =
1528
1532
  /** Entity operations available on test indexer for direct entity manipulation. */
1529
1533
  type TestIndexerEntityOperations<Entity> = {
1530
1534
  /** Get an entity by ID. Returns undefined if not found. */
1531
- readonly get: (id: string) => Promise<Entity | undefined>;
1535
+ readonly get: (id: EntityId<Entity>) => Promise<Entity | undefined>;
1532
1536
  /** Get an entity by ID or throw if not found. */
1533
- readonly getOrThrow: (id: string, message?: string) => Promise<Entity>;
1537
+ readonly getOrThrow: (id: EntityId<Entity>, message?: string) => Promise<Entity>;
1534
1538
  /** Get all entities. */
1535
1539
  readonly getAll: () => Promise<Entity[]>;
1536
1540
  /** Set (create or update) an entity. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.5.0-alpha.1",
3
+ "version": "3.5.0-alpha.3",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -69,10 +69,10 @@
69
69
  "tsx": "4.21.0"
70
70
  },
71
71
  "optionalDependencies": {
72
- "envio-linux-x64": "3.5.0-alpha.1",
73
- "envio-linux-x64-musl": "3.5.0-alpha.1",
74
- "envio-linux-arm64": "3.5.0-alpha.1",
75
- "envio-darwin-x64": "3.5.0-alpha.1",
76
- "envio-darwin-arm64": "3.5.0-alpha.1"
72
+ "envio-linux-x64": "3.5.0-alpha.3",
73
+ "envio-linux-x64-musl": "3.5.0-alpha.3",
74
+ "envio-linux-arm64": "3.5.0-alpha.3",
75
+ "envio-darwin-x64": "3.5.0-alpha.3",
76
+ "envio-darwin-arm64": "3.5.0-alpha.3"
77
77
  }
78
78
  }
@@ -45,6 +45,19 @@ let rec startProcessing = async (state: IndexerState.t, ~scheduleFetch, ~schedul
45
45
  // Hand off now that no batch is in flight.
46
46
  if state->IndexerState.isResolvingReorg {
47
47
  scheduleRollback()
48
+ } else if (
49
+ !(state->IndexerState.isStopped) &&
50
+ // Only a genuine fetch stall when some chain still has blocks to bring in.
51
+ // If every chain has buffered up to its known head, the loop is idling at
52
+ // the tip waiting for new blocks, not bottlenecked on fetch.
53
+ !(
54
+ state
55
+ ->IndexerState.chainStates
56
+ ->Dict.valuesToArray
57
+ ->Array.every(ChainState.isFetchingAtHead)
58
+ )
59
+ ) {
60
+ state->IndexerState.markProcessingStalledOnFetch
48
61
  }
49
62
  }
50
63
  }
@@ -91,6 +104,13 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
91
104
  // When resuming from persisted state, all events may already be processed.
92
105
  if EventProcessing.allChainsEventsProcessedToEndblock(state->IndexerState.chainStates) {
93
106
  Logging.info("All chains are caught up to end blocks.")
107
+ // A run that has nothing left to process still owes the schema its
108
+ // deferred indexes — otherwise a resume of a backfill that died right
109
+ // before finalizing would report ready without them.
110
+ state->IndexerState.markCaughtUp
111
+ if state->IndexerState.isFinalizingIndexes {
112
+ await FinalizeBackfill.run(state)
113
+ }
94
114
  if !(state->IndexerState.keepProcessAlive) {
95
115
  await ExitOnCaughtUp.run(state)
96
116
  }
@@ -143,6 +163,13 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
143
163
  state->IndexerState.clearRollback
144
164
  state->IndexerState.applyBatchProgress(~batch)
145
165
 
166
+ // Backfilling → FinalizingIndexes → Ready. Awaiting here holds the
167
+ // processing loop for the whole finalize, which is what pauses
168
+ // processing while the indexes are built.
169
+ if state->IndexerState.isFinalizingIndexes {
170
+ await FinalizeBackfill.run(state)
171
+ }
172
+
146
173
  if !isRealtimeBeforeUpdate && state->IndexerState.isRealtime {
147
174
  // Catching up just flipped the chain to realtime, which changes the
148
175
  // active source for waitForNewBlock (eg sync -> live). The waiter that
@@ -9,6 +9,7 @@ import * as ErrorHandling from "./ErrorHandling.res.mjs";
9
9
  import * as InMemoryStore from "./InMemoryStore.res.mjs";
10
10
  import * as ExitOnCaughtUp from "./ExitOnCaughtUp.res.mjs";
11
11
  import * as EventProcessing from "./EventProcessing.res.mjs";
12
+ import * as FinalizeBackfill from "./FinalizeBackfill.res.mjs";
12
13
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
13
14
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
14
15
  import * as SimulateDeadInputTracker from "./SimulateDeadInputTracker.res.mjs";
@@ -38,6 +39,10 @@ async function processNextBatch(state, scheduleFetch) {
38
39
  }
39
40
  if (EventProcessing.allChainsEventsProcessedToEndblock(IndexerState.chainStates(state))) {
40
41
  Logging.info("All chains are caught up to end blocks.");
42
+ IndexerState.markCaughtUp(state);
43
+ if (IndexerState.isFinalizingIndexes(state)) {
44
+ await FinalizeBackfill.run(state);
45
+ }
41
46
  if (!IndexerState.keepProcessAlive(state)) {
42
47
  return await ExitOnCaughtUp.run(state);
43
48
  } else {
@@ -64,6 +69,9 @@ async function processNextBatch(state, scheduleFetch) {
64
69
  }
65
70
  IndexerState.clearRollback(state);
66
71
  IndexerState.applyBatchProgress(state, batch);
72
+ if (IndexerState.isFinalizingIndexes(state)) {
73
+ await FinalizeBackfill.run(state);
74
+ }
67
75
  if (!isRealtimeBeforeUpdate && IndexerState.isRealtime(state)) {
68
76
  IndexerState.invalidateInflight(state);
69
77
  scheduleFetch();
@@ -108,6 +116,10 @@ async function startProcessing(state, scheduleFetch, scheduleRollback) {
108
116
  IndexerState.endProcessing(state);
109
117
  if (IndexerState.isResolvingReorg(state)) {
110
118
  return scheduleRollback();
119
+ } else if (!IndexerState.isStopped(state) && !Object.values(IndexerState.chainStates(state)).every(ChainState.isFetchingAtHead)) {
120
+ return IndexerState.markProcessingStalledOnFetch(state);
121
+ } else {
122
+ return;
111
123
  }
112
124
  }
113
125
 
@@ -209,11 +209,11 @@ let rec onQueryResponse = async (
209
209
  // kick (eg from the processing loop quiescing) collapses into this one.
210
210
  scheduleRollback()
211
211
  | None =>
212
- // Drop over-fetched events (a merged partition returning an address before
213
- // its effectiveStartBlock, or a wildcard param referencing an address
214
- // registered after the log's block) before contract registration, so they
215
- // neither spawn dynamic contracts nor enter the buffer.
216
- let newItems = chainState->ChainState.filterByClientAddress(parsedQueueItems)
212
+ // Over-fetched events (a merged partition returning an address before its
213
+ // effectiveStartBlock, or a wildcard param referencing an address
214
+ // registered after the log's block) are already dropped by the source's
215
+ // native address gate, so everything here is indexable.
216
+ let newItems = parsedQueueItems
217
217
  let itemsWithContractRegister = []
218
218
  for idx in 0 to newItems->Array.length - 1 {
219
219
  let item = newItems->Array.getUnsafe(idx)
@@ -145,17 +145,16 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
145
145
  IndexerState.beginReorg(state, chain, rollbackWithReorgDetectedBlockNumber);
146
146
  return scheduleRollback();
147
147
  }
148
- let newItems = ChainState.filterByClientAddress(chainState, parsedQueueItems);
149
148
  let itemsWithContractRegister = [];
150
- for (let idx = 0, idx_finish = newItems.length; idx < idx_finish; ++idx) {
151
- let item = newItems[idx];
149
+ for (let idx = 0, idx_finish = parsedQueueItems.length; idx < idx_finish; ++idx) {
150
+ let item = parsedQueueItems[idx];
152
151
  if (item.onEventRegistration.contractRegister !== undefined) {
153
152
  itemsWithContractRegister.push(item);
154
153
  }
155
154
  }
156
155
  let proceed = newItemsWithDcs => {
157
156
  if (!IndexerState.isStale(state, stateId)) {
158
- applyQueryResponse(state, chain, newItems, newItemsWithDcs, knownHeight, {
157
+ applyQueryResponse(state, chain, parsedQueueItems, newItemsWithDcs, knownHeight, {
159
158
  blockNumber: latestFetchedBlockNumber,
160
159
  blockTimestamp: latestFetchedBlockTimestamp
161
160
  }, query, transactionStore, blockStore);
@@ -7,9 +7,10 @@ type t = {
7
7
  // The registrations used to build this chain's sources and route native items.
8
8
  onEventRegistrations: array<Internal.onEventRegistration>,
9
9
  mutable fetchState: FetchState.t,
10
- // The chain-wide address index. Not `mutable`: the dict is mutated in place by
11
- // register/rollback, so the reference is stable across fetchState versions.
12
- indexingAddresses: IndexingAddresses.t,
10
+ // The chain-wide address index, kept in Rust. Not `mutable`: registration and
11
+ // rollback mutate it in place, so the handle is stable across fetchState
12
+ // versions — and it's the same handle this chain's source clients hold.
13
+ addressStore: AddressStore.t,
13
14
  sourceManager: SourceManager.t,
14
15
  chainConfig: Config.chain,
15
16
  mutable isProgressAtHead: bool,
@@ -83,7 +84,7 @@ let make = (
83
84
  ~chainConfig: Config.chain,
84
85
  ~fetchState: FetchState.t,
85
86
  ~onEventRegistrations=[],
86
- ~indexingAddresses: IndexingAddresses.t,
87
+ ~addressStore: AddressStore.t,
87
88
  ~sourceManager: SourceManager.t,
88
89
  ~reorgDetection: ReorgDetection.t,
89
90
  ~committedProgressBlockNumber: int,
@@ -102,7 +103,7 @@ let make = (
102
103
  logger,
103
104
  onEventRegistrations,
104
105
  fetchState,
105
- indexingAddresses,
106
+ addressStore,
106
107
  sourceManager,
107
108
  chainConfig,
108
109
  isProgressAtHead,
@@ -146,6 +147,7 @@ let makeInternal = (
146
147
  ~reorgCheckpoints: array<Internal.reorgCheckpoint>,
147
148
  ~maxReorgDepth,
148
149
  ~knownHeight=0,
150
+ ~isResumed=false,
149
151
  ~reducedPollingInterval=?,
150
152
  ): t => {
151
153
  // Handler binding + `where`-derived fetch state, and onBlock registrations,
@@ -166,12 +168,18 @@ let makeInternal = (
166
168
  }
167
169
  })
168
170
 
169
- let contractConfigs = IndexingAddresses.makeContractConfigs(~onEventRegistrations)
170
- let indexingAddressIndex = IndexingAddresses.make(~contractConfigs, ~addresses=indexingAddresses)
171
+ let lowercaseAddresses = config.lowercaseAddresses
172
+ // Created before the fetch state and the sources: `make` registers this
173
+ // chain's addresses into it, and every source client holds the same handle.
174
+ let addressStore = AddressStore.make(
175
+ ~ecosystem=config.ecosystem.name,
176
+ ~shouldChecksum=!lowercaseAddresses,
177
+ ~contracts=AddressStore.contractsOf(~onEventRegistrations),
178
+ )
171
179
 
172
180
  let fetchState = FetchState.make(
173
181
  ~maxAddrInPartition=config.maxAddrInPartition,
174
- ~contractConfigs,
182
+ ~addressStore,
175
183
  ~addresses=indexingAddresses,
176
184
  ~progressBlockNumber,
177
185
  ~startBlock,
@@ -187,12 +195,16 @@ let makeInternal = (
187
195
  ),
188
196
  ~onBlockRegistrations,
189
197
  ~firstEventBlock,
190
- // Only EVM sources (HyperSync + RPC) honor client-side address filtering so
191
- // far, so switching contracts to it is gated to EVM chains.
198
+ // Fuel and SVM route through the address store like EVM does, so the
199
+ // client-side path works for them but their address counts are nowhere
200
+ // near the threshold, so switching would only trade a server-side filter
201
+ // that costs nothing today for an over-fetch. Keep them server-side until a
202
+ // chain actually needs it.
192
203
  ~clientFilterAddressThreshold=switch config.ecosystem.name {
193
204
  | Evm => Some(config.clientFilterAddressThreshold)
194
205
  | Fuel | Svm => None
195
206
  },
207
+ ~isResumed,
196
208
  )
197
209
 
198
210
  let chainReorgCheckpoints = reorgCheckpoints->Array.filterMap(reorgCheckpoint => {
@@ -205,7 +217,6 @@ let makeInternal = (
205
217
 
206
218
  // Create sources lazily here - this is where API token validation happens
207
219
  let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
208
- let lowercaseAddresses = config.lowercaseAddresses
209
220
  let sources = switch chainConfig.sourceConfig {
210
221
  | Config.EvmSourceConfig({hypersync, rpcs}) =>
211
222
  let evmRpcs: array<EvmChain.rpc> = rpcs->Array.map((rpc): EvmChain.rpc => {
@@ -228,6 +239,7 @@ let makeInternal = (
228
239
  ~hyperSync=hypersync,
229
240
  ~rpcs=evmRpcs,
230
241
  ~lowercaseAddresses,
242
+ ~addressStore,
231
243
  )
232
244
  | Config.FuelSourceConfig({hypersync}) => [
233
245
  FuelHyperSyncSource.make({
@@ -235,6 +247,7 @@ let makeInternal = (
235
247
  endpointUrl: hypersync,
236
248
  apiToken: Env.envioApiToken,
237
249
  onEventRegistrations,
250
+ addressStore,
238
251
  }),
239
252
  ]
240
253
  | Config.SvmSourceConfig({hypersync, rpc}) =>
@@ -255,6 +268,7 @@ let makeInternal = (
255
268
  apiToken,
256
269
  onEventRegistrations,
257
270
  clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
271
+ addressStore,
258
272
  }),
259
273
  ]
260
274
  }
@@ -274,7 +288,7 @@ let makeInternal = (
274
288
  ~chainConfig,
275
289
  ~fetchState,
276
290
  ~onEventRegistrations,
277
- ~indexingAddresses=indexingAddressIndex,
291
+ ~addressStore,
278
292
  ~sourceManager=SourceManager.make(~sources, ~isRealtime, ~reducedPollingInterval?),
279
293
  ~reorgDetection=ReorgDetection.make(
280
294
  ~chainReorgCheckpoints,
@@ -371,6 +385,7 @@ let makeFromDbState = (
371
385
  ~isInReorgThreshold,
372
386
  ~isRealtime,
373
387
  ~knownHeight=resumedChainState.sourceBlockNumber,
388
+ ~isResumed=true,
374
389
  ~reducedPollingInterval?,
375
390
  )
376
391
  }
@@ -415,7 +430,7 @@ let setRollbackTargetBlock = (cs: t, ~blockNumber) => cs.rollbackTargetBlock = S
415
430
  // rather than reaching into it.
416
431
  let knownHeight = (cs: t) => cs.fetchState.knownHeight
417
432
  let contractAddresses = (cs: t, ~contractName) =>
418
- cs.indexingAddresses->IndexingAddresses.getContractAddresses(~contractName)
433
+ cs.addressStore->AddressStore.contractAddresses(contractName)
419
434
  let bufferSize = (cs: t) => cs.fetchState->FetchState.bufferSize
420
435
  let bufferReadyCount = (cs: t) => cs.fetchState->FetchState.bufferReadyCount
421
436
  let getProgressPercentage = (cs: t) => cs.fetchState->FetchState.getProgressPercentage
@@ -706,6 +721,9 @@ let groupBatchItems = (items: array<Internal.item>, ~includeBlocks: bool): (
706
721
  }
707
722
  }
708
723
  }
724
+ // onBlock items build their block from the handler's own block number, not
725
+ // from the stores — which is what lets the sources keep only the blocks and
726
+ // transactions an event item references.
709
727
  | Internal.Block(_) => ()
710
728
  }
711
729
  )
@@ -802,15 +820,12 @@ let materializePageItems = async (
802
820
  ))
803
821
  }
804
822
 
805
- let filterByClientAddress = (cs: t, items: array<Internal.item>): array<Internal.item> =>
806
- items->FetchState.filterByClientAddress(~indexingAddresses=cs.indexingAddresses)
807
-
808
823
  let handleQueryResult = (
809
824
  cs: t,
810
825
  ~query: FetchState.query,
811
826
  ~newItems,
812
827
  ~newItemsWithDcs,
813
- ~latestFetchedBlock,
828
+ ~latestFetchedBlock: FetchState.blockNumberAndTimestamp,
814
829
  ~knownHeight,
815
830
  ~transactionStore as txPage: option<TransactionStore.t>,
816
831
  ~blockStore as blockPage: option<BlockStore.t>,
@@ -830,7 +845,12 @@ let handleQueryResult = (
830
845
  | [] => cs.fetchState
831
846
  | _ =>
832
847
  cs.fetchState->FetchState.registerDynamicContracts(
833
- ~indexingAddresses=cs.indexingAddresses,
848
+ ~addressStore=cs.addressStore,
849
+ // This response is applied below, after the addresses land. It was routed
850
+ // before they existed, so whatever it claims has to be inside the
851
+ // catch-up range — and an unbounded query can reach past the height that
852
+ // was known when it went out.
853
+ ~claimCeiling=Pervasives.max(knownHeight, latestFetchedBlock.blockNumber),
834
854
  newItemsWithDcs,
835
855
  )
836
856
  }
@@ -906,7 +926,7 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
906
926
  numBatchesFetched: 0,
907
927
  startBlock: cs.fetchState.startBlock,
908
928
  endBlock: cs.fetchState.endBlock,
909
- numAddresses: cs.indexingAddresses->IndexingAddresses.size,
929
+ numAddresses: cs.addressStore->AddressStore.size,
910
930
  isReady: cs->isReady,
911
931
  sourceBlockNumber: cs.fetchState.knownHeight,
912
932
  progressBlockNumber: cs.committedProgressBlockNumber,
@@ -1065,11 +1085,14 @@ let applyBatchProgress = (cs: t, ~batch: Batch.t, ~blockTimestampName: string) =
1065
1085
  }
1066
1086
 
1067
1087
  // Mark the chain caught up to head/endblock. Called by CrossChainState only once
1068
- // every chain in the indexer is caught up, so no chain flips to ready while
1069
- // another is still backfilling. Sticky: a chain stays ready once set.
1070
- let markReady = (cs: t) =>
1088
+ // every chain in the indexer is caught up and the deferred schema indexes are
1089
+ // committed, so no chain flips to ready while another is still backfilling or
1090
+ // while an index the schema promises is still missing. `readyAt` is the
1091
+ // timestamp already committed to `envio_chains.ready_at` in that same
1092
+ // transaction. Sticky: a chain stays ready once set.
1093
+ let markReady = (cs: t, ~readyAt) =>
1071
1094
  if !(cs->isReady) {
1072
- cs.timestampCaughtUpToHeadOrEndblock = Date.make()->Some
1095
+ cs.timestampCaughtUpToHeadOrEndblock = Some(readyAt)
1073
1096
  }
1074
1097
 
1075
1098
  // Roll a chain back to a reorg target. With a progress diff, restore fetch/
@@ -1111,7 +1134,7 @@ let rollback = (
1111
1134
  }
1112
1135
  cs.fetchState =
1113
1136
  cs.fetchState->FetchState.rollback(
1114
- ~indexingAddresses=cs.indexingAddresses,
1137
+ ~addressStore=cs.addressStore,
1115
1138
  ~targetBlockNumber=newProgressBlockNumber,
1116
1139
  )
1117
1140
  cs.transactionStore->TransactionStore.rollback(newProgressBlockNumber)
@@ -1127,7 +1150,7 @@ let rollback = (
1127
1150
  )
1128
1151
  cs.fetchState =
1129
1152
  cs.fetchState->FetchState.rollback(
1130
- ~indexingAddresses=cs.indexingAddresses,
1153
+ ~addressStore=cs.addressStore,
1131
1154
  ~targetBlockNumber=rollbackTargetBlockNumber,
1132
1155
  )
1133
1156
  cs.transactionStore->TransactionStore.rollback(rollbackTargetBlockNumber)
@@ -11,6 +11,7 @@ import * as FieldMask from "./sources/FieldMask.res.mjs";
11
11
  import * as BlockStore from "./sources/BlockStore.res.mjs";
12
12
  import * as FetchState from "./FetchState.res.mjs";
13
13
  import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
14
+ import * as AddressStore from "./sources/AddressStore.res.mjs";
14
15
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
15
16
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
16
17
  import * as SourceManager from "./sources/SourceManager.res.mjs";
@@ -20,7 +21,6 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
20
21
  import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
21
22
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
22
23
  import * as TransactionStore from "./sources/TransactionStore.res.mjs";
23
- import * as IndexingAddresses from "./IndexingAddresses.res.mjs";
24
24
  import * as SvmHyperSyncSource from "./sources/SvmHyperSyncSource.res.mjs";
25
25
  import * as FuelHyperSyncSource from "./sources/FuelHyperSyncSource.res.mjs";
26
26
  import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
@@ -47,7 +47,7 @@ function validateOnEventRegistrations(chainId, registrations) {
47
47
  });
48
48
  }
49
49
 
50
- function make(chainConfig, fetchState, onEventRegistrationsOpt, indexingAddresses, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, logger) {
50
+ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, logger) {
51
51
  let onEventRegistrations = onEventRegistrationsOpt !== undefined ? onEventRegistrationsOpt : [];
52
52
  let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
53
53
  let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
@@ -62,7 +62,7 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, indexingAddresse
62
62
  logger: logger,
63
63
  onEventRegistrations: onEventRegistrations,
64
64
  fetchState: fetchState,
65
- indexingAddresses: indexingAddresses,
65
+ addressStore: addressStore,
66
66
  sourceManager: sourceManager,
67
67
  chainConfig: chainConfig,
68
68
  isProgressAtHead: isProgressAtHead,
@@ -89,9 +89,10 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, indexingAddresse
89
89
  };
90
90
  }
91
91
 
92
- function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firstEventBlockOpt, progressBlockNumber, config, registrationsByChainId, logger, timestampCaughtUpToHeadOrEndblock, numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, maxReorgDepth, knownHeightOpt, reducedPollingInterval) {
92
+ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firstEventBlockOpt, progressBlockNumber, config, registrationsByChainId, logger, timestampCaughtUpToHeadOrEndblock, numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, maxReorgDepth, knownHeightOpt, isResumedOpt, reducedPollingInterval) {
93
93
  let firstEventBlock = firstEventBlockOpt !== undefined ? Primitive_option.valFromOption(firstEventBlockOpt) : undefined;
94
94
  let knownHeight = knownHeightOpt !== undefined ? knownHeightOpt : 0;
95
+ let isResumed = isResumedOpt !== undefined ? isResumedOpt : false;
95
96
  let match = Stdlib_Option.getOr(registrationsByChainId[chainConfig.id.toString()], {
96
97
  onEventRegistrations: [],
97
98
  onBlockRegistrations: []
@@ -103,8 +104,8 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
103
104
  return Stdlib_JsError.throwWithMessage(`The start block for contract "` + contract.name + `" is less than the chain start block. This is not supported yet.`);
104
105
  }
105
106
  });
106
- let contractConfigs = IndexingAddresses.makeContractConfigs(onEventRegistrations);
107
- let indexingAddressIndex = IndexingAddresses.make(contractConfigs, indexingAddresses);
107
+ let lowercaseAddresses = config.lowercaseAddresses;
108
+ let addressStore = AddressStore.make(config.ecosystem.name, !lowercaseAddresses, AddressStore.contractsOf(onEventRegistrations));
108
109
  let match$1 = config.ecosystem.name;
109
110
  let tmp;
110
111
  switch (match$1) {
@@ -116,14 +117,13 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
116
117
  tmp = undefined;
117
118
  break;
118
119
  }
119
- let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, contractConfigs, indexingAddresses, config.maxAddrInPartition, chainConfig.id, (config.batchSize << 1), knownHeight, progressBlockNumber, match.onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : chainConfig.maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp));
120
+ let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, indexingAddresses, config.maxAddrInPartition, chainConfig.id, (config.batchSize << 1), knownHeight, progressBlockNumber, match.onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : chainConfig.maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed);
120
121
  let chainReorgCheckpoints = Stdlib_Array.filterMap(reorgCheckpoints, reorgCheckpoint => {
121
122
  if (reorgCheckpoint.chain_id === chainConfig.id) {
122
123
  return reorgCheckpoint;
123
124
  }
124
125
  });
125
126
  let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
126
- let lowercaseAddresses = config.lowercaseAddresses;
127
127
  let sources = chainConfig.sourceConfig;
128
128
  let sources$1;
129
129
  switch (sources.TAG) {
@@ -140,14 +140,15 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
140
140
  headers: headers
141
141
  };
142
142
  });
143
- sources$1 = EvmChain.makeSources(chain, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses);
143
+ sources$1 = EvmChain.makeSources(chain, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore);
144
144
  break;
145
145
  case "FuelSourceConfig" :
146
146
  sources$1 = [FuelHyperSyncSource.make({
147
147
  chain: chain,
148
148
  endpointUrl: sources.hypersync,
149
149
  apiToken: Env.envioApiToken,
150
- onEventRegistrations: onEventRegistrations
150
+ onEventRegistrations: onEventRegistrations,
151
+ addressStore: addressStore
151
152
  })];
152
153
  break;
153
154
  case "SvmSourceConfig" :
@@ -158,7 +159,8 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
158
159
  endpointUrl: hypersync,
159
160
  apiToken: Env.envioApiToken,
160
161
  onEventRegistrations: onEventRegistrations,
161
- clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis
162
+ clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
163
+ addressStore: addressStore
162
164
  })] : (
163
165
  rpc !== undefined ? [Svm.makeRPCSource(chain, rpc, undefined)] : Stdlib_JsError.throwWithMessage(`Chain ` + chain.toString() + ` has no SVM data source`)
164
166
  );
@@ -169,14 +171,14 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
169
171
  }
170
172
  let firstEventBlock$1 = fetchState.firstEventBlock;
171
173
  let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
172
- return make(chainConfig, fetchState, onEventRegistrations, indexingAddressIndex, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), config.reorgThresholdReadyTolerance, logger);
174
+ return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), config.reorgThresholdReadyTolerance, logger);
173
175
  }
174
176
 
175
177
  function makeFromConfig(chainConfig, config, registrationsByChainId, knownHeight) {
176
178
  let logger = Logging.createChild({
177
179
  chainId: chainConfig.id
178
180
  });
179
- return makeInternal(chainConfig, configAddresses(chainConfig), chainConfig.startBlock, chainConfig.endBlock, undefined, -1, config, registrationsByChainId, logger, undefined, 0, false, false, [], chainConfig.maxReorgDepth, knownHeight, undefined);
181
+ return makeInternal(chainConfig, configAddresses(chainConfig), chainConfig.startBlock, chainConfig.endBlock, undefined, -1, config, registrationsByChainId, logger, undefined, 0, false, false, [], chainConfig.maxReorgDepth, knownHeight, undefined, undefined);
180
182
  }
181
183
 
182
184
  function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInReorgThreshold, isRealtime, config, registrationsByChainId, reducedPollingInterval) {
@@ -185,7 +187,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
185
187
  chainId: chainId
186
188
  });
187
189
  let progressBlockNumber = resumedChainState.progressBlockNumber >= 0 ? resumedChainState.progressBlockNumber : resumedChainState.startBlock - 1 | 0;
188
- return makeInternal(chainConfig, resumedChainState.indexingAddresses, resumedChainState.startBlock, resumedChainState.endBlock, Primitive_option.some(resumedChainState.firstEventBlockNumber), progressBlockNumber, config, registrationsByChainId, logger, Env.updateSyncTimeOnRestart ? undefined : resumedChainState.timestampCaughtUpToHeadOrEndblock, resumedChainState.numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, resumedChainState.maxReorgDepth, resumedChainState.sourceBlockNumber, reducedPollingInterval);
190
+ return makeInternal(chainConfig, resumedChainState.indexingAddresses, resumedChainState.startBlock, resumedChainState.endBlock, Primitive_option.some(resumedChainState.firstEventBlockNumber), progressBlockNumber, config, registrationsByChainId, logger, Env.updateSyncTimeOnRestart ? undefined : resumedChainState.timestampCaughtUpToHeadOrEndblock, resumedChainState.numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, resumedChainState.maxReorgDepth, resumedChainState.sourceBlockNumber, true, reducedPollingInterval);
189
191
  }
190
192
 
191
193
  function logger(cs) {
@@ -250,7 +252,7 @@ function knownHeight(cs) {
250
252
  }
251
253
 
252
254
  function contractAddresses(cs, contractName) {
253
- return IndexingAddresses.getContractAddresses(cs.indexingAddresses, contractName);
255
+ return cs.addressStore.contractAddresses(contractName);
254
256
  }
255
257
 
256
258
  function bufferSize(cs) {
@@ -541,10 +543,6 @@ async function materializePageItems(items, transactionStore, blockStore, ecosyst
541
543
  ]);
542
544
  }
543
545
 
544
- function filterByClientAddress(cs, items) {
545
- return FetchState.filterByClientAddress(items, cs.indexingAddresses);
546
- }
547
-
548
546
  function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight, txPage, blockPage) {
549
547
  if (txPage !== undefined) {
550
548
  cs.transactionStore.merge(Primitive_option.valFromOption(txPage));
@@ -552,7 +550,7 @@ function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBl
552
550
  if (blockPage !== undefined) {
553
551
  cs.blockStore.merge(Primitive_option.valFromOption(blockPage));
554
552
  }
555
- let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.indexingAddresses, newItemsWithDcs) : cs.fetchState;
553
+ let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.addressStore, Primitive_int.max(knownHeight, latestFetchedBlock.blockNumber), newItemsWithDcs) : cs.fetchState;
556
554
  cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
557
555
  cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.itemsEst);
558
556
  }
@@ -587,7 +585,6 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
587
585
  startBlock: init.startBlock,
588
586
  endBlock: blockNumber,
589
587
  normalSelection: init.normalSelection,
590
- contractConfigs: init.contractConfigs,
591
588
  chainId: init.chainId,
592
589
  latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
593
590
  blockLag: init.blockLag,
@@ -606,7 +603,6 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
606
603
  startBlock: init$1.startBlock,
607
604
  endBlock: blockNumber,
608
605
  normalSelection: init$1.normalSelection,
609
- contractConfigs: init$1.contractConfigs,
610
606
  chainId: init$1.chainId,
611
607
  latestOnBlockBlockNumber: init$1.latestOnBlockBlockNumber,
612
608
  blockLag: init$1.blockLag,
@@ -645,7 +641,7 @@ function toMetrics(cs) {
645
641
  numBatchesFetched: 0,
646
642
  startBlock: cs.fetchState.startBlock,
647
643
  endBlock: cs.fetchState.endBlock,
648
- numAddresses: IndexingAddresses.size(cs.indexingAddresses),
644
+ numAddresses: cs.addressStore.size(),
649
645
  isReady: cs.timestampCaughtUpToHeadOrEndblock !== undefined,
650
646
  sourceBlockNumber: cs.fetchState.knownHeight,
651
647
  progressBlockNumber: cs.committedProgressBlockNumber,
@@ -715,7 +711,6 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
715
711
  startBlock: init.startBlock,
716
712
  endBlock: init.endBlock,
717
713
  normalSelection: init.normalSelection,
718
- contractConfigs: init.contractConfigs,
719
714
  chainId: init.chainId,
720
715
  latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
721
716
  blockLag: init.blockLag,
@@ -763,9 +758,9 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
763
758
  }
764
759
  }
765
760
 
766
- function markReady(cs) {
761
+ function markReady(cs, readyAt) {
767
762
  if (cs.timestampCaughtUpToHeadOrEndblock === undefined) {
768
- cs.timestampCaughtUpToHeadOrEndblock = new Date();
763
+ cs.timestampCaughtUpToHeadOrEndblock = readyAt;
769
764
  return;
770
765
  }
771
766
  }
@@ -774,7 +769,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
774
769
  if (newProgressBlockNumber === undefined) {
775
770
  if (isReorgChain) {
776
771
  cs.reorgDetection = ReorgDetection.rollbackToValidBlockNumber(cs.reorgDetection, rollbackTargetBlockNumber);
777
- cs.fetchState = FetchState.rollback(cs.fetchState, cs.indexingAddresses, rollbackTargetBlockNumber);
772
+ cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, rollbackTargetBlockNumber);
778
773
  cs.transactionStore.rollback(rollbackTargetBlockNumber);
779
774
  cs.blockStore.rollback(rollbackTargetBlockNumber);
780
775
  cs.committedProgressBlockNumber = Primitive_int.min(cs.committedProgressBlockNumber, rollbackTargetBlockNumber);
@@ -792,7 +787,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
792
787
  if (safeCheckpointTracking !== undefined) {
793
788
  cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
794
789
  }
795
- cs.fetchState = FetchState.rollback(cs.fetchState, cs.indexingAddresses, newProgressBlockNumber);
790
+ cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, newProgressBlockNumber);
796
791
  cs.transactionStore.rollback(newProgressBlockNumber);
797
792
  cs.blockStore.rollback(newProgressBlockNumber);
798
793
  cs.committedProgressBlockNumber = newProgressBlockNumber;
@@ -842,7 +837,6 @@ export {
842
837
  isReady,
843
838
  isFetchingAtHead,
844
839
  isAtHeadWithoutEndBlock,
845
- filterByClientAddress,
846
840
  handleQueryResult,
847
841
  materializeBatchItems,
848
842
  materializePageItems,