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
@@ -97,7 +97,6 @@ let getErrorMessage = (exn: exn): option<string> =>
97
97
  | _ => None
98
98
  }
99
99
 
100
-
101
100
  // `EvmRpcClient.getNextPage` throws a napi error whose message is a JSON
102
101
  // payload describing the retry decision:
103
102
  // `{"kind":"Retry","attemptedToBlock":int,"errorMessage":string|null,
@@ -618,6 +617,8 @@ type options = {
618
617
  // The chain's registrations, indexed by their sequential `index`.
619
618
  onEventRegistrations: array<Internal.evmOnEventRegistration>,
620
619
  lowercaseAddresses: bool,
620
+ // The chain's address index; the client reads it while routing.
621
+ addressStore: AddressStore.t,
621
622
  ws?: string,
622
623
  headers?: dict<string>,
623
624
  }
@@ -630,6 +631,7 @@ let make = (
630
631
  chain,
631
632
  onEventRegistrations,
632
633
  lowercaseAddresses,
634
+ addressStore,
633
635
  ?ws,
634
636
  ?headers,
635
637
  }: options,
@@ -647,12 +649,11 @@ let make = (
647
649
  let client = Rpc.makeClient(url, ~headers?)
648
650
  let rpcClient = EvmRpcClient.make(
649
651
  ~url,
650
- ~eventRegistrations=HyperSyncClient.Registration.fromOnEventRegistrations(
651
- onEventRegistrations,
652
- ),
652
+ ~eventRegistrations=HyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations),
653
653
  ~checksumAddresses=!lowercaseAddresses,
654
654
  ~syncConfig,
655
655
  ~headers?,
656
+ ~addressStore,
656
657
  )
657
658
 
658
659
  // Requests are made from shared, memoized loaders, so they can't be
