envio 3.3.0-alpha.7 → 3.3.0-alpha.8

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 (89) hide show
  1. package/index.d.ts +23 -5
  2. package/package.json +6 -6
  3. package/src/Api.res +1 -1
  4. package/src/Api.res.mjs +1 -1
  5. package/src/ChainFetching.res +21 -10
  6. package/src/ChainFetching.res.mjs +11 -10
  7. package/src/ChainState.res +328 -165
  8. package/src/ChainState.res.mjs +205 -100
  9. package/src/ChainState.resi +16 -7
  10. package/src/Config.res +11 -27
  11. package/src/Config.res.mjs +8 -7
  12. package/src/Core.res +7 -0
  13. package/src/CrossChainState.res +53 -80
  14. package/src/CrossChainState.res.mjs +43 -59
  15. package/src/CrossChainState.resi +1 -1
  16. package/src/Ecosystem.res +3 -3
  17. package/src/Ecosystem.res.mjs +3 -3
  18. package/src/Envio.res +14 -9
  19. package/src/EventConfigBuilder.res +105 -56
  20. package/src/EventConfigBuilder.res.mjs +69 -33
  21. package/src/EventProcessing.res +19 -15
  22. package/src/EventProcessing.res.mjs +13 -12
  23. package/src/FetchState.res +375 -171
  24. package/src/FetchState.res.mjs +249 -251
  25. package/src/HandlerLoader.res +7 -112
  26. package/src/HandlerLoader.res.mjs +1 -87
  27. package/src/HandlerRegister.res +209 -6
  28. package/src/HandlerRegister.res.mjs +90 -5
  29. package/src/HandlerRegister.resi +13 -2
  30. package/src/IndexerState.res +6 -3
  31. package/src/IndexerState.res.mjs +3 -3
  32. package/src/IndexerState.resi +1 -1
  33. package/src/IndexingAddresses.res +10 -7
  34. package/src/IndexingAddresses.res.mjs +8 -7
  35. package/src/IndexingAddresses.resi +3 -1
  36. package/src/Internal.res +82 -36
  37. package/src/Internal.res.mjs +11 -1
  38. package/src/Main.res +22 -23
  39. package/src/Main.res.mjs +15 -17
  40. package/src/Metrics.res +43 -2
  41. package/src/Metrics.res.mjs +39 -3
  42. package/src/Prometheus.res +0 -35
  43. package/src/Prometheus.res.mjs +33 -68
  44. package/src/RawEvent.res +7 -2
  45. package/src/RawEvent.res.mjs +4 -4
  46. package/src/SimulateItems.res +20 -7
  47. package/src/SimulateItems.res.mjs +7 -11
  48. package/src/TestIndexer.res +1 -1
  49. package/src/TestIndexer.res.mjs +1 -1
  50. package/src/sources/BlockStore.res +46 -0
  51. package/src/sources/BlockStore.res.mjs +24 -0
  52. package/src/sources/EventRouter.res +19 -12
  53. package/src/sources/EventRouter.res.mjs +7 -5
  54. package/src/sources/Evm.res +54 -4
  55. package/src/sources/Evm.res.mjs +43 -4
  56. package/src/sources/EvmChain.res +14 -18
  57. package/src/sources/EvmChain.res.mjs +12 -13
  58. package/src/sources/FieldMask.res +39 -0
  59. package/src/sources/FieldMask.res.mjs +42 -0
  60. package/src/sources/Fuel.res +5 -2
  61. package/src/sources/Fuel.res.mjs +4 -3
  62. package/src/sources/HyperFuelSource.res +31 -40
  63. package/src/sources/HyperFuelSource.res.mjs +52 -44
  64. package/src/sources/HyperSync.res +31 -9
  65. package/src/sources/HyperSync.res.mjs +47 -31
  66. package/src/sources/HyperSync.resi +10 -3
  67. package/src/sources/HyperSyncClient.res +15 -4
  68. package/src/sources/HyperSyncHeightStream.res +1 -8
  69. package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
  70. package/src/sources/HyperSyncSource.res +37 -58
  71. package/src/sources/HyperSyncSource.res.mjs +42 -42
  72. package/src/sources/RpcSource.res +91 -82
  73. package/src/sources/RpcSource.res.mjs +76 -45
  74. package/src/sources/RpcWebSocketHeightStream.res +0 -5
  75. package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
  76. package/src/sources/SimulateSource.res +5 -3
  77. package/src/sources/SimulateSource.res.mjs +12 -4
  78. package/src/sources/Source.res +19 -5
  79. package/src/sources/SourceManager.res +62 -6
  80. package/src/sources/SourceManager.res.mjs +55 -8
  81. package/src/sources/SourceManager.resi +10 -0
  82. package/src/sources/Svm.res +14 -10
  83. package/src/sources/Svm.res.mjs +23 -6
  84. package/src/sources/SvmHyperSyncClient.res +5 -6
  85. package/src/sources/SvmHyperSyncSource.res +82 -41
  86. package/src/sources/SvmHyperSyncSource.res.mjs +86 -41
  87. package/src/sources/TransactionStore.res +6 -107
  88. package/src/sources/TransactionStore.res.mjs +5 -86
  89. package/svm.schema.json +19 -0
