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
@@ -34,7 +34,7 @@ module GetLogs: {
34
34
  ~toBlock: option<int>,
35
35
  ~maxNumLogs: option<int>,
36
36
  ~registrationIndexes: array<int>,
37
- ~addressesByContractName: dict<array<Address.t>>,
37
+ ~addressSet: AddressSet.t,
38
38
  ~clientFilteredContracts: option<array<string>>,
39
39
  ) => promise<logsQueryPage>
40
40
  }
@@ -302,9 +302,9 @@ module Registration = {
302
302
  }
303
303
 
304
304
  module EventItems = {
305
- // The whole per-query input: block range, the partition's registration
306
- // selection (by id), and its current addresses. Log selections, field
307
- // selection, and the routing index are derived on the Rust side.
305
+ // The whole per-query input beside the partition's address set: block range
306
+ // and the registration selection (by id). Log selections, field selection,
307
+ // and the routing index are derived on the Rust side.
308
308
  type query = {
309
309
  fromBlock: int,
310
310
  // Inclusive; None queries to the end of available data.
@@ -312,7 +312,6 @@ module EventItems = {
312
312
  // Absent means no server-side cap on the number of logs returned.
313
313
  maxNumLogs?: int,
314
314
  registrationIndexes: array<int>,
315
- addressesByContractName: dict<array<Address.t>>,
316
315
  // Contract names to fetch address-free even though their registrations
317
316
  // depend on addresses (client-side filtering). None/empty means
318
317
  // every address-dependent contract is filtered server-side.
@@ -345,7 +344,9 @@ module EventItems = {
345
344
  type response = {
346
345
  archiveHeight: option<int>,
347
346
  nextBlock: int,
348
- // One header per block number referenced by `items`.
347
+ // One header per returned block number, including blocks no item
348
+ // references — reorg detection reads them all. The block store keeps only
349
+ // the ones items reference.
349
350
  blocks: array<blockHeader>,
350
351
  items: array<item>,
351
352
  rollbackGuard: option<ResponseTypes.rollbackGuard>,
@@ -358,16 +359,22 @@ type t = {
358
359
  // and blocks.
359
360
  getEventItems: (
360
361
  ~query: EventItems.query,
362
+ ~addressSet: AddressSet.t,
361
363
  ) => promise<(EventItems.response, TransactionStore.t, BlockStore.t)>,
362
364
  getHeight: unit => promise<int>,
363
365
  }
364
366
 
365
367
  @send
366
- external classNew: (Core.evmHyperSyncClientCtor, cfg, string, array<Registration.input>) => t =
367
- "new"
368
+ external classNew: (
369
+ Core.evmHyperSyncClientCtor,
370
+ cfg,
371
+ string,
372
+ array<Registration.input>,
373
+ AddressStore.t,
374
+ ) => t = "new"
368
375
 
369
- let makeWithAgent = (cfg, ~userAgent, ~eventRegistrations) =>
370
- Core.getAddon().evmHyperSyncClient->classNew(cfg, userAgent, eventRegistrations)
376
+ let makeWithAgent = (cfg, ~userAgent, ~eventRegistrations, ~addressStore) =>
377
+ Core.getAddon().evmHyperSyncClient->classNew(cfg, userAgent, eventRegistrations, addressStore)
371
378
 
372
379
  type logLevel = [#trace | #debug | #info | #warn | #error]
373
380
  let logLevelSchema: S.t<logLevel> = S.enum([#trace, #debug, #info, #warn, #error])
@@ -393,6 +400,7 @@ let make = (
393
400
  ~retryBackoffMs=?,
394
401
  ~retryCeilingMs=?,
395
402
  ~logLevel=#info,
403
+ ~addressStore,
396
404
  ) => {
397
405
  let envioVersion = Utils.EnvioPackage.value.version
398
406
  makeWithAgent(
@@ -412,5 +420,6 @@ let make = (
412
420
  },
413
421
  ~userAgent=`hyperindex/${envioVersion}`,
414
422
  ~eventRegistrations,
423
+ ~addressStore,
415
424
  )
416
425
  }
@@ -73,8 +73,8 @@ let Registration = {
73
73
 
74
74
  let EventItems = {};
75
75
 
76
- function makeWithAgent(cfg, userAgent, eventRegistrations) {
77
- return Core.getAddon().EvmHyperSyncClient.new(cfg, userAgent, eventRegistrations);
76
+ function makeWithAgent(cfg, userAgent, eventRegistrations, addressStore) {
77
+ return Core.getAddon().EvmHyperSyncClient.new(cfg, userAgent, eventRegistrations, addressStore);
78
78
  }
79
79
 
80
80
  let logLevelSchema = S$RescriptSchema.$$enum([
@@ -99,7 +99,7 @@ function logLevelToString(level) {
99
99
  }
100
100
  }
101
101
 
102
- function make(url, apiToken, httpReqTimeoutMillis, eventRegistrations, enableChecksumAddressesOpt, serializationFormat, enableQueryCaching, retryBaseMs, retryBackoffMs, retryCeilingMs, logLevelOpt) {
102
+ function make(url, apiToken, httpReqTimeoutMillis, eventRegistrations, enableChecksumAddressesOpt, serializationFormat, enableQueryCaching, retryBaseMs, retryBackoffMs, retryCeilingMs, logLevelOpt, addressStore) {
103
103
  let enableChecksumAddresses = enableChecksumAddressesOpt !== undefined ? enableChecksumAddressesOpt : true;
104
104
  let logLevel = logLevelOpt !== undefined ? logLevelOpt : "info";
105
105
  let envioVersion = Utils.EnvioPackage.value.version;
@@ -115,7 +115,7 @@ function make(url, apiToken, httpReqTimeoutMillis, eventRegistrations, enableChe
115
115
  serializationFormat: serializationFormat,
116
116
  enableQueryCaching: enableQueryCaching,
117
117
  logLevel: logLevelToString(logLevel)
118
- }, `hyperindex/` + envioVersion, eventRegistrations);
118
+ }, `hyperindex/` + envioVersion, eventRegistrations, addressStore);
119
119
  }
120
120
 
121
121
  export {
@@ -617,6 +617,8 @@ type options = {
617
617
  // The chain's registrations, indexed by their sequential `index`.
618
618
  onEventRegistrations: array<Internal.evmOnEventRegistration>,
619
619
  lowercaseAddresses: bool,
620
+ // The chain's address index; the client reads it while routing.
621
+ addressStore: AddressStore.t,
620
622
  ws?: string,
621
623
  headers?: dict<string>,
622
624
  }
@@ -629,6 +631,7 @@ let make = (
629
631
  chain,
630
632
  onEventRegistrations,
631
633
  lowercaseAddresses,
634
+ addressStore,
632
635
  ?ws,
633
636
  ?headers,
634
637
  }: options,
@@ -650,6 +653,7 @@ let make = (
650
653
  ~checksumAddresses=!lowercaseAddresses,
651
654
  ~syncConfig,
652
655
  ~headers?,
656
+ ~addressStore,
653
657
  )
654
658
 
655
659
  // Requests are made from shared, memoized loaders, so they can't be
@@ -796,8 +800,7 @@ let make = (
796
800
  let getItemsOrThrow = async (
797
801
  ~fromBlock,
798
802
  ~toBlock,
799
- ~addressesByContractName,
800
- ~contractNameByAddress as _,
803
+ ~addressSet,
801
804
  ~knownHeight,
802
805
  ~partitionId,
803
806
  ~selection: FetchState.selection,
@@ -830,14 +833,16 @@ let make = (
830
833
  )
831
834
  }
832
835
 
833
- let {items, toBlock: queriedToBlock, requestStats} = try await rpcClient.getNextPage({
834
- fromBlock,
835
- toBlockCeiling: toBlock,
836
- partitionId,
837
- registrationIndexes: selection.onEventRegistrations->Array.map(reg => reg.index),
838
- addressesByContractName,
839
- clientFilteredContracts: selection.clientFilteredContracts,
840
- }) catch {
836
+ let {items, toBlock: queriedToBlock, requestStats} = try await rpcClient.getNextPage(
837
+ {
838
+ fromBlock,
839
+ toBlockCeiling: toBlock,
840
+ partitionId,
841
+ registrationIndexes: selection.onEventRegistrations->Array.map(reg => reg.index),
842
+ clientFilteredContracts: selection.clientFilteredContracts,
843
+ },
844
+ addressSet,
845
+ ) catch {
841
846
  | exn =>
842
847
  switch exn->parseGetNextPageRetryError {
843
848
  | Some((attemptedToBlock, retry, requestStats)) =>
@@ -734,7 +734,7 @@ function make(param) {
734
734
  let urlHost = host !== undefined ? host : Stdlib_JsError.throwWithMessage(`The RPC url for chain ` + chain.toString() + ` is in incorrect format. The RPC url needs to start with either http:// or https://`);
735
735
  let name = `RPC (` + urlHost + `)`;
736
736
  let client = Rpc.makeClient(url, headers);
737
- let rpcClient = EvmRpcClient.make(url, !lowercaseAddresses, syncConfig, undefined, headers, HyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations));
737
+ let rpcClient = EvmRpcClient.make(url, !lowercaseAddresses, syncConfig, undefined, headers, HyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), param.addressStore);
738
738
  let pendingRequestStats = [];
739
739
  let recordRequest = (method, seconds) => {
740
740
  pendingRequestStats.push({
@@ -820,7 +820,7 @@ function make(param) {
820
820
  Error: new Error()
821
821
  };
822
822
  }, lowercaseAddresses);
823
- let getItemsOrThrow = async (fromBlock, toBlock, addressesByContractName, param, knownHeight, partitionId, selection, param$1, retry, param$2) => {
823
+ let getItemsOrThrow = async (fromBlock, toBlock, addressSet, knownHeight, partitionId, selection, param, retry, param$1) => {
824
824
  let startFetchingBatchTimeRef = Performance.now();
825
825
  let toBlock$1 = toBlock !== undefined ? Primitive_int.min(toBlock, knownHeight) : knownHeight;
826
826
  let firstBlockParentPromise = fromBlock > 0 ? LazyLoader.get(blockLoader.contents, fromBlock - 1 | 0).then(json => parseBlockInfo(json)) : Promise.resolve(undefined);
@@ -841,9 +841,8 @@ function make(param) {
841
841
  toBlockCeiling: toBlock$1,
842
842
  partitionId: partitionId,
843
843
  registrationIndexes: selection.onEventRegistrations.map(reg => reg.index),
844
- addressesByContractName: addressesByContractName,
845
844
  clientFilteredContracts: selection.clientFilteredContracts
846
- });
845
+ }, addressSet);
847
846
  } catch (raw_exn) {
848
847
  let exn = Primitive_exceptions.internalToException(raw_exn);
849
848
  let match$1 = parseGetNextPageRetryError(exn);
@@ -18,8 +18,7 @@ let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain
18
18
  getItemsOrThrow: (
19
19
  ~fromBlock,
20
20
  ~toBlock,
21
- ~addressesByContractName as _,
22
- ~contractNameByAddress,
21
+ ~addressSet,
23
22
  ~knownHeight as _,
24
23
  ~partitionId as _,
25
24
  ~selection: FetchState.selection,
@@ -28,11 +27,11 @@ let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain
28
27
  ~logger as _,
29
28
  ) => {
30
29
  // Mirror a real backend: return only the items this query would match —
31
- // in the block range, part of the selection, and (for non-wildcard events)
32
- // emitted by an address the partition is querying. Wildcard events are
33
- // over-fetched regardless of srcAddress, leaving the client-side address
34
- // filter to gate them exactly as it does for a HyperSync response. Overlapping
35
- // queries may return the same item more than once; the buffer dedups it.
30
+ // in the block range, part of the selection, and passing the same address
31
+ // gates a real source applies natively while routing (the emitter must be
32
+ // registered for the event's contract at or before the item's block, and
33
+ // any address-valued param filter must hold). Overlapping queries may
34
+ // return the same item more than once; the buffer dedups it.
36
35
  let toBlockQueried = switch toBlock {
37
36
  | Some(toBlock) => toBlock
38
37
  | None => reportedHeight
@@ -49,11 +48,27 @@ let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain
49
48
  false
50
49
  } else if !(selectionEventIds->Utils.Set.has(onEventRegistration.eventConfig.id)) {
51
50
  false
52
- } else if onEventRegistration.isWildcard {
53
- true
54
51
  } else {
55
- let sa = eventItem.payload->Internal.getPayloadSrcAddress->Address.toString
56
- contractNameByAddress->Utils.Dict.dangerouslyGetNonOption(sa)->Option.isSome
52
+ let contractName = onEventRegistration.eventConfig.contractName
53
+ let emitterAllowed =
54
+ onEventRegistration.isWildcard ||
55
+ addressSet->AddressSet.has(
56
+ eventItem.payload->Internal.getPayloadSrcAddress,
57
+ contractName,
58
+ blockNumber,
59
+ )
60
+ emitterAllowed &&
61
+ switch onEventRegistration.addressFilterParamGroups {
62
+ | None
63
+ | Some([]) => true
64
+ | Some(groups) =>
65
+ let params = eventItem.payload->Internal.getPayloadAddressParams
66
+ groups->Array.some(group =>
67
+ group->Array.every(name =>
68
+ addressSet->AddressSet.has(params->Dict.getUnsafe(name), contractName, blockNumber)
69
+ )
70
+ )
71
+ }
57
72
  }
58
73
  })
59
74
 
@@ -1,7 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
4
- import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
4
 
6
5
  function make(items, endBlock, chain) {
7
6
  let reportedHeight = Primitive_int.max(endBlock, 1);
@@ -22,7 +21,7 @@ function make(items, endBlock, chain) {
22
21
  height: reportedHeight,
23
22
  requestStats: []
24
23
  }),
25
- getItemsOrThrow: (fromBlock, toBlock, param, contractNameByAddress, param$1, param$2, selection, param$3, param$4, param$5) => {
24
+ getItemsOrThrow: (fromBlock, toBlock, addressSet, param, param$1, selection, param$2, param$3, param$4) => {
26
25
  let toBlockQueried = toBlock !== undefined ? toBlock : reportedHeight;
27
26
  let selectionEventIds = new Set();
28
27
  selection.onEventRegistrations.forEach(reg => {
@@ -37,11 +36,20 @@ function make(items, endBlock, chain) {
37
36
  if (!selectionEventIds.has(onEventRegistration.eventConfig.id)) {
38
37
  return false;
39
38
  }
40
- if (onEventRegistration.isWildcard) {
39
+ let contractName = onEventRegistration.eventConfig.contractName;
40
+ let emitterAllowed = onEventRegistration.isWildcard || addressSet.has(item.payload.srcAddress, contractName, blockNumber);
41
+ if (!emitterAllowed) {
42
+ return false;
43
+ }
44
+ let groups = onEventRegistration.addressFilterParamGroups;
45
+ if (groups === undefined) {
46
+ return true;
47
+ }
48
+ if (groups.length === 0) {
41
49
  return true;
42
50
  }
43
- let sa = item.payload.srcAddress;
44
- return Stdlib_Option.isSome(contractNameByAddress[sa]);
51
+ let params = item.payload.params;
52
+ return groups.some(group => group.every(name => addressSet.has(params[name], contractName, blockNumber)));
45
53
  });
46
54
  return Promise.resolve({
47
55
  knownHeight: reportedHeight,
@@ -73,8 +73,10 @@ type t = {
73
73
  getItemsOrThrow: (
74
74
  ~fromBlock: int,
75
75
  ~toBlock: option<int>,
76
- ~addressesByContractName: dict<array<Address.t>>,
77
- ~contractNameByAddress: dict<string>,
76
+ // The partition's slice of the chain's address index. The source hands it
77
+ // straight to its Rust client, which builds the query's address filter from
78
+ // it and gates every returned item against the chain-wide store.
79
+ ~addressSet: AddressSet.t,
78
80
  ~knownHeight: int,
79
81
  ~partitionId: string,
80
82
  ~selection: FetchState.selection,
@@ -754,7 +754,7 @@ let executeQuery = async (
754
754
  "source": source.name,
755
755
  "fromBlock": query.fromBlock,
756
756
  "toBlock": toBlock,
757
- "addresses": query.addressesByContractName->FetchState.addressesByContractNameCount,
757
+ "addresses": query.addresses->AddressSet.size,
758
758
  "retry": retry,
759
759
  },
760
760
  )
@@ -763,8 +763,7 @@ let executeQuery = async (
763
763
  let response = await source.getItemsOrThrow(
764
764
  ~fromBlock=query.fromBlock,
765
765
  ~toBlock,
766
- ~addressesByContractName=query.addressesByContractName,
767
- ~contractNameByAddress=query.addressesByContractName->FetchState.deriveContractNameByAddress,
766
+ ~addressSet=query.addresses,
768
767
  ~partitionId=query.partitionId,
769
768
  ~knownHeight,
770
769
  ~selection=query.selection,
@@ -3,7 +3,6 @@
3
3
  import * as Utils from "../Utils.res.mjs";
4
4
  import * as Source from "./Source.res.mjs";
5
5
  import * as Logging from "../Logging.res.mjs";
6
- import * as FetchState from "../FetchState.res.mjs";
7
6
  import * as Performance from "../bindings/Performance.res.mjs";
8
7
  import * as ErrorHandling from "../ErrorHandling.res.mjs";
9
8
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
@@ -597,11 +596,11 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
597
596
  source: source.name,
598
597
  fromBlock: query.fromBlock,
599
598
  toBlock: toBlock,
600
- addresses: FetchState.addressesByContractNameCount(query.addressesByContractName),
599
+ addresses: query.addresses.size(),
601
600
  retry: retry
602
601
  });
603
602
  try {
604
- let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addressesByContractName, FetchState.deriveContractNameByAddress(query.addressesByContractName), knownHeight, query.partitionId, query.selection, query.itemsTarget, retry, logger$2);
603
+ let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addresses, knownHeight, query.partitionId, query.selection, query.itemsTarget, retry, logger$2);
605
604
  recordRequestStats(sourceState, response.requestStats);
606
605
  sourceState.lastFailedAt = undefined;
607
606
  if (response.knownHeight > sourceState.knownHeight) {
@@ -100,8 +100,7 @@ let makeRPCSource = (~chain, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): S
100
100
  getItemsOrThrow: (
101
101
  ~fromBlock as _,
102
102
  ~toBlock as _,
103
- ~addressesByContractName as _,
104
- ~contractNameByAddress as _,
103
+ ~addressSet as _,
105
104
  ~knownHeight as _,
106
105
  ~partitionId as _,
107
106
  ~selection as _,
@@ -91,7 +91,7 @@ function makeRPCSource(chain, rpc, sourceForOpt) {
91
91
  }]
92
92
  };
93
93
  },
94
- getItemsOrThrow: (param, param$1, param$2, param$3, param$4, param$5, param$6, param$7, param$8, param$9) => Stdlib_JsError.throwWithMessage("Svm does not support getting items")
94
+ getItemsOrThrow: (param, param$1, param$2, param$3, param$4, param$5, param$6, param$7, param$8) => Stdlib_JsError.throwWithMessage("Svm does not support getting items")
95
95
  };
96
96
  }
97
97
 
@@ -191,7 +191,10 @@ module EventItems = {
191
191
  // Absent means no server-side cap on the number of instructions returned.
192
192
  maxNumInstructions?: int,
193
193
  registrationIndexes: array<int>,
194
- addressesByContractName: dict<array<Address.t>>,
194
+ // Program names to fetch address-free even though their registrations
195
+ // depend on addresses (client-side filtering). None/empty means every
196
+ // address-dependent program is filtered server-side.
197
+ clientFilteredContracts: option<array<string>>,
195
198
  }
196
199
 
197
200
  type log = {
@@ -221,8 +224,10 @@ module EventItems = {
221
224
 
222
225
  type response = {
223
226
  nextSlot: int,
224
- // One lean header per slot referenced by `items`; the full blocks live in
225
- // the block store returned alongside.
227
+ // One lean header per returned slot, including slots no item references
228
+ // reorg detection and the batch's latest timestamp read them all. The full
229
+ // blocks live in the block store returned alongside, which keeps only the
230
+ // slots items reference.
226
231
  blocks: array<ResponseTypes.block>,
227
232
  items: array<item>,
228
233
  }
@@ -240,6 +245,7 @@ type t = {
240
245
  // prep.
241
246
  getEventItems: (
242
247
  ~query: EventItems.query,
248
+ ~addressSet: AddressSet.t,
243
249
  ) => promise<(EventItems.response, TransactionStore.t, BlockStore.t)>,
244
250
  }
245
251
 
@@ -249,6 +255,7 @@ external classFromConfig: (
249
255
  cfg,
250
256
  string,
251
257
  array<Registration.input>,
258
+ AddressStore.t,
252
259
  ) => t = "fromConfig"
253
260
 
254
261
  let make = (
@@ -259,6 +266,7 @@ let make = (
259
266
  ~retryBaseMs=?,
260
267
  ~retryCeilingMs=?,
261
268
  ~eventRegistrations=[],
269
+ ~addressStore,
262
270
  ) => {
263
271
  let envioVersion = Utils.EnvioPackage.value.version
264
272
  Core.getAddon().svmHyperSyncClient->classFromConfig(
@@ -272,5 +280,6 @@ let make = (
272
280
  },
273
281
  `hyperindex/${envioVersion}`,
274
282
  eventRegistrations,
283
+ addressStore,
275
284
  )
276
285
  }
@@ -45,7 +45,7 @@ let ResponseTypes = {};
45
45
 
46
46
  let EventItems = {};
47
47
 
48
- function make(url, apiToken, httpReqTimeoutMillis, maxNumRetries, retryBaseMs, retryCeilingMs, eventRegistrationsOpt) {
48
+ function make(url, apiToken, httpReqTimeoutMillis, maxNumRetries, retryBaseMs, retryCeilingMs, eventRegistrationsOpt, addressStore) {
49
49
  let eventRegistrations = eventRegistrationsOpt !== undefined ? eventRegistrationsOpt : [];
50
50
  let envioVersion = Utils.EnvioPackage.value.version;
51
51
  return Core.getAddon().SvmHyperSyncClient.fromConfig({
@@ -55,7 +55,7 @@ function make(url, apiToken, httpReqTimeoutMillis, maxNumRetries, retryBaseMs, r
55
55
  maxNumRetries: maxNumRetries,
56
56
  retryBaseMs: retryBaseMs,
57
57
  retryCeilingMs: retryCeilingMs
58
- }, `hyperindex/` + envioVersion, eventRegistrations);
58
+ }, `hyperindex/` + envioVersion, eventRegistrations, addressStore);
59
59
  }
60
60
 
61
61
  export {
@@ -6,6 +6,8 @@ type options = {
6
6
  apiToken: option<string>,
7
7
  onEventRegistrations: array<Internal.svmOnEventRegistration>,
8
8
  clientTimeoutMillis: int,
9
+ // The chain's address index; the client reads it while routing.
10
+ addressStore: AddressStore.t,
9
11
  }
10
12
 
11
13
  // Synthesize a stable logIndex for an SVM instruction so the FetchState
@@ -66,7 +68,7 @@ let toSvmInstruction = (
66
68
  }
67
69
 
68
70
  let make = (
69
- {chain, endpointUrl, apiToken, onEventRegistrations, clientTimeoutMillis}: options,
71
+ {chain, endpointUrl, apiToken, onEventRegistrations, clientTimeoutMillis, addressStore}: options,
70
72
  ): t => {
71
73
  let name = "SvmHyperSync"
72
74
 
@@ -80,13 +82,13 @@ let make = (
80
82
  ~eventRegistrations=SvmHyperSyncClient.Registration.fromOnEventRegistrations(
81
83
  onEventRegistrations,
82
84
  ),
85
+ ~addressStore,
83
86
  )
84
87
 
85
88
  let getItemsOrThrow = async (
86
89
  ~fromBlock,
87
90
  ~toBlock,
88
- ~addressesByContractName,
89
- ~contractNameByAddress as _,
91
+ ~addressSet,
90
92
  ~knownHeight,
91
93
  ~partitionId as _,
92
94
  ~selection: FetchState.selection,
@@ -102,10 +104,13 @@ let make = (
102
104
  toSlot: toBlock,
103
105
  maxNumInstructions: ?itemsTarget,
104
106
  registrationIndexes: selection.onEventRegistrations->Array.map(reg => reg.index),
105
- addressesByContractName,
107
+ clientFilteredContracts: selection.clientFilteredContracts,
106
108
  }
107
109
 
108
- let (resp, transactionStore, blockStore) = try await client.getEventItems(~query) catch {
110
+ let (resp, transactionStore, blockStore) = try await client.getEventItems(
111
+ ~query,
112
+ ~addressSet,
113
+ ) catch {
109
114
  | exn =>
110
115
  throw(
111
116
  Source.GetItemsError(
@@ -58,21 +58,22 @@ function toSvmInstruction(item, programName, instructionName) {
58
58
  function make(param) {
59
59
  let onEventRegistrations = param.onEventRegistrations;
60
60
  let chain = param.chain;
61
- let client = SvmHyperSyncClient.make(param.endpointUrl, param.apiToken, param.clientTimeoutMillis, undefined, undefined, undefined, SvmHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations));
62
- let getItemsOrThrow = async (fromBlock, toBlock, addressesByContractName, param, knownHeight, param$1, selection, itemsTarget, retry, param$2) => {
61
+ let client = SvmHyperSyncClient.make(param.endpointUrl, param.apiToken, param.clientTimeoutMillis, undefined, undefined, undefined, SvmHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), param.addressStore);
62
+ let getItemsOrThrow = async (fromBlock, toBlock, addressSet, knownHeight, param, selection, itemsTarget, retry, param$1) => {
63
63
  let totalTimeRef = Performance.now();
64
64
  let pageFetchRef = Performance.now();
65
65
  let query_registrationIndexes = selection.onEventRegistrations.map(reg => reg.index);
66
+ let query_clientFilteredContracts = selection.clientFilteredContracts;
66
67
  let query = {
67
68
  fromSlot: fromBlock,
68
69
  toSlot: toBlock,
69
70
  maxNumInstructions: itemsTarget,
70
71
  registrationIndexes: query_registrationIndexes,
71
- addressesByContractName: addressesByContractName
72
+ clientFilteredContracts: query_clientFilteredContracts
72
73
  };
73
74
  let match;
74
75
  try {
75
- match = await client.getEventItems(query);
76
+ match = await client.getEventItems(query, addressSet);
76
77
  } catch (raw_exn) {
77
78
  let exn = Primitive_exceptions.internalToException(raw_exn);
78
79
  throw {