@@ -799,8 +800,7 @@ let make = (
799
800
  let getItemsOrThrow = async (
800
801
  ~fromBlock,
801
802
  ~toBlock,
802
- ~addressesByContractName,
803
- ~contractNameByAddress as _,
803
+ ~addressSet,
804
804
  ~knownHeight,
805
805
  ~partitionId,
806
806
  ~selection: FetchState.selection,
@@ -833,13 +833,16 @@ let make = (
833
833
  )
834
834
  }
835
835
 
836
- let {items, toBlock: queriedToBlock, requestStats} = try await rpcClient.getNextPage({
837
- fromBlock,
838
- toBlockCeiling: toBlock,
839
- partitionId,
840
- registrationIndexes: selection.onEventRegistrations->Array.map(reg => reg.index),
841
- addressesByContractName,
842
- }) 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 {
843
846
  | exn =>
844
847
  switch exn->parseGetNextPageRetryError {
845
848
  | Some((attemptedToBlock, retry, requestStats)) =>
@@ -879,63 +882,64 @@ let make = (
879
882
  onEventRegistration.eventConfig->(
880
883
  Utils.magic: Internal.eventConfig => Internal.evmEventConfig
881
884
  )
885
+
882
886
  (
883
887
  async () => {
884
- let (block, transaction) = try await Promise.all2((
885
- log->getEventBlockOrThrow(~selectedBlockFields=eventConfig.selectedBlockFields),
886
- log->getEventTransactionOrThrow(
887
- ~selectedTransactionFields=eventConfig.selectedTransactionFields->(
888
- Utils.magic: Utils.Set.t<string> => Utils.Set.t<Internal.evmTransactionField>
889
- ),
890
- ),
891
- )) catch {
892
- | TransactionDataNotFound({message}) =>
893
- let backoffMillis = switch retry {
894
- | 0 => 100
895
- | _ => 500 * retry
896
- }
897
- throw(
898
- Source.GetItemsError(
899
- FailedGettingItems({
900
- exn: %raw(`null`),
901
- attemptedToBlock: toBlock,
902
- retry: WithBackoff({
903
- message: `${message}. The RPC provider might be load-balanced between nodes that drift independently slightly from the head. Indexing should continue correctly after retrying the query in ${backoffMillis->Int.toString}ms.`,
904
- backoffMillis,
905
- }),
906
- }),
907
- ),
908
- )
909
- | exn =>
910
- throw(
911
- Source.GetItemsError(
912
- FailedGettingFieldSelection({
913
- message: "Failed getting selected fields. Please double-check your RPC provider returns correct data.",
914
- exn,
915
- blockNumber: log.blockNumber,
916
- logIndex: log.logIndex,
917
- }),
918
- ),
919
- )
920
- }
921
-
922
- Internal.Event({
923
- onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
924
- blockNumber: block->getBlockNumber,
925
- chain,
888
+ let (block, transaction) = try await Promise.all2((
889
+ log->getEventBlockOrThrow(~selectedBlockFields=eventConfig.selectedBlockFields),
890
+ log->getEventTransactionOrThrow(
891
+ ~selectedTransactionFields=eventConfig.selectedTransactionFields->(
892
+ Utils.magic: Utils.Set.t<string> => Utils.Set.t<Internal.evmTransactionField>
893
+ ),
894
+ ),
895
+ )) catch {
896
+ | TransactionDataNotFound({message}) =>
897
+ let backoffMillis = switch retry {
898
+ | 0 => 100
899
+ | _ => 500 * retry
900
+ }
901
+ throw(
902
+ Source.GetItemsError(
903
+ FailedGettingItems({
904
+ exn: %raw(`null`),
905
+ attemptedToBlock: toBlock,
906
+ retry: WithBackoff({
907
+ message: `${message}. The RPC provider might be load-balanced between nodes that drift independently slightly from the head. Indexing should continue correctly after retrying the query in ${backoffMillis->Int.toString}ms.`,
908
+ backoffMillis,
909
+ }),
910
+ }),
911
+ ),
912
+ )
913
+ | exn =>
914
+ throw(
915
+ Source.GetItemsError(
916
+ FailedGettingFieldSelection({
917
+ message: "Failed getting selected fields. Please double-check your RPC provider returns correct data.",
918
+ exn,
919
+ blockNumber: log.blockNumber,
926
920
  logIndex: log.logIndex,
927
- transactionIndex: log.transactionIndex,
928
- payload: {
929
- contractName: eventConfig.contractName,
930
- eventName: eventConfig.name,
931
- chainId: chain->ChainMap.Chain.toChainId,
932
- params: decoded,
933
- block,
934
- transaction,
935
- srcAddress: log.address,
936
- logIndex: log.logIndex,
937
- }->Evm.fromPayload,
938
- })
921
+ }),
922
+ ),
923
+ )
924
+ }
925
+
926
+ Internal.Event({
927
+ onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
928
+ blockNumber: block->getBlockNumber,
929
+ chain,
930
+ logIndex: log.logIndex,
931
+ transactionIndex: log.transactionIndex,
932
+ payload: {
933
+ contractName: eventConfig.contractName,
934
+ eventName: eventConfig.name,
935
+ chainId: chain->ChainMap.Chain.toChainId,
936
+ params: decoded,
937
+ block,
938
+ transaction,
939
+ srcAddress: log.address,
940
+ logIndex: log.logIndex,
941
+ }->Evm.fromPayload,
942
+ })
939
943
  }
940
944
  )()
941
945
  })
@@ -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,8 +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
845
+ }, addressSet);
846
846
  } catch (raw_exn) {
847
847
  let exn = Primitive_exceptions.internalToException(raw_exn);
848
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,
@@ -82,8 +84,10 @@ type t = {
82
84
  // source should ask its backend for, from the query's own estResponseSize.
83
85
  // A HyperSync-backed source enforces it server-side, so a wrong estimate
84
86
  // truncates the response instead of overshooting the shared buffer. Sources
85
- // without an equivalent lever (RPC, Fuel, Simulate) ignore it.
86
- ~itemsTarget: int,
87
+ // without an equivalent lever (RPC, Fuel, Simulate) ignore it. None means no
88
+ // cap: bounded chunk queries fetch their whole range even if denser than
89
+ // expected, so client-side-filtered items can't truncate the range short.
90
+ ~itemsTarget: option<int>,
87
91
  ~retry: int,
88
92
  ~logger: Pino.t,
89
93
  ) => promise<blockRangeFetchResponse>,
@@ -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
 
@@ -188,9 +188,13 @@ module EventItems = {
188
188
  fromSlot: int,
189
189
  // Inclusive; None queries to the end of available data.
190
190
  toSlot: option<int>,
191
- maxNumInstructions: int,
191
+ // Absent means no server-side cap on the number of instructions returned.
192
+ maxNumInstructions?: int,
192
193
  registrationIndexes: array<int>,
193
- 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>>,
194
198
  }
195
199
 
196
200
  type log = {
@@ -220,8 +224,10 @@ module EventItems = {
220
224
 
221
225
  type response = {
222
226
  nextSlot: int,
223
- // One lean header per slot referenced by `items`; the full blocks live in
224
- // 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.
225
231
  blocks: array<ResponseTypes.block>,
226
232
  items: array<item>,
227
233
  }
@@ -239,6 +245,7 @@ type t = {
239
245
  // prep.
240
246
  getEventItems: (
241
247
  ~query: EventItems.query,
248
+ ~addressSet: AddressSet.t,
242
249
  ) => promise<(EventItems.response, TransactionStore.t, BlockStore.t)>,
243
250
  }
244
251
 
@@ -248,6 +255,7 @@ external classFromConfig: (
248
255
  cfg,
249
256
  string,
250
257
  array<Registration.input>,
258
+ AddressStore.t,
251
259
  ) => t = "fromConfig"
252
260
 
253
261
  let make = (
@@ -258,6 +266,7 @@ let make = (
258
266
  ~retryBaseMs=?,
259
267
  ~retryCeilingMs=?,
260
268
  ~eventRegistrations=[],
269
+ ~addressStore,
261
270
  ) => {
262
271
  let envioVersion = Utils.EnvioPackage.value.version
263
272
  Core.getAddon().svmHyperSyncClient->classFromConfig(
@@ -271,5 +280,6 @@ let make = (
271
280
  },
272
281
  `hyperindex/${envioVersion}`,
273
282
  eventRegistrations,
283
+ addressStore,
274
284
  )
275
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,
@@ -100,12 +102,15 @@ let make = (
100
102
  let query: SvmHyperSyncClient.EventItems.query = {
101
103
  fromSlot: fromBlock,
102
104
  toSlot: toBlock,
103
- maxNumInstructions: itemsTarget,
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 {