@@ -72,116 +72,12 @@ let autoLoadFromSrcHandlers = async (~handlers: string) => {
72
72
  ->Promise.all
73
73
  }
74
74
 
75
- // EVM re-runs `parseEventFiltersOrThrow` with the registered `where:` JSON so
76
- // per-event filters propagate into `getEventFiltersOrThrow` / `filterByAddresses`
77
- // — which is why `evmEventConfig` has to retain `sighash` and `indexedParams`.
78
- // `dependsOnAddresses` is routed through `Internal.dependsOnAddresses` so the
79
- // formula stays in sync with `EventConfigBuilder.build{Evm,Fuel}EventConfig`.
80
- let applyRegistrations = (~config: Config.t): Config.t => {
81
- let newChainMap = config.chainMap->ChainMap.map(chain => {
82
- let newContracts = chain.contracts->Array.map(contract => {
83
- let newEvents = contract.events->Array.map(
84
- ev => {
85
- let isWildcard = HandlerRegister.isWildcard(
86
- ~contractName=ev.contractName,
87
- ~eventName=ev.name,
88
- )
89
- let handler = HandlerRegister.getHandler(
90
- ~contractName=ev.contractName,
91
- ~eventName=ev.name,
92
- )
93
- let contractRegister = HandlerRegister.getContractRegister(
94
- ~contractName=ev.contractName,
95
- ~eventName=ev.name,
96
- )
97
- switch config.ecosystem.name {
98
- | Fuel =>
99
- let fuelEv = ev->(Utils.magic: Internal.eventConfig => Internal.fuelEventConfig)
100
-
101
- ({
102
- ...fuelEv,
103
- isWildcard,
104
- handler,
105
- contractRegister,
106
- clientAddressFilter: ?EventConfigBuilder.buildAddressFilter([], ~isWildcard),
107
- dependsOnAddresses: Internal.dependsOnAddresses(
108
- ~isWildcard,
109
- ~filterByAddresses=false,
110
- ),
111
- } :> Internal.eventConfig)
112
- | Evm =>
113
- let evmEv = ev->(Utils.magic: Internal.eventConfig => Internal.evmEventConfig)
114
- let eventFilters = HandlerRegister.getOnEventWhere(
115
- ~contractName=ev.contractName,
116
- ~eventName=ev.name,
117
- )
118
- let indexedParams = evmEv.paramsMetadata->Array.filter(p => p.indexed)
119
- let {
120
- getEventFiltersOrThrow,
121
- filterByAddresses,
122
- addressFilterParamGroups,
123
- } = LogSelection.parseEventFiltersOrThrow(
124
- ~eventFilters,
125
- ~sighash=evmEv.sighash,
126
- ~params=indexedParams->Array.map(p => p.name),
127
- ~contractName=ev.contractName,
128
- ~probeChainId=chain.id,
129
- ~onEventBlockFilterSchema=config.ecosystem.onEventBlockFilterSchema,
130
- ~topic1=?indexedParams
131
- ->Array.get(0)
132
- ->Option.map(EventConfigBuilder.buildTopicGetter),
133
- ~topic2=?indexedParams
134
- ->Array.get(1)
135
- ->Option.map(EventConfigBuilder.buildTopicGetter),
136
- ~topic3=?indexedParams
137
- ->Array.get(2)
138
- ->Option.map(EventConfigBuilder.buildTopicGetter),
139
- )
140
-
141
- ({
142
- ...evmEv,
143
- isWildcard,
144
- handler,
145
- contractRegister,
146
- getEventFiltersOrThrow,
147
- filterByAddresses,
148
- clientAddressFilter: ?EventConfigBuilder.buildAddressFilter(
149
- addressFilterParamGroups,
150
- ~isWildcard,
151
- ),
152
- dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses),
153
- } :> Internal.eventConfig)
154
- | Svm =>
155
- let svmEv =
156
- ev->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
157
- ({
158
- ...svmEv,
159
- isWildcard,
160
- handler,
161
- contractRegister,
162
- clientAddressFilter: ?EventConfigBuilder.buildAddressFilter(
163
- [],
164
- ~isWildcard,
165
- ~srcAddressExpr="event.programId",
166
- ),
167
- dependsOnAddresses: Internal.dependsOnAddresses(
168
- ~isWildcard,
169
- ~filterByAddresses=false,
170
- ),
171
- } :> Internal.eventConfig)
172
- }
173
- },
174
- )
175
- {...contract, events: newEvents}
176
- })
177
- {...chain, contracts: newContracts}
178
- })
179
- {...config, chainMap: newChainMap}
180
- }
181
-
182
- // `Config` never reads `HandlerRegister`. The only way to get a config that
183
- // reflects registration state is through the returned value here.
184
- let registerAllHandlers = async (~config: Config.t) => {
75
+ // `Config` holds only event definitions; handler + per-chain `where`
76
+ // registration state is layered on separately as `onEventRegistration`s by
77
+ // `HandlerRegister.finishRegistration`. This loads the user handler files
78
+ // (populating the global `HandlerRegister` registry as a side effect) and
79
+ // returns the resulting per-chain registrations.
80
+ let registerAllHandlers = async (~config: Config.t): HandlerRegister.registrationsByChainId => {
185
81
  HandlerRegister.startRegistration(~ecosystem=config.ecosystem)
186
82
 
187
83
  // Auto-load all .js files from src/handlers directory
@@ -194,6 +90,5 @@ let registerAllHandlers = async (~config: Config.t) => {
194
90
  })
195
91
  ->Promise.all
196
92
 
197
- let registrations = HandlerRegister.finishRegistration()
198
- (applyRegistrations(~config), registrations)
93
+ HandlerRegister.finishRegistration(~config)
199
94
  }
