envio 3.5.0-alpha.0 → 3.5.0-alpha.2

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 (92) hide show
  1. package/index.d.ts +10 -6
  2. package/package.json +6 -6
  3. package/src/BatchProcessing.res +13 -0
  4. package/src/BatchProcessing.res.mjs +4 -0
  5. package/src/Bin.res +2 -2
  6. package/src/ChainFetching.res +5 -5
  7. package/src/ChainFetching.res.mjs +3 -4
  8. package/src/ChainState.res +44 -36
  9. package/src/ChainState.res.mjs +36 -31
  10. package/src/ChainState.resi +2 -12
  11. package/src/Change.res +3 -3
  12. package/src/Config.res +4 -4
  13. package/src/Config.res.mjs +1 -10
  14. package/src/Core.res +4 -35
  15. package/src/Core.res.mjs +1 -31
  16. package/src/CrossChainState.res +2 -20
  17. package/src/CrossChainState.res.mjs +2 -6
  18. package/src/EntityId.res +15 -0
  19. package/src/EntityId.res.mjs +9 -0
  20. package/src/Env.res +16 -0
  21. package/src/Env.res.mjs +6 -0
  22. package/src/EventConfigBuilder.res +1 -58
  23. package/src/EventConfigBuilder.res.mjs +1 -33
  24. package/src/FetchState.res +663 -484
  25. package/src/FetchState.res.mjs +491 -440
  26. package/src/HandlerRegister.res +45 -0
  27. package/src/HandlerRegister.res.mjs +43 -0
  28. package/src/InMemoryStore.res +11 -4
  29. package/src/InMemoryTable.res +12 -10
  30. package/src/InMemoryTable.res.mjs +6 -5
  31. package/src/IndexerState.res +59 -2
  32. package/src/IndexerState.res.mjs +48 -3
  33. package/src/IndexerState.resi +2 -0
  34. package/src/Internal.res +17 -8
  35. package/src/LogSelection.res +2 -1
  36. package/src/Metrics.res +37 -6
  37. package/src/Metrics.res.mjs +5 -1
  38. package/src/Persistence.res +1 -1
  39. package/src/PgStorage.res +55 -22
  40. package/src/PgStorage.res.mjs +25 -17
  41. package/src/SimulateItems.res +2 -2
  42. package/src/TestIndexer.res +6 -5
  43. package/src/TestIndexer.res.mjs +3 -2
  44. package/src/UserContext.res +3 -3
  45. package/src/Writing.res +12 -2
  46. package/src/Writing.res.mjs +13 -2
  47. package/src/bindings/ClickHouse.res +5 -3
  48. package/src/bindings/ClickHouse.res.mjs +2 -5
  49. package/src/bindings/Vitest.res +22 -1
  50. package/src/bindings/Vitest.res.mjs +15 -0
  51. package/src/db/EntityHistory.res +16 -7
  52. package/src/db/EntityHistory.res.mjs +7 -6
  53. package/src/db/Table.res +40 -9
  54. package/src/db/Table.res.mjs +44 -6
  55. package/src/sources/AddressSet.res +48 -0
  56. package/src/sources/AddressSet.res.mjs +11 -0
  57. package/src/sources/AddressStore.res +152 -0
  58. package/src/sources/AddressStore.res.mjs +97 -0
  59. package/src/sources/EvmChain.res +3 -0
  60. package/src/sources/EvmChain.res.mjs +4 -2
  61. package/src/sources/EvmHyperSyncSource.res +9 -9
  62. package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
  63. package/src/sources/EvmRpcClient.res +10 -3
  64. package/src/sources/EvmRpcClient.res.mjs +3 -3
  65. package/src/sources/FuelHyperSync.res +4 -3
  66. package/src/sources/FuelHyperSync.res.mjs +3 -3
  67. package/src/sources/FuelHyperSync.resi +2 -1
  68. package/src/sources/FuelHyperSyncClient.res +12 -6
  69. package/src/sources/FuelHyperSyncClient.res.mjs +2 -2
  70. package/src/sources/FuelHyperSyncSource.res +7 -4
  71. package/src/sources/FuelHyperSyncSource.res.mjs +3 -3
  72. package/src/sources/HyperSync.res +9 -5
  73. package/src/sources/HyperSync.res.mjs +3 -3
  74. package/src/sources/HyperSync.resi +3 -2
  75. package/src/sources/HyperSyncClient.res +24 -10
  76. package/src/sources/HyperSyncClient.res.mjs +4 -4
  77. package/src/sources/RpcSource.res +71 -67
  78. package/src/sources/RpcSource.res.mjs +4 -4
  79. package/src/sources/SimulateSource.res +26 -11
  80. package/src/sources/SimulateSource.res.mjs +13 -5
  81. package/src/sources/Source.res +8 -4
  82. package/src/sources/SourceManager.res +2 -3
  83. package/src/sources/SourceManager.res.mjs +2 -3
  84. package/src/sources/Svm.res +1 -2
  85. package/src/sources/Svm.res.mjs +1 -1
  86. package/src/sources/SvmHyperSyncClient.res +14 -4
  87. package/src/sources/SvmHyperSyncClient.res.mjs +2 -2
  88. package/src/sources/SvmHyperSyncSource.res +11 -6
  89. package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
  90. package/src/IndexingAddresses.res +0 -108
  91. package/src/IndexingAddresses.res.mjs +0 -101
  92. package/src/IndexingAddresses.resi +0 -34
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.0",
3
+ "version": "3.5.0-alpha.2",
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.0",
73
- "envio-linux-x64-musl": "3.5.0-alpha.0",
74
- "envio-linux-arm64": "3.5.0-alpha.0",
75
- "envio-darwin-x64": "3.5.0-alpha.0",
76
- "envio-darwin-arm64": "3.5.0-alpha.0"
72
+ "envio-linux-x64": "3.5.0-alpha.2",
73
+ "envio-linux-x64-musl": "3.5.0-alpha.2",
74
+ "envio-linux-arm64": "3.5.0-alpha.2",
75
+ "envio-darwin-x64": "3.5.0-alpha.2",
76
+ "envio-darwin-arm64": "3.5.0-alpha.2"
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
  }
