envio 3.5.0-alpha.1 → 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 (86) 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/ChainFetching.res +5 -5
  6. package/src/ChainFetching.res.mjs +3 -4
  7. package/src/ChainState.res +38 -21
  8. package/src/ChainState.res.mjs +17 -24
  9. package/src/ChainState.resi +1 -5
  10. package/src/Change.res +3 -3
  11. package/src/Config.res +0 -4
  12. package/src/Config.res.mjs +0 -10
  13. package/src/Core.res +3 -0
  14. package/src/EntityId.res +15 -0
  15. package/src/EntityId.res.mjs +9 -0
  16. package/src/EventConfigBuilder.res +1 -58
  17. package/src/EventConfigBuilder.res.mjs +1 -33
  18. package/src/FetchState.res +345 -459
  19. package/src/FetchState.res.mjs +269 -430
  20. package/src/HandlerRegister.res +45 -0
  21. package/src/HandlerRegister.res.mjs +43 -0
  22. package/src/InMemoryStore.res +11 -4
  23. package/src/InMemoryTable.res +12 -10
  24. package/src/InMemoryTable.res.mjs +6 -5
  25. package/src/IndexerState.res +59 -2
  26. package/src/IndexerState.res.mjs +48 -3
  27. package/src/IndexerState.resi +2 -0
  28. package/src/Internal.res +17 -8
  29. package/src/LogSelection.res +2 -1
  30. package/src/Metrics.res +37 -6
  31. package/src/Metrics.res.mjs +5 -1
  32. package/src/Persistence.res +1 -1
  33. package/src/PgStorage.res +55 -22
  34. package/src/PgStorage.res.mjs +25 -17
  35. package/src/SimulateItems.res +2 -2
  36. package/src/TestIndexer.res +6 -5
  37. package/src/TestIndexer.res.mjs +3 -2
  38. package/src/UserContext.res +3 -3
  39. package/src/Writing.res +12 -2
  40. package/src/Writing.res.mjs +13 -2
  41. package/src/bindings/ClickHouse.res +5 -3
  42. package/src/bindings/ClickHouse.res.mjs +2 -5
  43. package/src/bindings/Vitest.res +22 -1
  44. package/src/bindings/Vitest.res.mjs +15 -0
  45. package/src/db/EntityHistory.res +16 -7
  46. package/src/db/EntityHistory.res.mjs +7 -6
  47. package/src/db/Table.res +40 -9
  48. package/src/db/Table.res.mjs +44 -6
  49. package/src/sources/AddressSet.res +48 -0
  50. package/src/sources/AddressSet.res.mjs +11 -0
  51. package/src/sources/AddressStore.res +152 -0
  52. package/src/sources/AddressStore.res.mjs +97 -0
  53. package/src/sources/EvmChain.res +3 -0
  54. package/src/sources/EvmChain.res.mjs +4 -2
  55. package/src/sources/EvmHyperSyncSource.res +6 -3
  56. package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
  57. package/src/sources/EvmRpcClient.res +6 -3
  58. package/src/sources/EvmRpcClient.res.mjs +3 -3
  59. package/src/sources/FuelHyperSync.res +4 -3
  60. package/src/sources/FuelHyperSync.res.mjs +3 -3
  61. package/src/sources/FuelHyperSync.resi +2 -1
  62. package/src/sources/FuelHyperSyncClient.res +12 -6
  63. package/src/sources/FuelHyperSyncClient.res.mjs +2 -2
  64. package/src/sources/FuelHyperSyncSource.res +7 -4
  65. package/src/sources/FuelHyperSyncSource.res.mjs +3 -3
  66. package/src/sources/HyperSync.res +6 -4
  67. package/src/sources/HyperSync.res.mjs +2 -3
  68. package/src/sources/HyperSync.resi +1 -1
  69. package/src/sources/HyperSyncClient.res +18 -9
  70. package/src/sources/HyperSyncClient.res.mjs +4 -4
  71. package/src/sources/RpcSource.res +15 -10
  72. package/src/sources/RpcSource.res.mjs +3 -4
  73. package/src/sources/SimulateSource.res +26 -11
  74. package/src/sources/SimulateSource.res.mjs +13 -5
  75. package/src/sources/Source.res +4 -2
  76. package/src/sources/SourceManager.res +2 -3
  77. package/src/sources/SourceManager.res.mjs +2 -3
  78. package/src/sources/Svm.res +1 -2
  79. package/src/sources/Svm.res.mjs +1 -1
  80. package/src/sources/SvmHyperSyncClient.res +12 -3
  81. package/src/sources/SvmHyperSyncClient.res.mjs +2 -2
  82. package/src/sources/SvmHyperSyncSource.res +10 -5
  83. package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
  84. package/src/IndexingAddresses.res +0 -151
  85. package/src/IndexingAddresses.res.mjs +0 -130
  86. package/src/IndexingAddresses.resi +0 -39