@@ -5,16 +5,11 @@ import * as Path from "path";
5
5
  import * as Utils from "./Utils.res.mjs";
6
6
  import * as Logging from "./Logging.res.mjs";
7
7
  import * as Process from "process";
8
- import * as ChainMap from "./ChainMap.res.mjs";
9
- import * as Internal from "./Internal.res.mjs";
10
8
  import * as Nodemodule from "node:module";
11
- import * as LogSelection from "./LogSelection.res.mjs";
12
- import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
13
9
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
14
10
  import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
15
11
  import * as HandlerRegister from "./HandlerRegister.res.mjs";
16
12
  import * as Promises from "node:fs/promises";
17
- import * as EventConfigBuilder from "./EventConfigBuilder.res.mjs";
18
13
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
19
14
 
20
15
  try {
@@ -61,98 +56,17 @@ async function autoLoadFromSrcHandlers(handlers) {
61
56
  })));
62
57
  }
63
58
 
64
- function applyRegistrations(config) {
65
- let newChainMap = ChainMap.map(config.chainMap, chain => {
66
- let newContracts = chain.contracts.map(contract => {
67
- let newEvents = contract.events.map(ev => {
68
- let isWildcard = HandlerRegister.isWildcard(ev.contractName, ev.name);
69
- let handler = HandlerRegister.getHandler(ev.contractName, ev.name);
70
- let contractRegister = HandlerRegister.getContractRegister(ev.contractName, ev.name);
71
- let match = config.ecosystem.name;
72
- switch (match) {
73
- case "evm" :
74
- let eventFilters = HandlerRegister.getOnEventWhere(ev.contractName, ev.name);
75
- let indexedParams = ev.paramsMetadata.filter(p => p.indexed);
76
- let match$1 = LogSelection.parseEventFiltersOrThrow(eventFilters, ev.sighash, indexedParams.map(p => p.name), ev.contractName, chain.id, config.ecosystem.onEventBlockFilterSchema, Stdlib_Option.map(indexedParams[0], EventConfigBuilder.buildTopicGetter), Stdlib_Option.map(indexedParams[1], EventConfigBuilder.buildTopicGetter), Stdlib_Option.map(indexedParams[2], EventConfigBuilder.buildTopicGetter));
77
- let filterByAddresses = match$1.filterByAddresses;
78
- let newrecord = {...ev};
79
- newrecord.getEventFiltersOrThrow = match$1.getEventFiltersOrThrow;
80
- newrecord.contractRegister = contractRegister;
81
- newrecord.handler = handler;
82
- newrecord.clientAddressFilter = EventConfigBuilder.buildAddressFilter(match$1.addressFilterParamGroups, isWildcard, undefined);
83
- newrecord.dependsOnAddresses = Internal.dependsOnAddresses(isWildcard, filterByAddresses);
84
- newrecord.filterByAddresses = filterByAddresses;
85
- newrecord.isWildcard = isWildcard;
86
- return newrecord;
87
- case "fuel" :
88
- let newrecord$1 = {...ev};
89
- newrecord$1.contractRegister = contractRegister;
90
- newrecord$1.handler = handler;
91
- newrecord$1.clientAddressFilter = EventConfigBuilder.buildAddressFilter([], isWildcard, undefined);
92
- newrecord$1.dependsOnAddresses = Internal.dependsOnAddresses(isWildcard, false);
93
- newrecord$1.isWildcard = isWildcard;
94
- return newrecord$1;
95
- case "svm" :
96
- let newrecord$2 = {...ev};
97
- newrecord$2.contractRegister = contractRegister;
98
- newrecord$2.handler = handler;
99
- newrecord$2.clientAddressFilter = EventConfigBuilder.buildAddressFilter([], isWildcard, "event.programId");
100
- newrecord$2.dependsOnAddresses = Internal.dependsOnAddresses(isWildcard, false);
101
- newrecord$2.isWildcard = isWildcard;
102
- return newrecord$2;
103
- }
104
- });
105
- return {
106
- name: contract.name,
107
- abi: contract.abi,
108
- addresses: contract.addresses,
109
- events: newEvents,
110
- startBlock: contract.startBlock
111
- };
112
- });
113
- let newrecord = {...chain};
114
- newrecord.contracts = newContracts;
115
- return newrecord;
116
- });
117
- return {
118
- name: config.name,
119
- description: config.description,
120
- handlers: config.handlers,
121
- contractHandlers: config.contractHandlers,
122
- shouldRollbackOnReorg: config.shouldRollbackOnReorg,
123
- shouldSaveFullHistory: config.shouldSaveFullHistory,
124
- storage: config.storage,
125
- chainMap: newChainMap,
126
- defaultChain: config.defaultChain,
127
- ecosystem: config.ecosystem,
128
- enableRawEvents: config.enableRawEvents,
129
- maxAddrInPartition: config.maxAddrInPartition,
130
- batchSize: config.batchSize,
131
- lowercaseAddresses: config.lowercaseAddresses,
132
- isDev: config.isDev,
133
- userEntitiesByName: config.userEntitiesByName,
134
- userEntities: config.userEntities,
135
- allEntities: config.allEntities,
136
- allEnums: config.allEnums
137
- };
138
- }
139
-
140
59
  async function registerAllHandlers(config) {
141
60
  HandlerRegister.startRegistration(config.ecosystem);
142
61
  await autoLoadFromSrcHandlers(config.handlers);
143
62
  await Promise.all(config.contractHandlers.map(param => registerContractHandlers(param.name, param.handler)));
144
- let registrations = HandlerRegister.finishRegistration();
145
- return [
146
- applyRegistrations(config),
147
- registrations
148
- ];
63
+ return HandlerRegister.finishRegistration(config);
149
64
  }