@@ -108,6 +108,10 @@ async function startProcessing(state, scheduleFetch, scheduleRollback) {
108
108
  IndexerState.endProcessing(state);
109
109
  if (IndexerState.isResolvingReorg(state)) {
110
110
  return scheduleRollback();
111
+ } else if (!IndexerState.isStopped(state) && !Object.values(IndexerState.chainStates(state)).every(ChainState.isFetchingAtHead)) {
112
+ return IndexerState.markProcessingStalledOnFetch(state);
113
+ } else {
114
+ return;
111
115
  }
112
116
  }
113
117
 
package/src/Bin.res CHANGED
@@ -52,8 +52,8 @@ let applyEnv = (env: dict<JSON.t>) =>
52
52
  let run = async args => {
53
53
  try {
54
54
  switch (await Core.runCli(args))->Null.toOption {
55
- // Rust-only command (codegen / init / serve / stop / docker / metrics /
56
- // help / version / scripts) — nothing for JS to do, exit cleanly.
55
+ // Rust-only command (codegen / init / stop / docker / metrics / help /
56
+ // version / scripts) — nothing for JS to do, exit cleanly.
57
57
  | None => ()
58
58
  | Some(json) =>
59
59
  switch decodeCommand(json->JSON.parseOrThrow) {
@@ -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,
@@ -166,12 +167,18 @@ let makeInternal = (
166
167
  }
167
168
  })
168
169
 
169
- let contractConfigs = IndexingAddresses.makeContractConfigs(~onEventRegistrations)
170
- let indexingAddressIndex = IndexingAddresses.make(~contractConfigs, ~addresses=indexingAddresses)
170
+ let lowercaseAddresses = config.lowercaseAddresses
171
+ // Created before the fetch state and the sources: `make` registers this
172
+ // chain's addresses into it, and every source client holds the same handle.
173
+ let addressStore = AddressStore.make(
174
+ ~ecosystem=config.ecosystem.name,
175
+ ~shouldChecksum=!lowercaseAddresses,
176
+ ~contracts=AddressStore.contractsOf(~onEventRegistrations),
177
+ )
171
178
 
172
179
  let fetchState = FetchState.make(
173
180
  ~maxAddrInPartition=config.maxAddrInPartition,
174
- ~contractConfigs,
181
+ ~addressStore,
175
182
  ~addresses=indexingAddresses,
176
183
  ~progressBlockNumber,
177
184
  ~startBlock,
@@ -187,6 +194,15 @@ let makeInternal = (
187
194
  ),
188
195
  ~onBlockRegistrations,
189
196
  ~firstEventBlock,
197
+ // Fuel and SVM route through the address store like EVM does, so the
198
+ // client-side path works for them — but their address counts are nowhere
199
+ // near the threshold, so switching would only trade a server-side filter
200
+ // that costs nothing today for an over-fetch. Keep them server-side until a
201
+ // chain actually needs it.
202
+ ~clientFilterAddressThreshold=switch config.ecosystem.name {
203
+ | Evm => Some(config.clientFilterAddressThreshold)
204
+ | Fuel | Svm => None
205
+ },
190
206
  )
191
207
 
192
208
  let chainReorgCheckpoints = reorgCheckpoints->Array.filterMap(reorgCheckpoint => {
@@ -199,7 +215,6 @@ let makeInternal = (
199
215
 
200
216
  // Create sources lazily here - this is where API token validation happens
201
217
  let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
202
- let lowercaseAddresses = config.lowercaseAddresses
203
218
  let sources = switch chainConfig.sourceConfig {
204
219
  | Config.EvmSourceConfig({hypersync, rpcs}) =>
205
220
  let evmRpcs: array<EvmChain.rpc> = rpcs->Array.map((rpc): EvmChain.rpc => {
@@ -222,6 +237,7 @@ let makeInternal = (
222
237
  ~hyperSync=hypersync,
223
238
  ~rpcs=evmRpcs,
224
239
  ~lowercaseAddresses,
240
+ ~addressStore,
225
241
  )
226
242
  | Config.FuelSourceConfig({hypersync}) => [
227
243
  FuelHyperSyncSource.make({
@@ -229,6 +245,7 @@ let makeInternal = (
229
245
  endpointUrl: hypersync,
230
246
  apiToken: Env.envioApiToken,
231
247
  onEventRegistrations,
248
+ addressStore,
232
249
  }),
233
250
  ]
234
251
  | Config.SvmSourceConfig({hypersync, rpc}) =>
@@ -249,6 +266,7 @@ let makeInternal = (
249
266
  apiToken,
250
267
  onEventRegistrations,
251
268
  clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
269
+ addressStore,
252
270
  }),
253
271
  ]
254
272
  }
@@ -268,7 +286,7 @@ let makeInternal = (
268
286
  ~chainConfig,
269
287
  ~fetchState,
270
288
  ~onEventRegistrations,
271
- ~indexingAddresses=indexingAddressIndex,
289
+ ~addressStore,
272
290
  ~sourceManager=SourceManager.make(~sources, ~isRealtime, ~reducedPollingInterval?),
273
291
  ~reorgDetection=ReorgDetection.make(
274
292
  ~chainReorgCheckpoints,
@@ -409,7 +427,7 @@ let setRollbackTargetBlock = (cs: t, ~blockNumber) => cs.rollbackTargetBlock = S
409
427
  // rather than reaching into it.
410
428
  let knownHeight = (cs: t) => cs.fetchState.knownHeight
411
429
  let contractAddresses = (cs: t, ~contractName) =>
412
- cs.indexingAddresses->IndexingAddresses.getContractAddresses(~contractName)
430
+ cs.addressStore->AddressStore.contractAddresses(contractName)
413
431
  let bufferSize = (cs: t) => cs.fetchState->FetchState.bufferSize
414
432
  let bufferReadyCount = (cs: t) => cs.fetchState->FetchState.bufferReadyCount
415
433
  let getProgressPercentage = (cs: t) => cs.fetchState->FetchState.getProgressPercentage
@@ -541,13 +559,7 @@ let frontierProgress = (cs: t) =>
541
559
  // maxTargetBlock set to the most-behind chain's progress mapped onto this
542
560
  // chain, so a chain with budget can't run further ahead than the chain the
543
561
  // whole pool is prioritizing.
544
- let getNextQuery = (
545
- cs: t,
546
- ~chainTargetItems: float,
547
- ~chunkItemsMultiplier=1.,
548
- ~itemsTargetFloor=0,
549
- ~maxTargetBlock=?,
550
- ) => {
562
+ let getNextQuery = (cs: t, ~chainTargetItems: float, ~maxTargetBlock=?) => {
551
563
  let chainTargetBlock = cs->targetBlock(~chainTargetItems)
552
564
  let chainTargetBlock = switch maxTargetBlock {
553
565
  | Some(maxTargetBlock) => Pervasives.min(chainTargetBlock, maxTargetBlock)
@@ -561,10 +573,6 @@ let getNextQuery = (
561
573
  // of being held by an oversized probe.
562
574
  let chainTargetItems = switch cs->effectiveDensity {
563
575
  | Some(density) if density > 0. =>
564
- // No extra headroom here: the budget is reserved in honest itemsEst units,
565
- // and truncation safety lives in the itemsTarget server cap (sized with
566
- // chunkItemsMultiplier at query creation) — multiplying the budget cap too
567
- // would compound the two and hold budget away from other chains.
568
576
  let rangeCost =
569
577
  density *. (chainTargetBlock - cs.fetchState->FetchState.bufferBlockNumber)->Int.toFloat
570
578
  Pervasives.min(chainTargetItems, Math.ceil(rangeCost) +. cs.pendingBudget)
@@ -572,12 +580,7 @@ let getNextQuery = (
572
580
  // budget to the cold-chain cap, so it's used as-is.
573
581
  | _ => chainTargetItems
574
582
  }
575
- cs.fetchState->FetchState.getNextQuery(
576
- ~chainTargetBlock,
577
- ~chainTargetItems,
578
- ~chunkItemsMultiplier,
579
- ~itemsTargetFloor,
580
- )
583
+ cs.fetchState->FetchState.getNextQuery(~chainTargetBlock, ~chainTargetItems)
581
584
  }
582
585
 
583
586
  // Run a fetch tick for this chain against its sources, feeding the owned fetch
@@ -715,6 +718,9 @@ let groupBatchItems = (items: array<Internal.item>, ~includeBlocks: bool): (
715
718
  }
716
719
  }
717
720
  }
721
+ // onBlock items build their block from the handler's own block number, not
722
+ // from the stores — which is what lets the sources keep only the blocks and
723
+ // transactions an event item references.
718
724
  | Internal.Block(_) => ()
719
725
  }
720
726
  )
@@ -811,15 +817,12 @@ let materializePageItems = async (
811
817
  ))
812
818
  }
813
819
 
814
- let filterByClientAddress = (cs: t, items: array<Internal.item>): array<Internal.item> =>
815
- items->FetchState.filterByClientAddress(~indexingAddresses=cs.indexingAddresses)
816
-
817
820
  let handleQueryResult = (
818
821
  cs: t,
819
822
  ~query: FetchState.query,
820
823
  ~newItems,
821
824
  ~newItemsWithDcs,
822
- ~latestFetchedBlock,
825
+ ~latestFetchedBlock: FetchState.blockNumberAndTimestamp,
823
826
  ~knownHeight,
824
827
  ~transactionStore as txPage: option<TransactionStore.t>,
825
828
  ~blockStore as blockPage: option<BlockStore.t>,
@@ -839,7 +842,12 @@ let handleQueryResult = (
839
842
  | [] => cs.fetchState
840
843
  | _ =>
841
844
  cs.fetchState->FetchState.registerDynamicContracts(
842
- ~indexingAddresses=cs.indexingAddresses,
845
+ ~addressStore=cs.addressStore,
846
+ // This response is applied below, after the addresses land. It was routed
847
+ // before they existed, so whatever it claims has to be inside the
848
+ // catch-up range — and an unbounded query can reach past the height that
849
+ // was known when it went out.
850
+ ~claimCeiling=Pervasives.max(knownHeight, latestFetchedBlock.blockNumber),
843
851
  newItemsWithDcs,
844
852
  )
845
853
  }
@@ -915,7 +923,7 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
915
923
  numBatchesFetched: 0,
916
924
  startBlock: cs.fetchState.startBlock,
917
925
  endBlock: cs.fetchState.endBlock,
918
- numAddresses: cs.indexingAddresses->IndexingAddresses.size,
926
+ numAddresses: cs.addressStore->AddressStore.size,
919
927
  isReady: cs->isReady,
920
928
  sourceBlockNumber: cs.fetchState.knownHeight,
921
929
  progressBlockNumber: cs.committedProgressBlockNumber,
@@ -1120,7 +1128,7 @@ let rollback = (
1120
1128
  }
1121
1129
  cs.fetchState =
1122
1130
  cs.fetchState->FetchState.rollback(
1123
- ~indexingAddresses=cs.indexingAddresses,
1131
+ ~addressStore=cs.addressStore,
1124
1132
  ~targetBlockNumber=newProgressBlockNumber,
1125
1133
  )
1126
1134
  cs.transactionStore->TransactionStore.rollback(newProgressBlockNumber)
@@ -1136,7 +1144,7 @@ let rollback = (
1136
1144
  )
1137
1145
  cs.fetchState =
1138
1146
  cs.fetchState->FetchState.rollback(
1139
- ~indexingAddresses=cs.indexingAddresses,
1147
+ ~addressStore=cs.addressStore,
1140
1148
  ~targetBlockNumber=rollbackTargetBlockNumber,
1141
1149
  )
1142
1150
  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,
@@ -103,16 +103,26 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
103
103
  return Stdlib_JsError.throwWithMessage(`The start block for contract "` + contract.name + `" is less than the chain start block. This is not supported yet.`);
104
104
  }
105
105
  });
106
- let contractConfigs = IndexingAddresses.makeContractConfigs(onEventRegistrations);
107
- let indexingAddressIndex = IndexingAddresses.make(contractConfigs, indexingAddresses);
108
- 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));
106
+ let lowercaseAddresses = config.lowercaseAddresses;
107
+ let addressStore = AddressStore.make(config.ecosystem.name, !lowercaseAddresses, AddressStore.contractsOf(onEventRegistrations));
108
+ let match$1 = config.ecosystem.name;
109
+ let tmp;
110
+ switch (match$1) {
111
+ case "evm" :
112
+ tmp = config.clientFilterAddressThreshold;
113
+ break;
114
+ case "fuel" :
115
+ case "svm" :
116
+ tmp = undefined;
117
+ break;
118
+ }
119
+ 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));
109
120
  let chainReorgCheckpoints = Stdlib_Array.filterMap(reorgCheckpoints, reorgCheckpoint => {
110
121
  if (reorgCheckpoint.chain_id === chainConfig.id) {
111
122
  return reorgCheckpoint;
112
123
  }
113
124
  });