@@ -16,6 +16,8 @@ type options = {
16
16
  serializationFormat: HyperSyncClient.serializationFormat,
17
17
  enableQueryCaching: bool,
18
18
  logLevel: HyperSyncClient.logLevel,
19
+ // The chain's address index; the client reads it while routing.
20
+ addressStore: AddressStore.t,
19
21
  }
20
22
 
21
23
  let make = (
@@ -29,6 +31,7 @@ let make = (
29
31
  serializationFormat,
30
32
  enableQueryCaching,
31
33
  logLevel,
34
+ addressStore,
32
35
  }: options,
33
36
  ): t => {
34
37
  let name = "HyperSync"
@@ -50,6 +53,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
50
53
  ~serializationFormat,
51
54
  ~enableQueryCaching,
52
55
  ~logLevel,
56
+ ~addressStore,
53
57
  ) {
54
58
  | client => client
55
59
  | exception exn =>
@@ -86,8 +90,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
86
90
  let getItemsOrThrow = async (
87
91
  ~fromBlock,
88
92
  ~toBlock,
89
- ~addressesByContractName,
90
- ~contractNameByAddress as _,
93
+ ~addressSet,
91
94
  ~knownHeight,
92
95
  ~partitionId as _,
93
96
  ~selection: FetchState.selection,
@@ -106,7 +109,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
106
109
  ~toBlock,
107
110
  ~maxNumLogs=itemsTarget,
108
111
  ~registrationIndexes=selection.onEventRegistrations->Array.map(reg => reg.index),
109
- ~addressesByContractName,
112
+ ~addressSet,
110
113
  ~clientFilteredContracts=selection.clientFilteredContracts,
111
114
  ) catch {
112
115
  | HyperSync.GetLogs.Error(error) =>
@@ -28,7 +28,7 @@ Set the ENVIO_API_TOKEN environment variable in your .env file.
28
28
  Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
29
29
  let client;
30
30
  try {
31
- client = HyperSyncClient.make(endpointUrl, apiToken$1, param.clientTimeoutMillis, HyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), !param.lowercaseAddresses, param.serializationFormat, param.enableQueryCaching, undefined, undefined, undefined, param.logLevel);
31
+ client = HyperSyncClient.make(endpointUrl, apiToken$1, param.clientTimeoutMillis, HyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), !param.lowercaseAddresses, param.serializationFormat, param.enableQueryCaching, undefined, undefined, undefined, param.logLevel, param.addressStore);
32
32
  } catch (raw_exn) {
33
33
  let exn = Primitive_exceptions.internalToException(raw_exn);
34
34
  client = ErrorHandling.mkLogAndRaise(undefined, "Failed to instantiate the hypersync client, please double check your ABI", exn);
@@ -52,12 +52,12 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
52
52
  }
53
53
  };
54
54
  };