150
65
 
151
66
  export {
152
67
  toImportUrl,
153
68
  registerContractHandlers,
154
69
  autoLoadFromSrcHandlers,
155
- applyRegistrations,
156
70
  registerAllHandlers,
157
71
  }
158
72
  /* Not a pure module */
@@ -10,18 +10,30 @@ let empty = {
10
10
  eventOptions: None,
11
11
  }
12
12
 
13
- type registrations = {onBlockByChainId: dict<array<Internal.onBlockConfig>>}
13
+ // Per-chain onEventRegistrations built from the event definitions in
14
+ // `Config.t` plus whatever handler/contractRegister/eventOptions got
15
+ // registered for them, and the onBlock registrations collected during
16
+ // registration.
17
+ type chainRegistrations = {
18
+ onEventRegistrations: array<Internal.onEventRegistration>,
19
+ onBlockRegistrations: array<Internal.onBlockRegistration>,
20
+ }
21
+
22
+ // The finished registration state returned by `finishRegistration`.
23
+ type registrationsByChainId = dict<chainRegistrations>
24
+
25
+ type pendingRegistrations = {onBlockByChainId: dict<array<Internal.onBlockRegistration>>}
14
26
 
15
27
  type activeRegistration = {
16
28
  ecosystem: Ecosystem.t,
17
- registrations: registrations,
29
+ registrations: pendingRegistrations,
18
30
  mutable finished: bool,
19
31
  }