114
125
  let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
115
- let lowercaseAddresses = config.lowercaseAddresses;
116
126
  let sources = chainConfig.sourceConfig;
117
127
  let sources$1;
118
128
  switch (sources.TAG) {
@@ -129,14 +139,15 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
129
139
  headers: headers
130
140
  };
131
141
  });
132
- sources$1 = EvmChain.makeSources(chain, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses);
142
+ sources$1 = EvmChain.makeSources(chain, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore);
133
143
  break;
134
144
  case "FuelSourceConfig" :
135
145
  sources$1 = [FuelHyperSyncSource.make({
136
146
  chain: chain,
137
147
  endpointUrl: sources.hypersync,
138
148
  apiToken: Env.envioApiToken,
139
- onEventRegistrations: onEventRegistrations
149
+ onEventRegistrations: onEventRegistrations,
150
+ addressStore: addressStore
140
151
  })];
141
152
  break;
142
153
  case "SvmSourceConfig" :
@@ -147,7 +158,8 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
147
158
  endpointUrl: hypersync,
148
159
  apiToken: Env.envioApiToken,
149
160
  onEventRegistrations: onEventRegistrations,
150
- clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis
161
+ clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
162
+ addressStore: addressStore
151
163
  })] : (
152
164
  rpc !== undefined ? [Svm.makeRPCSource(chain, rpc, undefined)] : Stdlib_JsError.throwWithMessage(`Chain ` + chain.toString() + ` has no SVM data source`)
153
165
  );