55
- let getItemsOrThrow = async (fromBlock, toBlock, addressesByContractName, param, knownHeight, param$1, selection, itemsTarget, retry, param$2) => {
55
+ let getItemsOrThrow = async (fromBlock, toBlock, addressSet, knownHeight, param, selection, itemsTarget, retry, param$1) => {
56
56
  let totalTimeRef = Performance.now();
57
57
  let startFetchingBatchTimeRef = Performance.now();
58
58
  let pageUnsafe;
59
59
  try {
60
- pageUnsafe = await HyperSync.GetLogs.query(client, fromBlock, toBlock, itemsTarget, selection.onEventRegistrations.map(reg => reg.index), addressesByContractName, selection.clientFilteredContracts);
60
+ pageUnsafe = await HyperSync.GetLogs.query(client, fromBlock, toBlock, itemsTarget, selection.onEventRegistrations.map(reg => reg.index), addressSet, selection.clientFilteredContracts);
61
61
  } catch (raw_error) {
62
62
  let error = Primitive_exceptions.internalToException(raw_error);
63
63
  if (error.RE_EXN_ID === HyperSync.GetLogs.$$Error) {
@@ -26,7 +26,6 @@ type nextPageParams = {
26
26
  // selections and the routing index are derived on the Rust side from the
27
27
  // registrations passed at construction.
28
28
  registrationIndexes: array<int>,
29
- addressesByContractName: dict<array<Address.t>>,
30
29
  // Contract names to fetch address-free even though their registrations
31
30
  // depend on addresses (client-side filtering). None/empty means
32
31
  // every address-dependent contract is filtered server-side.
@@ -42,7 +41,7 @@ type nextPageResponse = {
42
41
  // The caller provides a range; Rust decides the actual `toBlock` and returns it.
43
42
  type t = {
44
43
  getHeight: unit => promise<int>,
45
- getNextPage: nextPageParams => promise<nextPageResponse>,
44
+ getNextPage: (nextPageParams, AddressSet.t) => promise<nextPageResponse>,
46
45
  }
47
46
 
48
47
  @send
@@ -51,6 +50,7 @@ external classNew: (
51
50
  cfg,
52
51
  array<HyperSyncClient.Registration.input>,
53
52
  ~checksumAddresses: bool,
53
+ ~addressStore: AddressStore.t,
54
54
  ) => t = "new"
55
55
 
56
56
  // Rust encodes JSON-RPC errors as a JSON payload in the napi error's
@@ -89,6 +89,7 @@ let make = (
89
89
  ~httpReqTimeoutMillis=?,
90
90
  ~headers=?,
91
91
  ~eventRegistrations=[],
92
+ ~addressStore,
92
93
  ) => {
93
94
  let client = Core.getAddon().evmRpcClient->classNew(
94
95
  {
@@ -104,9 +105,11 @@ let make = (
104
105
  },
105
106
  eventRegistrations,
106
107
  ~checksumAddresses,
108
+ ~addressStore,
107
109
  )
108
110
  {
109
111
  getHeight: () => client.getHeight()->Promise.catch(coerceErrorOrThrow),
110
- getNextPage: params => client.getNextPage(params)->Promise.catch(coerceErrorOrThrow),
112
+ getNextPage: (params, addressSet) =>
113
+ client.getNextPage(params, addressSet)->Promise.catch(coerceErrorOrThrow),
111
114
  }
112
115
  }
@@ -46,7 +46,7 @@ function coerceErrorOrThrow(exn) {
46
46
  throw exn;
47
47
  }
48
48
 
49
- function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers, eventRegistrationsOpt) {
49
+ function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers, eventRegistrationsOpt, addressStore) {
50
50
  let eventRegistrations = eventRegistrationsOpt !== undefined ? eventRegistrationsOpt : [];
51
51
  let client = Core.getAddon().EvmRpcClient.new({
52
52
  url: url,
@@ -58,10 +58,10 @@ function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers,
58
58
  intervalCeiling: syncConfig.intervalCeiling,
59
59
  backoffMillis: syncConfig.backoffMillis,
60
60
  queryTimeoutMillis: syncConfig.queryTimeoutMillis
61
- }, eventRegistrations, checksumAddresses);
61
+ }, eventRegistrations, checksumAddresses, addressStore);
62
62
  return {
63
63
  getHeight: () => Stdlib_Promise.$$catch(client.getHeight(), coerceErrorOrThrow),
64
- getNextPage: params => Stdlib_Promise.$$catch(client.getNextPage(params), coerceErrorOrThrow)
64
+ getNextPage: (params, addressSet) => Stdlib_Promise.$$catch(client.getNextPage(params, addressSet), coerceErrorOrThrow)
65
65
  };
66
66
  }
67
67
 
@@ -43,16 +43,17 @@ module GetLogs = {
43
43
  ~fromBlock,
44
44
  ~toBlock,
45
45
  ~registrationIndexes,
46
- ~addressesByContractName,
46
+ ~addressSet,
47
+ ~clientFilteredContracts,
47
48
  ): logsQueryPage => {
48
49
  let query: FuelHyperSyncClient.EventItems.query = {
49
50
  fromBlock,
50
51
  toBlock,
51
52
  registrationIndexes,
52
- addressesByContractName,
53
+ clientFilteredContracts,
53
54
  }
54
55
 
55
- let res = switch await client->FuelHyperSyncClient.getEventItems(query) {
56
+ let res = switch await client->FuelHyperSyncClient.getEventItems(query, addressSet) {
56
57
  | res => res
57
58
  | exception exn =>
58
59
  switch exn->extractMissingParams {
@@ -29,16 +29,16 @@ function extractMissingParams(exn) {
29
29
  }
30
30
  }
31
31
 
32
- async function query(client, fromBlock, toBlock, registrationIndexes, addressesByContractName) {
32
+ async function query(client, fromBlock, toBlock, registrationIndexes, addressSet, clientFilteredContracts) {
33
33
  let query$1 = {
34
34
  fromBlock: fromBlock,
35
35
  toBlock: toBlock,
36
36
  registrationIndexes: registrationIndexes,
37
- addressesByContractName: addressesByContractName
37
+ clientFilteredContracts: clientFilteredContracts
38
38
  };
39
39
  let res;
40
40
  try {
41
- res = await client.getEventItems(query$1);
41
+ res = await client.getEventItems(query$1, addressSet);
42
42
  } catch (raw_exn) {
43
43
  let exn = Primitive_exceptions.internalToException(raw_exn);
44
44
  let missingParams = extractMissingParams(exn);
@@ -17,6 +17,7 @@ module GetLogs: {
17
17
  ~fromBlock: int,
18
18
  ~toBlock: option<int>,
19
19
  ~registrationIndexes: array<int>,
20
- ~addressesByContractName: dict<array<Address.t>>,
20
+ ~addressSet: AddressSet.t,
21
+ ~clientFilteredContracts: option<array<string>>,
21
22
  ) => promise<logsQueryPage>
22
23
  }
@@ -52,15 +52,18 @@ module Registration = {
52
52
  }
53
53
 
54
54
  module EventItems = {
55
- // The whole per-query input: block range, the partition's registration
56
- // selection (by index), and its current addresses. Receipt selections,
57
- // field selection, and routing are derived on the Rust side.
55
+ // The whole per-query input beside the partition's address set: block range
56
+ // and the registration selection (by index). Receipt selections, field
57
+ // selection, and routing are derived on the Rust side.
58
58
  type query = {
59
59
  fromBlock: int,
60
60
  // Inclusive; None queries to the end of available data.
61
61
  toBlock: option<int>,
62
62
  registrationIndexes: array<int>,
63
- addressesByContractName: dict<array<Address.t>>,
63
+ // Contract names to fetch address-free even though their registrations
64
+ // depend on addresses (client-side filtering). None/empty means every
65
+ // address-dependent contract is filtered server-side.
66
+ clientFilteredContracts: option<array<string>>,
64
67
  }
65
68
 
66
69
  // One routed receipt with its kind-specific columns flattened: LogData
@@ -102,19 +105,22 @@ external classNew: (
102
105
  cfg,
103
106
  ~userAgent: string,
104
107
  array<Registration.input>,
108
+ AddressStore.t,
105
109
  ) => t = "new"
106
110
 
107
- let make = (cfg: cfg, ~eventRegistrations) => {
111
+ let make = (cfg: cfg, ~eventRegistrations, ~addressStore) => {
108
112
  let envioVersion = Utils.EnvioPackage.value.version
109
113
  Core.getAddon().fuelHyperSyncClient->classNew(
110
114
  cfg,
111
115
  ~userAgent=`hyperindex/${envioVersion}`,
112
116
  eventRegistrations,
117
+ addressStore,
113
118
  )
114
119
  }
115
120
 
116
121
  @send
117
- external getEventItems: (t, EventItems.query) => promise<EventItems.response> = "getEventItems"
122
+ external getEventItems: (t, EventItems.query, AddressSet.t) => promise<EventItems.response> =
123
+ "getEventItems"
118
124
 
119
125
  @send
120
126
  external getHeight: t => promise<int> = "getHeight"
@@ -58,9 +58,9 @@ let Registration = {
58
58
 
59
59
  let EventItems = {};
60
60
 
61
- function make(cfg, eventRegistrations) {
61
+ function make(cfg, eventRegistrations, addressStore) {
62
62
  let envioVersion = Utils.EnvioPackage.value.version;
63
- return Core.getAddon().FuelHyperSyncClient.new(cfg, `hyperindex/` + envioVersion, eventRegistrations);
63
+ return Core.getAddon().FuelHyperSyncClient.new(cfg, `hyperindex/` + envioVersion, eventRegistrations, addressStore);
64
64
  }
65
65
 
66
66
  export {
@@ -8,9 +8,11 @@ type options = {
8
8
  apiToken: option<string>,
9
9
  // The chain's registrations, indexed by their sequential `index`.
10
10
  onEventRegistrations: array<Internal.fuelOnEventRegistration>,
11
+ // The chain's address index; the client reads it while routing.
12
+ addressStore: AddressStore.t,
11
13
  }
12
14
 
13
- let make = ({chain, endpointUrl, apiToken, onEventRegistrations}: options): t => {
15
+ let make = ({chain, endpointUrl, apiToken, onEventRegistrations, addressStore}: options): t => {
14
16
  let name = "HyperFuel"
15
17
 
16
18
  let apiToken = switch apiToken {
@@ -24,6 +26,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
24
26
  let client = switch FuelHyperSyncClient.make(
25
27
  {url: endpointUrl, apiToken},
26
28
  ~eventRegistrations=FuelHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations),
29
+ ~addressStore,
27
30
  ) {
28
31
  | client => client
29
32
  | exception exn =>
@@ -33,8 +36,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
33
36
  let getItemsOrThrow = async (
34
37
  ~fromBlock,
35
38
  ~toBlock,
36
- ~addressesByContractName,
37
- ~contractNameByAddress as _,
39
+ ~addressSet,
38
40
  ~knownHeight,
39
41
  ~partitionId as _,
40
42
  ~selection: FetchState.selection,
@@ -52,7 +54,8 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
52
54
  ~fromBlock,
53
55
  ~toBlock,
54
56
  ~registrationIndexes=selection.onEventRegistrations->Array.map(reg => reg.index),
55
- ~addressesByContractName,
57
+ ~addressSet,
58
+ ~clientFilteredContracts=selection.clientFilteredContracts,
56
59
  ) catch {
57
60
  | FuelHyperSync.GetLogs.Error(error) =>
58
61
  throw(
@@ -27,17 +27,17 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
27
27
  client = FuelHyperSyncClient.make({
28
28
  url: param.endpointUrl,
29
29
  apiToken: apiToken$1
30
- }, FuelHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations));
30
+ }, FuelHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), param.addressStore);
31
31
  } catch (raw_exn) {
32
32
  let exn = Primitive_exceptions.internalToException(raw_exn);
33
33
  client = ErrorHandling.mkLogAndRaise(undefined, "Failed to instantiate the HyperFuel client", exn);
34
34
  }
35
- let getItemsOrThrow = async (fromBlock, toBlock, addressesByContractName, param, knownHeight, param$1, selection, param$2, retry, logger) => {
35
+ let getItemsOrThrow = async (fromBlock, toBlock, addressSet, knownHeight, param, selection, param$1, retry, logger) => {
36
36
  let totalTimeRef = Performance.now();
37
37
  let startFetchingBatchTimeRef = Performance.now();
38
38
  let pageUnsafe;
39
39
  try {
40
- pageUnsafe = await FuelHyperSync.GetLogs.query(client, fromBlock, toBlock, selection.onEventRegistrations.map(reg => reg.index), addressesByContractName);
40
+ pageUnsafe = await FuelHyperSync.GetLogs.query(client, fromBlock, toBlock, selection.onEventRegistrations.map(reg => reg.index), addressSet, selection.clientFilteredContracts);
41
41
  } catch (raw_error) {
42
42
  let error = Primitive_exceptions.internalToException(raw_error);
43
43
  if (error.RE_EXN_ID === FuelHyperSync.GetLogs.$$Error) {
@@ -16,7 +16,7 @@ let reraisIfRateLimited = exn =>
16
16
 
17
17
  type logsQueryPage = {
18
18
  items: array<HyperSyncClient.EventItems.item>,
19
- // Block headers referenced by `items`, deduplicated by block number.
19
+ // Headers for every returned block, deduplicated by block number.
20
20
  blocks: array<HyperSyncClient.EventItems.blockHeader>,
21
21
  nextBlock: int,
22
22
  archiveHeight: int,
@@ -92,7 +92,7 @@ module GetLogs = {
92
92
  ~toBlock,
93
93
  ~maxNumLogs,
94
94
  ~registrationIndexes,
95
- ~addressesByContractName,
95
+ ~addressSet,
96
96
  ~clientFilteredContracts,
97
97
  ): logsQueryPage => {
98
98
  let query: HyperSyncClient.EventItems.query = {
@@ -100,11 +100,13 @@ module GetLogs = {
100
100
  toBlock,
101
101
  ?maxNumLogs,
102
102
  registrationIndexes,
103
- addressesByContractName,
104
103
  clientFilteredContracts,
105
104
  }
106
105
 
107
- let (res, transactionStore, blockStore) = switch await client.getEventItems(~query) {
106
+ let (res, transactionStore, blockStore) = switch await client.getEventItems(
107
+ ~query,
108
+ ~addressSet,
109
+ ) {
108
110
  | res => res
109
111
  | exception exn =>
110
112
  reraisIfRateLimited(exn)
@@ -82,18 +82,17 @@ function extractMissingParams(exn) {
82
82
  }
83
83
  }
84
84
 
85
- async function query(client, fromBlock, toBlock, maxNumLogs, registrationIndexes, addressesByContractName, clientFilteredContracts) {
85
+ async function query(client, fromBlock, toBlock, maxNumLogs, registrationIndexes, addressSet, clientFilteredContracts) {
86
86
  let query$1 = {
87
87
  fromBlock: fromBlock,
88
88
  toBlock: toBlock,
89
89
  maxNumLogs: maxNumLogs,
90
90
  registrationIndexes: registrationIndexes,
91
- addressesByContractName: addressesByContractName,
92
91
  clientFilteredContracts: clientFilteredContracts
93
92
  };
94
93
  let match;
95
94
  try {
96
- match = await client.getEventItems(query$1);
95
+ match = await client.getEventItems(query$1, addressSet);
97
96
  } catch (raw_exn) {
98
97
  let exn = Primitive_exceptions.internalToException(raw_exn);
99
98
  reraisIfRateLimited(exn);
@@ -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