20
32
 
21
33
  // Stashed on `globalThis` so a duplicate envio module instance — e.g. when the
22
34
  // CLI's `bin.mjs` resolves envio from one path but the user's handlers resolve
23
35
  // it from `node_modules/envio` — shares one registry. Without this, each copy
24
- // keeps its own dict and `applyRegistrations` reads empty state.
36
+ // keeps its own dict and `buildOnEventRegistrations` reads empty state.
25
37
  //
26
38
  // Version-gated: the record shapes below can evolve between envio versions,
27
39
  // so the guard uses strict full-version equality. On mismatch we throw with
@@ -119,11 +131,202 @@ let startRegistration = (~ecosystem) => {
119
131
  }
120
132
  }
121
133
 
122
- let finishRegistration = () => {
134
+ // Enrich one event definition into its (event, chain) registration using
135
+ // whatever handler/contractRegister/where the user registered for it. Shared
136
+ // by chain startup (`buildOnEventRegistrations`), `simulate`, and test
137
+ // helpers so the three stay in sync instead of re-deriving the per-ecosystem
138
+ // dispatch each place.
139
+ let buildOnEventRegistration = (
140
+ ~config: Config.t,
141
+ ~chainId: int,
142
+ ~eventConfig: Internal.eventConfig,
143
+ ~startBlock=?,
144
+ ): Internal.onEventRegistration => {
145
+ let contractName = eventConfig.contractName
146
+ let eventName = eventConfig.name
147
+ let t = get(~contractName, ~eventName)
148
+ let isWildcard = t.eventOptions->Option.flatMap(v => v.wildcard)->Option.getOr(false)
149
+ let handler = t.handler
150
+ let contractRegister = t.contractRegister
151
+
152
+ switch config.ecosystem.name {
153
+ | Fuel =>
154
+ (EventConfigBuilder.buildFuelOnEventRegistration(
155
+ ~eventConfig=eventConfig->(Utils.magic: Internal.eventConfig => Internal.fuelEventConfig),
156
+ ~isWildcard,
157
+ ~handler,
158
+ ~contractRegister,
159
+ ~startBlock?,
160
+ ) :> Internal.onEventRegistration)
161
+ | Svm =>
162
+ (EventConfigBuilder.buildSvmOnEventRegistration(
163
+ ~eventConfig=eventConfig->(
164
+ Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig
165
+ ),
166
+ ~isWildcard,
167
+ ~handler,
168
+ ~contractRegister,
169
+ ~startBlock?,
170
+ ) :> Internal.onEventRegistration)
171
+ | Evm =>
172
+ (EventConfigBuilder.buildEvmOnEventRegistration(
173
+ ~eventConfig=eventConfig->(Utils.magic: Internal.eventConfig => Internal.evmEventConfig),
174
+ ~isWildcard,
175
+ ~handler,
176
+ ~contractRegister,
177
+ ~eventFilters=t.eventOptions->Option.flatMap(v => v.where),
178
+ ~probeChainId=chainId,
179
+ ~onEventBlockFilterSchema=config.ecosystem.onEventBlockFilterSchema,
180
+ ~startBlock?,
181
+ ) :> Internal.onEventRegistration)
182
+ }
183
+ }
184
+
185
+ // Enrich a chain's static event definitions with the registered
186
+ // handler/contractRegister/where (validating them along the way) to produce
187
+ // the onEventRegistrations ChainState indexes off. Runs once per chain when
188
+ // registration finishes, so a bad `where`/duplicate event throws during
189
+ // startup with a stack trace pointing here instead of surfacing later from
190
+ // inside ChainState's construction. Events without a handler/contractRegister
191
+ // get added to `notRegisteredEventsByContract` (event names grouped by
192
+ // contract name) so the caller can report them once for the whole indexer
193
+ // instead of per chain.
194
+ let buildOnEventRegistrations = (
195
+ ~chainConfig: Config.chain,
196
+ ~config: Config.t,
197
+ ~notRegisteredEventsByContract: dict<Utils.Set.t<string>>,
198
+ ): array<Internal.onEventRegistration> => {
199
+ // We don't need the router itself, but only validation logic,
200
+ // since now event router is created for selection of events
201
+ // and validation doesn't work correctly in routers.
202
+ // Ideally to split it into two different parts.
203
+ let eventRouter = EventRouter.empty()
204
+
205
+ let onEventRegistrations: array<Internal.onEventRegistration> = []
206
+
207
+ chainConfig.contracts->Array.forEach(contract => {
208
+ let contractName = contract.name
209
+
210
+ contract.events->Array.forEach(eventConfig => {
211
+ let eventName = eventConfig.name
212
+
213
+ let onEventRegistration = buildOnEventRegistration(
214
+ ~config,
215
+ ~chainId=chainConfig.id,
216
+ ~eventConfig,
217
+ ~startBlock=?contract.startBlock,
218
+ )
219
+ let {isWildcard, handler, contractRegister} = onEventRegistration
220
+
221
+ // Should validate the events
222
+ eventRouter->EventRouter.addOrThrow(
223
+ eventConfig.id,
224
+ (),
225
+ ~contractName,
226
+ ~chain=ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id),
227
+ ~eventName,
228
+ ~isWildcard,
229
+ )
230
+
231
+ // Filter out events without a handler/contractRegister so they aren't
232
+ // fetched or dispatched (unless raw events are enabled).
233
+ let shouldBeIncluded = if config.enableRawEvents {
234
+ true
235
+ } else {
236
+ let isRegistered = contractRegister->Option.isSome || handler->Option.isSome
237
+ if !isRegistered {
238
+ let eventNames = switch notRegisteredEventsByContract->Utils.Dict.dangerouslyGetNonOption(
239
+ contractName,
240
+ ) {
241
+ | Some(set) => set
242
+ | None => {
243
+ let set = Utils.Set.make()
244
+ notRegisteredEventsByContract->Dict.set(contractName, set)
245
+ set
246
+ }
247
+ }
248
+ eventNames->Utils.Set.add(eventName)->ignore
249
+ }
250
+ isRegistered
251
+ }
252
+
253
+ // Check if event has Static([]) filters (from a dynamic where
254
+ // callback returning `false` / SkipAll for this chain).
255
+ // If so, skip it entirely - it should never be fetched
256
+ let shouldSkip = try {
257
+ let getEventFiltersOrThrow = (
258
+ onEventRegistration->(
259
+ Utils.magic: Internal.onEventRegistration => Internal.evmOnEventRegistration
260
+ )
261
+ ).getEventFiltersOrThrow
262
+
263
+ // Check for non-evm chains
264
+ if (
265
+ getEventFiltersOrThrow->(Utils.magic: (ChainMap.Chain.t => Internal.eventFilters) => bool)
266
+ ) {
267
+ switch getEventFiltersOrThrow(ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)) {
268
+ | Static([]) => true
269
+ | _ => false
270
+ }
271
+ } else {
272
+ false
273
+ }
274
+ } catch {
275
+ // Can throw when filter is invalid
276
+ // Don't skip an event in this case. Let it throw in a better place - source code
277
+ | _ => false
278
+ }
279
+
280
+ if shouldBeIncluded && !shouldSkip {
281
+ onEventRegistrations->Array.push(onEventRegistration)
282
+ }
283
+ })
284
+ })
285
+
286
+ onEventRegistrations
287
+ }
288
+
289
+ let finishRegistration = (~config: Config.t): registrationsByChainId => {
123
290
  switch activeRegistration.contents {
124
291
  | Some(r) => {
125
292
  r.finished = true
126
- r.registrations
293
+ let notRegisteredEventsByContract: dict<Utils.Set.t<string>> = Dict.make()
294
+ let registrationsByChainId: registrationsByChainId = Dict.make()
295
+ config.chainMap
296
+ ->ChainMap.values
297
+ ->Array.forEach(chainConfig => {
298
+ let key = chainConfig.id->Int.toString
299
+ registrationsByChainId->Dict.set(
300
+ key,
301
+ {
302
+ onEventRegistrations: buildOnEventRegistrations(
303
+ ~chainConfig,
304
+ ~config,
305
+ ~notRegisteredEventsByContract,
306
+ ),
307
+ onBlockRegistrations: r.registrations.onBlockByChainId
308
+ ->Utils.Dict.dangerouslyGetNonOption(key)
309
+ ->Option.getOr([]),
310
+ },
311
+ )
312
+ })
313
+
314
+ // Reported once for the whole indexer (a shared contract on multiple
315
+ // chains would otherwise repeat the same message per chain).
316
+ let notRegisteredEntries = notRegisteredEventsByContract->Dict.toArray
317
+ if notRegisteredEntries->Utils.Array.notEmpty {
318
+ let groups =
319
+ notRegisteredEntries
320
+ ->Array.map(((contractName, eventNames)) =>
321
+ `${contractName} (${eventNames->Utils.Set.toArray->Array.joinUnsafe(", ")})`
322
+ )
323
+ ->Array.joinUnsafe(", ")
324
+ Logging.getLogger()->Logging.childInfo(
325
+ `Events without a handler, skipped for indexing: ${groups}`,
326
+ )
327
+ }
328
+
329
+ registrationsByChainId
127
330
  }
128
331
  | None =>
129
332
  JsError.throwWithMessage(
@@ -178,7 +381,7 @@ let registerOnBlock = (
178
381
  interval,
179
382
  chainId,
180
383
  handler,
181
- }: Internal.onBlockConfig
384
+ }: Internal.onBlockRegistration
182
385
  ),
183
386
  )