@@ -158,7 +170,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
158
170
  }
159
171
  let firstEventBlock$1 = fetchState.firstEventBlock;
160
172
  let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
161
- 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);
173
+ 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);
162
174
  }
163
175
 
164
176
  function makeFromConfig(chainConfig, config, registrationsByChainId, knownHeight) {
@@ -239,7 +251,7 @@ function knownHeight(cs) {
239
251
  }
240
252
 
241
253
  function contractAddresses(cs, contractName) {
242
- return IndexingAddresses.getContractAddresses(cs.indexingAddresses, contractName);
254
+ return cs.addressStore.contractAddresses(contractName);
243
255
  }
244
256
 
245
257
  function bufferSize(cs) {
@@ -348,9 +360,7 @@ function frontierProgress(cs) {
348
360
  }
349
361
  }
350
362
 
351
- function getNextQuery(cs, chainTargetItems, chunkItemsMultiplierOpt, itemsTargetFloorOpt, maxTargetBlock) {
352
- let chunkItemsMultiplier = chunkItemsMultiplierOpt !== undefined ? chunkItemsMultiplierOpt : 1;
353
- let itemsTargetFloor = itemsTargetFloorOpt !== undefined ? itemsTargetFloorOpt : 0;
363
+ function getNextQuery(cs, chainTargetItems, maxTargetBlock) {
354
364
  let chainTargetBlock = targetBlock(cs, chainTargetItems);
355
365
  let chainTargetBlock$1 = maxTargetBlock !== undefined ? Primitive_int.min(chainTargetBlock, maxTargetBlock) : chainTargetBlock;
356
366
  let density = effectiveDensity(cs);
@@ -361,7 +371,7 @@ function getNextQuery(cs, chainTargetItems, chunkItemsMultiplierOpt, itemsTarget
361
371
  } else {
362
372
  chainTargetItems$1 = chainTargetItems;
363
373
  }
364
- return FetchState.getNextQuery(cs.fetchState, chainTargetBlock$1, chainTargetItems$1, chunkItemsMultiplier, itemsTargetFloor);
374
+ return FetchState.getNextQuery(cs.fetchState, chainTargetBlock$1, chainTargetItems$1);
365
375
  }
366
376
 
367
377
  function dispatch(cs, executeQuery, waitForNewBlock, onNewBlock, action, stateId) {
@@ -532,10 +542,6 @@ async function materializePageItems(items, transactionStore, blockStore, ecosyst
532
542
  ]);
533
543
  }
534
544
 
535
- function filterByClientAddress(cs, items) {
536
- return FetchState.filterByClientAddress(items, cs.indexingAddresses);
537
- }
538
-
539
545
  function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight, txPage, blockPage) {
540
546
  if (txPage !== undefined) {
541
547
  cs.transactionStore.merge(Primitive_option.valFromOption(txPage));
@@ -543,7 +549,7 @@ function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBl
543
549
  if (blockPage !== undefined) {
544
550
  cs.blockStore.merge(Primitive_option.valFromOption(blockPage));
545
551
  }
546
- let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.indexingAddresses, newItemsWithDcs) : cs.fetchState;
552
+ let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.addressStore, Primitive_int.max(knownHeight, latestFetchedBlock.blockNumber), newItemsWithDcs) : cs.fetchState;
547
553
  cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
548
554
  cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.itemsEst);
549
555
  }
@@ -578,7 +584,6 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
578
584
  startBlock: init.startBlock,
579
585
  endBlock: blockNumber,
580
586
  normalSelection: init.normalSelection,
581
- contractConfigs: init.contractConfigs,
582
587
  chainId: init.chainId,
583
588
  latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
584
589
  blockLag: init.blockLag,
@@ -586,7 +591,8 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
586
591
  maxOnBlockBufferSize: init.maxOnBlockBufferSize,
587
592
  onBlockRegistrations: init.onBlockRegistrations,
588
593
  knownHeight: init.knownHeight,
589
- firstEventBlock: init.firstEventBlock
594
+ firstEventBlock: init.firstEventBlock,
595
+ clientFilterAddressThreshold: init.clientFilterAddressThreshold
590
596
  };
591
597
  return;
592
598
  }
@@ -596,7 +602,6 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
596
602
  startBlock: init$1.startBlock,
597
603
  endBlock: blockNumber,
598
604
  normalSelection: init$1.normalSelection,
599
- contractConfigs: init$1.contractConfigs,
600
605
  chainId: init$1.chainId,
601
606
  latestOnBlockBlockNumber: init$1.latestOnBlockBlockNumber,
602
607
  blockLag: init$1.blockLag,