184
387
  })
@@ -2,9 +2,13 @@
2
2
 
3
3
  import * as Utils from "./Utils.res.mjs";
4
4
  import * as Logging from "./Logging.res.mjs";
5
+ import * as ChainMap from "./ChainMap.res.mjs";
6
+ import * as EventRouter from "./sources/EventRouter.res.mjs";
5
7
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
7
9
  import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
10
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
11
+ import * as EventConfigBuilder from "./EventConfigBuilder.res.mjs";
8
12
 
9
13
  let version = Utils.EnvioPackage.value.version;
10
14
 
@@ -88,14 +92,94 @@ function startRegistration(ecosystem) {
88
92
  };
89
93
  }
90
94
 
91
- function finishRegistration() {
95
+ function buildOnEventRegistration(config, chainId, eventConfig, startBlock) {
96
+ let contractName = eventConfig.contractName;
97
+ let eventName = eventConfig.name;
98
+ let t = get(contractName, eventName);
99
+ let isWildcard = Stdlib_Option.getOr(Stdlib_Option.flatMap(t.eventOptions, v => v.wildcard), false);
100
+ let handler = t.handler;
101
+ let contractRegister = t.contractRegister;
102
+ let match = config.ecosystem.name;
103
+ switch (match) {
104
+ case "evm" :
105
+ return EventConfigBuilder.buildEvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, Stdlib_Option.flatMap(t.eventOptions, v => v.where), chainId, config.ecosystem.onEventBlockFilterSchema, startBlock);
106
+ case "fuel" :
107
+ return EventConfigBuilder.buildFuelOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
108
+ case "svm" :
109
+ return EventConfigBuilder.buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
110
+ }
111
+ }
112
+
113
+ function buildOnEventRegistrations(chainConfig, config, notRegisteredEventsByContract) {
114
+ let eventRouter = EventRouter.empty();
115
+ let onEventRegistrations = [];
116
+ chainConfig.contracts.forEach(contract => {
117
+ let contractName = contract.name;
118
+ contract.events.forEach(eventConfig => {
119
+ let eventName = eventConfig.name;
120
+ let onEventRegistration = buildOnEventRegistration(config, chainConfig.id, eventConfig, contract.startBlock);
121
+ EventRouter.addOrThrow(eventRouter, eventConfig.id, undefined, contractName, onEventRegistration.isWildcard, eventName, ChainMap.Chain.makeUnsafe(chainConfig.id));
122
+ let shouldBeIncluded;
123
+ if (config.enableRawEvents) {
124
+ shouldBeIncluded = true;
125
+ } else {
126
+ let isRegistered = Stdlib_Option.isSome(onEventRegistration.contractRegister) || Stdlib_Option.isSome(onEventRegistration.handler);
127
+ if (!isRegistered) {
128
+ let set = notRegisteredEventsByContract[contractName];
129
+ let eventNames;
130
+ if (set !== undefined) {
131
+ eventNames = Primitive_option.valFromOption(set);
132
+ } else {
133
+ let set$1 = new Set();
134
+ notRegisteredEventsByContract[contractName] = set$1;
135
+ eventNames = set$1;
136
+ }
137
+ eventNames.add(eventName);
138
+ }
139
+ shouldBeIncluded = isRegistered;
140
+ }
141
+ let shouldSkip;
142
+ try {
143
+ let getEventFiltersOrThrow = onEventRegistration.getEventFiltersOrThrow;
144
+ if (getEventFiltersOrThrow) {
145
+ let match = getEventFiltersOrThrow(ChainMap.Chain.makeUnsafe(chainConfig.id));
146
+ shouldSkip = match.TAG === "Static" ? match._0.length === 0 : false;
147
+ } else {
148
+ shouldSkip = false;
149
+ }
150
+ } catch (exn) {
151
+ shouldSkip = false;
152
+ }
153
+ if (shouldBeIncluded && !shouldSkip) {
154
+ onEventRegistrations.push(onEventRegistration);
155
+ return;
156
+ }
157
+ });
158
+ });
159
+ return onEventRegistrations;
160
+ }
161
+
162
+ function finishRegistration(config) {
92
163
  let r = activeRegistration.contents;
93
- if (r !== undefined) {
94
- r.finished = true;
95
- return r.registrations;
96
- } else {
164
+ if (r === undefined) {
97
165
  return Stdlib_JsError.throwWithMessage("The indexer has not started registering handlers, so can't finish it.");
98
166
  }
167
+ r.finished = true;
168
+ let notRegisteredEventsByContract = {};
169
+ let registrationsByChainId = {};
170
+ ChainMap.values(config.chainMap).forEach(chainConfig => {
171
+ let key = chainConfig.id.toString();
172
+ registrationsByChainId[key] = {
173
+ onEventRegistrations: buildOnEventRegistrations(chainConfig, config, notRegisteredEventsByContract),
174
+ onBlockRegistrations: Stdlib_Option.getOr(r.registrations.onBlockByChainId[key], [])
175
+ };
176
+ });
177
+ let notRegisteredEntries = Object.entries(notRegisteredEventsByContract);
178
+ if (Utils.$$Array.notEmpty(notRegisteredEntries)) {
179
+ let groups = notRegisteredEntries.map(param => param[0] + ` (` + Array.from(param[1]).join(", ") + `)`).join(", ");
180
+ Logging.childInfo(Logging.getLogger(), `Events without a handler, skipped for indexing: ` + groups);
181
+ }
182
+ return registrationsByChainId;
99
183
  }
100
184
 
101
185
  function isPendingRegistration() {
@@ -282,6 +366,7 @@ export {
282
366
  isPendingRegistration,
283
367
  finishRegistration,
284
368
  throwIfFinishedRegistration,
369
+ buildOnEventRegistration,
285
370
  setHandler,
286
371
  setContractRegister,
287
372
  getHandler,
@@ -1,10 +1,21 @@
1
- type registrations = {onBlockByChainId: dict<array<Internal.onBlockConfig>>}
1
+ type chainRegistrations = {
2
+ onEventRegistrations: array<Internal.onEventRegistration>,
3
+ onBlockRegistrations: array<Internal.onBlockRegistration>,
4
+ }
5
+ type registrationsByChainId = dict<chainRegistrations>
2
6
 
3
7
  let startRegistration: (~ecosystem: Ecosystem.t) => unit
4
8
  let isPendingRegistration: unit => bool
5
- let finishRegistration: unit => registrations
9
+ let finishRegistration: (~config: Config.t) => registrationsByChainId
6
10
  let throwIfFinishedRegistration: (~methodName: string) => unit
7
11
 
12
+ let buildOnEventRegistration: (
13
+ ~config: Config.t,
14
+ ~chainId: int,
15
+ ~eventConfig: Internal.eventConfig,
16
+ ~startBlock: int=?,
17
+ ) => Internal.onEventRegistration
18
+
8
19
  let setHandler: (
9
20
  ~contractName: string,
10
21
  ~eventName: string,
@@ -188,7 +188,7 @@ let makeFromDbState = (
188
188
  ~config: Config.t,
189
189
  ~persistence: Persistence.t,
190
190
  ~initialState: Persistence.initialState,
191
- ~registrations,
191
+ ~registrationsByChainId,
192
192
  ~isDevelopmentMode=false,
193
193
  ~shouldUseTui=false,
194
194
  ~exitAfterFirstEventBlock=false,
@@ -234,7 +234,7 @@ let makeFromDbState = (
234
234
  ~isInReorgThreshold,
235
235
  ~isRealtime,
236
236
  ~config,
237
- ~registrations,
237
+ ~registrationsByChainId,
238
238
  ~reducedPollingInterval?,
239
239
  ),
240
240
  )
@@ -362,7 +362,10 @@ let clearRollback = (state: t) => state.rollbackState = NoRollback
362
362
  let invalidateInflight = (state: t) => state.epoch = state.epoch + 1
363
363
 
364
364
  let applyBatchProgress = (state: t, ~batch: Batch.t) =>
365
- state.crossChainState->CrossChainState.applyBatchProgress(~batch)
365
+ state.crossChainState->CrossChainState.applyBatchProgress(
366
+ ~batch,
367
+ ~blockTimestampName=state.config.ecosystem.blockTimestampName,
368
+ )
366
369
 
367
370
  // Processing-loop mutex. Guards ProcessEventBatch re-entry so only one
368
371
  // processing loop runs at a time.