@@ -604,7 +609,8 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
604
609
  maxOnBlockBufferSize: init$1.maxOnBlockBufferSize,
605
610
  onBlockRegistrations: init$1.onBlockRegistrations,
606
611
  knownHeight: init$1.knownHeight,
607
- firstEventBlock: init$1.firstEventBlock
612
+ firstEventBlock: init$1.firstEventBlock,
613
+ clientFilterAddressThreshold: init$1.clientFilterAddressThreshold
608
614
  };
609
615
  }
610
616
 
@@ -634,7 +640,7 @@ function toMetrics(cs) {
634
640
  numBatchesFetched: 0,
635
641
  startBlock: cs.fetchState.startBlock,
636
642
  endBlock: cs.fetchState.endBlock,
637
- numAddresses: IndexingAddresses.size(cs.indexingAddresses),
643
+ numAddresses: cs.addressStore.size(),
638
644
  isReady: cs.timestampCaughtUpToHeadOrEndblock !== undefined,
639
645
  sourceBlockNumber: cs.fetchState.knownHeight,
640
646
  progressBlockNumber: cs.committedProgressBlockNumber,
@@ -704,7 +710,6 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
704
710
  startBlock: init.startBlock,
705
711
  endBlock: init.endBlock,
706
712
  normalSelection: init.normalSelection,
707
- contractConfigs: init.contractConfigs,
708
713
  chainId: init.chainId,
709
714
  latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
710
715
  blockLag: init.blockLag,
@@ -712,7 +717,8 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
712
717
  maxOnBlockBufferSize: init.maxOnBlockBufferSize,
713
718
  onBlockRegistrations: init.onBlockRegistrations,
714
719
  knownHeight: init.knownHeight,
715
- firstEventBlock: firstEventBlock
720
+ firstEventBlock: firstEventBlock,
721
+ clientFilterAddressThreshold: init.clientFilterAddressThreshold
716
722
  };
717
723
  }
718
724
  }
@@ -762,7 +768,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
762
768
  if (newProgressBlockNumber === undefined) {
763
769
  if (isReorgChain) {
764
770
  cs.reorgDetection = ReorgDetection.rollbackToValidBlockNumber(cs.reorgDetection, rollbackTargetBlockNumber);
765
- cs.fetchState = FetchState.rollback(cs.fetchState, cs.indexingAddresses, rollbackTargetBlockNumber);
771
+ cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, rollbackTargetBlockNumber);
766
772
  cs.transactionStore.rollback(rollbackTargetBlockNumber);
767
773
  cs.blockStore.rollback(rollbackTargetBlockNumber);
768
774
  cs.committedProgressBlockNumber = Primitive_int.min(cs.committedProgressBlockNumber, rollbackTargetBlockNumber);
@@ -780,7 +786,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
780
786
  if (safeCheckpointTracking !== undefined) {
781
787
  cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
782
788
  }
783
- cs.fetchState = FetchState.rollback(cs.fetchState, cs.indexingAddresses, newProgressBlockNumber);
789
+ cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, newProgressBlockNumber);
784
790
  cs.transactionStore.rollback(newProgressBlockNumber);
785
791
  cs.blockStore.rollback(newProgressBlockNumber);
786
792
  cs.committedProgressBlockNumber = newProgressBlockNumber;
@@ -830,7 +836,6 @@ export {
830
836
  isReady,
831
837
  isFetchingAtHead,
832
838
  isAtHeadWithoutEndBlock,
833
- filterByClientAddress,
834
839
  handleQueryResult,
835
840
  materializeBatchItems,
836
841
  materializePageItems,
@@ -9,7 +9,7 @@ let make: (
9
9
  ~chainConfig: Config.chain,
10
10
  ~fetchState: FetchState.t,
11
11
  ~onEventRegistrations: array<Internal.onEventRegistration>=?,
12
- ~indexingAddresses: IndexingAddresses.t,
12
+ ~addressStore: AddressStore.t,
13
13
  ~sourceManager: SourceManager.t,
14
14
  ~reorgDetection: ReorgDetection.t,
15
15
  ~committedProgressBlockNumber: int,
@@ -80,13 +80,7 @@ let hasReadyItem: t => bool
80
80
  let targetBlock: (t, ~chainTargetItems: float) => int
81
81
  let blockAtProgress: (t, ~progress: float) => int
82
82
  let frontierProgress: t => float
83
- let getNextQuery: (
84
- t,
85
- ~chainTargetItems: float,
86
- ~chunkItemsMultiplier: float=?,
87
- ~itemsTargetFloor: int=?,
88
- ~maxTargetBlock: int=?,
89
- ) => FetchState.nextQuery
83
+ let getNextQuery: (t, ~chainTargetItems: float, ~maxTargetBlock: int=?) => FetchState.nextQuery
90
84
  let dispatch: (
91
85
  t,
92
86
  ~executeQuery: FetchState.query => promise<unit>,
@@ -110,10 +104,6 @@ let isReady: t => bool
110
104
  let isFetchingAtHead: t => bool
111
105
  let isAtHeadWithoutEndBlock: t => bool
112
106
 
113
- // Drop over-fetched events an address-param filter rejects, before contract
114
- // registration runs on them. Uses the chain's current indexing addresses.
115
- let filterByClientAddress: (t, array<Internal.item>) => array<Internal.item>
116
-
117
107
  // State transitions. The chain state is mutated only through these.
118
108
  let handleQueryResult: (
119
109
  t,
package/src/Change.res CHANGED
@@ -1,9 +1,9 @@
1
1
  @tag("type")
2
2
  type t<'entity> =
3
- | @as("SET") Set({entityId: string, entity: 'entity, checkpointId: bigint})
4
- | @as("DELETE") Delete({entityId: string, checkpointId: bigint})
3
+ | @as("SET") Set({entityId: EntityId.t, entity: 'entity, checkpointId: bigint})
4
+ | @as("DELETE") Delete({entityId: EntityId.t, checkpointId: bigint})
5
5
 
6
6
  @get
7
- external getEntityId: t<'entity> => string = "entityId"
7
+ external getEntityId: t<'entity> => EntityId.t = "entityId"
8
8
  @get
9
9
  external getCheckpointId: t<'entity> => bigint = "checkpointId"